[OpenLayers-Commits] r11392 -
sandbox/bartvde/sencha/openlayers/lib/sencha
commits-20090109 at openlayers.org
commits-20090109 at openlayers.org
Thu Feb 24 05:21:55 EST 2011
Author: bartvde
Date: 2011-02-24 02:21:54 -0800 (Thu, 24 Feb 2011)
New Revision: 11392
Removed:
sandbox/bartvde/sencha/openlayers/lib/sencha/sencha-touch-debug-w-comments.js
Log:
removing sencha file from sandbox
Deleted: sandbox/bartvde/sencha/openlayers/lib/sencha/sencha-touch-debug-w-comments.js
===================================================================
--- sandbox/bartvde/sencha/openlayers/lib/sencha/sencha-touch-debug-w-comments.js 2011-02-24 10:17:43 UTC (rev 11391)
+++ sandbox/bartvde/sencha/openlayers/lib/sencha/sencha-touch-debug-w-comments.js 2011-02-24 10:21:54 UTC (rev 11392)
@@ -1,44801 +0,0 @@
-/**
- * @class Ext
- * Ext core utilities and functions.
- * @singleton
- */
-if (typeof Ext === "undefined") {
- Ext = {};
-}
-
-/**
- * Copies all the properties of config to obj.
- * @param {Object} object The receiver of the properties
- * @param {Object} config The source of the properties
- * @param {Object} defaults A different object that will also be applied for default values
- * @return {Object} returns obj
- * @member Ext apply
- */
-Ext.apply = (function() {
- // IE doesn't recognize that toString (and valueOf) method as explicit one but it "thinks" that's a native one.
- for(var key in {valueOf:1}) {
- return function(object, config, defaults) {
- // no "this" reference for friendly out of scope calls
- if (defaults) {
- Ext.apply(object, defaults);
- }
- if (object && config && typeof config === 'object') {
- for (var key in config) {
- object[key] = config[key];
- }
- }
- return object;
- };
- }
- return function(object, config, defaults) {
- // no "this" reference for friendly out of scope calls
- if (defaults) {
- Ext.apply(object, defaults);
- }
- if (object && config && typeof config === 'object') {
- for (var key in config) {
- object[key] = config[key];
- }
- if (config.toString !== Object.prototype.toString) {
- object.toString = config.toString;
- }
- if (config.valueOf !== Object.prototype.valueOf) {
- object.valueOf = config.valueOf;
- }
- }
- return object;
- };
-})();
-
-Ext.apply(Ext, {
- platformVersion: '1.0',
- platformVersionDetail: {
- major: 1,
- minor: 0,
- patch: 2
- },
- userAgent: navigator.userAgent.toLowerCase(),
- cache: {},
- idSeed: 1000,
- BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
- isStrict: document.compatMode == "CSS1Compat",
-
- windowId: 'ext-window',
- documentId: 'ext-document',
-
- /**
- * A reusable empty function
- * @property
- * @type Function
- */
- emptyFn : function() {},
-
- /**
- * True if the page is running over SSL
- * @type Boolean
- */
- isSecure : /^https/i.test(window.location.protocol),
-
- /**
- * True when the document is fully initialized and ready for action
- * @type Boolean
- */
- isReady : false,
-
- /**
- * True to automatically uncache orphaned Ext.Elements periodically (defaults to true)
- * @type Boolean
- */
- enableGarbageCollector : true,
-
- /**
- * True to automatically purge event listeners during garbageCollection (defaults to true).
- * @type Boolean
- */
- enableListenerCollection : true,
-
- /**
- * Copies all the properties of config to obj if they don't already exist.
- * @param {Object} obj The receiver of the properties
- * @param {Object} config The source of the properties
- * @return {Object} returns obj
- */
- applyIf : function(object, config) {
- var property, undefined;
-
- if (object) {
- for (property in config) {
- if (object[property] === undefined) {
- object[property] = config[property];
- }
- }
- }
-
- return object;
- },
-
- /**
- * Repaints the whole page. This fixes frequently encountered painting issues in mobile Safari.
- */
- repaint : function() {
- var mask = Ext.getBody().createChild({
- cls: 'x-mask x-mask-transparent'
- });
- setTimeout(function() {
- mask.remove();
- }, 0);
- },
-
- /**
- * Generates unique ids. If the element already has an id, it is unchanged
- * @param {Mixed} el (optional) The element to generate an id for
- * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
- * @return {String} The generated Id.
- */
- id : function(el, prefix) {
- el = Ext.getDom(el) || {};
- if (el === document) {
- el.id = this.documentId;
- }
- else if (el === window) {
- el.id = this.windowId;
- }
- el.id = el.id || ((prefix || 'ext-gen') + (++Ext.idSeed));
- return el.id;
- },
-
- /**
- * <p>Extends one class to create a subclass and optionally overrides members with the passed literal. This method
- * also adds the function "override()" to the subclass that can be used to override members of the class.</p>
- * For example, to create a subclass of Ext GridPanel:
- * <pre><code>
-MyGridPanel = Ext.extend(Ext.grid.GridPanel, {
-constructor: function(config) {
-
-// Create configuration for this Grid.
- var store = new Ext.data.Store({...});
- var colModel = new Ext.grid.ColumnModel({...});
-
-// Create a new config object containing our computed properties
-// *plus* whatever was in the config parameter.
- config = Ext.apply({
- store: store,
- colModel: colModel
- }, config);
-
- MyGridPanel.superclass.constructor.call(this, config);
-
-// Your postprocessing here
-},
-
-yourMethod: function() {
- // etc.
-}
-});
- </code></pre>
- *
- * <p>This function also supports a 3-argument call in which the subclass's constructor is
- * passed as an argument. In this form, the parameters are as follows:</p>
- * <div class="mdetail-params"><ul>
- * <li><code>subclass</code> : Function <div class="sub-desc">The subclass constructor.</div></li>
- * <li><code>superclass</code> : Function <div class="sub-desc">The constructor of class being extended</div></li>
- * <li><code>overrides</code> : Object <div class="sub-desc">A literal with members which are copied into the subclass's
- * prototype, and are therefore shared among all instances of the new class.</div></li>
- * </ul></div>
- *
- * @param {Function} superclass The constructor of class being extended.
- * @param {Object} overrides <p>A literal with members which are copied into the subclass's
- * prototype, and are therefore shared between all instances of the new class.</p>
- * <p>This may contain a special member named <tt><b>constructor</b></tt>. This is used
- * to define the constructor of the new class, and is returned. If this property is
- * <i>not</i> specified, a constructor is generated and returned which just calls the
- * superclass's constructor passing on its parameters.</p>
- * <p><b>It is essential that you call the superclass constructor in any provided constructor. See example code.</b></p>
- * @return {Function} The subclass constructor from the <code>overrides</code> parameter, or a generated one if not provided.
- */
- extend : function() {
- // inline overrides
- var inlineOverrides = function(o){
- for (var m in o) {
- if (!o.hasOwnProperty(m)) {
- continue;
- }
- this[m] = o[m];
- }
- };
-
- var objectConstructor = Object.prototype.constructor;
-
- return function(subclass, superclass, overrides){
- // First we check if the user passed in just the superClass with overrides
- if (Ext.isObject(superclass)) {
- overrides = superclass;
- superclass = subclass;
- subclass = overrides.constructor != objectConstructor
- ? overrides.constructor
- : function(){ superclass.apply(this, arguments); };
- }
-
- if (!superclass) {
- throw "Attempting to extend from a class which has not been loaded on the page.";
- }
-
- // We create a new temporary class
- var F = function(){},
- subclassProto,
- superclassProto = superclass.prototype;
-
- F.prototype = superclassProto;
- subclassProto = subclass.prototype = new F();
- subclassProto.constructor = subclass;
- subclass.superclass = superclassProto;
-
- if(superclassProto.constructor == objectConstructor){
- superclassProto.constructor = superclass;
- }
-
- subclass.override = function(overrides){
- Ext.override(subclass, overrides);
- };
-
- subclassProto.superclass = subclassProto.supr = (function(){
- return superclassProto;
- });
-
- subclassProto.override = inlineOverrides;
- subclassProto.proto = subclassProto;
-
- subclass.override(overrides);
- subclass.extend = function(o) {
- return Ext.extend(subclass, o);
- };
-
- return subclass;
- };
- }(),
-
- /**
- * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
- * Usage:<pre><code>
-Ext.override(MyClass, {
-newMethod1: function(){
- // etc.
-},
-newMethod2: function(foo){
- // etc.
-}
-});
- </code></pre>
- * @param {Object} origclass The class to override
- * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
- * containing one or more methods.
- * @method override
- */
- override : function(origclass, overrides) {
- Ext.apply(origclass.prototype, overrides);
- },
-
- /**
- * Creates namespaces to be used for scoping variables and classes so that they are not global.
- * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
- * <pre><code>
-Ext.namespace('Company', 'Company.data');
-Ext.namespace('Company.data'); // equivalent and preferable to above syntax
-Company.Widget = function() { ... }
-Company.data.CustomStore = function(config) { ... }
- </code></pre>
- * @param {String} namespace1
- * @param {String} namespace2
- * @param {String} etc
- * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
- * @method namespace
- */
- namespace : function() {
- var ln = arguments.length,
- i, value, split, x, xln, parts, object;
-
- for (i = 0; i < ln; i++) {
- value = arguments[i];
- parts = value.split(".");
- if (window.Ext) {
- object = window[parts[0]] = Object(window[parts[0]]);
- } else {
- object = arguments.callee.caller.arguments[0];
- }
- for (x = 1, xln = parts.length; x < xln; x++) {
- object = object[parts[x]] = Object(object[parts[x]]);
- }
- }
- return object;
- },
-
- /**
- * Takes an object and converts it to an encoded URL. e.g. Ext.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2". Optionally,
- * property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
- * @param {Object} o The object to encode
- * @param {String} pre (optional) A prefix to add to the url encoded string
- * @return {String}
- */
- urlEncode : function(o, pre) {
- var empty,
- buf = [],
- e = encodeURIComponent;
-
- Ext.iterate(o, function(key, item){
- empty = Ext.isEmpty(item);
- Ext.each(empty ? key : item, function(val){
- buf.push('&', e(key), '=', (!Ext.isEmpty(val) && (val != key || !empty)) ? (Ext.isDate(val) ? Ext.encode(val).replace(/"/g, '') : e(val)) : '');
- });
- });
-
- if(!pre){
- buf.shift();
- pre = '';
- }
-
- return pre + buf.join('');
- },
-
- /**
- * Takes an encoded URL and and converts it to an object. Example:
- * <pre><code>
-Ext.urlDecode("foo=1&bar=2"); // returns {foo: "1", bar: "2"}
-Ext.urlDecode("foo=1&bar=2&bar=3&bar=4", false); // returns {foo: "1", bar: ["2", "3", "4"]}
- </code></pre>
- * @param {String} string
- * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
- * @return {Object} A literal with members
- */
- urlDecode : function(string, overwrite) {
- if (Ext.isEmpty(string)) {
- return {};
- }
-
- var obj = {},
- pairs = string.split('&'),
- d = decodeURIComponent,
- name,
- value;
-
- Ext.each(pairs, function(pair) {
- pair = pair.split('=');
- name = d(pair[0]);
- value = d(pair[1]);
- obj[name] = overwrite || !obj[name] ? value : [].concat(obj[name]).concat(value);
- });
-
- return obj;
- },
-
- /**
- * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
- * @param {String} value The string to encode
- * @return {String} The encoded text
- */
- htmlEncode : function(value) {
- return Ext.util.Format.htmlEncode(value);
- },
-
- /**
- * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
- * @param {String} value The string to decode
- * @return {String} The decoded text
- */
- htmlDecode : function(value) {
- return Ext.util.Format.htmlDecode(value);
- },
-
- /**
- * Appends content to the query string of a URL, handling logic for whether to place
- * a question mark or ampersand.
- * @param {String} url The URL to append to.
- * @param {String} s The content to append to the URL.
- * @return (String) The resulting URL
- */
- urlAppend : function(url, s) {
- if (!Ext.isEmpty(s)) {
- return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
- }
- return url;
- },
-
- /**
- * Converts any iterable (numeric indices and a length property) into a true array
- * Don't use this on strings. IE doesn't support "abc"[0] which this implementation depends on.
- * For strings, use this instead: "abc".match(/./g) => [a,b,c];
- * @param {Iterable} array the iterable object to be turned into a true Array.
- * @param {Number} start a number that specifies where to start the selection.
- * @param {Number} end a number that specifies where to end the selection.
- * @return (Array) array
- */
- toArray : function(array, start, end) {
- return Array.prototype.slice.call(array, start || 0, end || array.length);
- },
-
- /**
- * Iterates an array calling the supplied function.
- * @param {Array/NodeList/Mixed} array The array to be iterated. If this
- * argument is not really an array, the supplied function is called once.
- * @param {Function} fn The function to be called with each item. If the
- * supplied function returns false, iteration stops and this method returns
- * the current <code>index</code>. This function is called with
- * the following arguments:
- * <div class="mdetail-params"><ul>
- * <li><code>item</code> : <i>Mixed</i>
- * <div class="sub-desc">The item at the current <code>index</code>
- * in the passed <code>array</code></div></li>
- * <li><code>index</code> : <i>Number</i>
- * <div class="sub-desc">The current index within the array</div></li>
- * <li><code>allItems</code> : <i>Array</i>
- * <div class="sub-desc">The <code>array</code> passed as the first
- * argument to <code>Ext.each</code>.</div></li>
- * </ul></div>
- * @param {Object} scope The scope (<code>this</code> reference) in which the specified function is executed.
- * Defaults to the <code>item</code> at the current <code>index</code>util
- * within the passed <code>array</code>.
- * @return See description for the fn parameter.
- */
- each : function(array, fn, scope) {
- if (Ext.isEmpty(array, true)) {
- return 0;
- }
- if (!Ext.isIterable(array) || Ext.isPrimitive(array)) {
- array = [array];
- }
- for (var i = 0, len = array.length; i < len; i++) {
- if (fn.call(scope || array[i], array[i], i, array) === false) {
- return i;
- }
- }
- return true;
- },
-
- /**
- * Iterates either the elements in an array, or each of the properties in an object.
- * <b>Note</b>: If you are only iterating arrays, it is better to call {@link #each}.
- * @param {Object/Array} object The object or array to be iterated
- * @param {Function} fn The function to be called for each iteration.
- * The iteration will stop if the supplied function returns false, or
- * all array elements / object properties have been covered. The signature
- * varies depending on the type of object being interated:
- * <div class="mdetail-params"><ul>
- * <li>Arrays : <tt>(Object item, Number index, Array allItems)</tt>
- * <div class="sub-desc">
- * When iterating an array, the supplied function is called with each item.</div></li>
- * <li>Objects : <tt>(String key, Object value, Object)</tt>
- * <div class="sub-desc">
- * When iterating an object, the supplied function is called with each key-value pair in
- * the object, and the iterated object</div></li>
- * </ul></divutil>
- * @param {Object} scope The scope (<code>this</code> reference) in which the specified function is executed. Defaults to
- * the <code>object</code> being iterated.
- */
- iterate : function(obj, fn, scope) {
- if (Ext.isEmpty(obj)) {
- return;
- }
- if (Ext.isIterable(obj)) {
- Ext.each(obj, fn, scope);
- return;
- }
- else if (Ext.isObject(obj)) {
- for (var prop in obj) {
- if (obj.hasOwnProperty(prop)) {
- if (fn.call(scope || obj, prop, obj[prop], obj) === false) {
- return;
- }
- }
- }
- }
- },
-
- /**
- * Plucks the value of a property from each item in the Array
- *
-// Example:
-Ext.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
- *
- * @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.
- */
- pluck : function(arr, prop) {
- var ret = [];
- Ext.each(arr, function(v) {
- ret.push(v[prop]);
- });
- return ret;
- },
-
- /**
- * Returns the current document body as an {@link Ext.Element}.
- * @return Ext.Element The document body
- */
- getBody : function() {
- return Ext.get(document.body || false);
- },
-
- /**
- * Returns the current document head as an {@link Ext.Element}.
- * @return Ext.Element The document head
- */
- getHead : function() {
- var head;
-
- return function() {
- if (head == undefined) {
- head = Ext.get(DOC.getElementsByTagName("head")[0]);
- }
-
- return head;
- };
- }(),
-
- /**
- * Returns the current HTML document object as an {@link Ext.Element}.
- * @return Ext.Element The document
- */
- getDoc : function() {
- return Ext.get(document);
- },
-
- /**
- * 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, <tt>undefined</tt> if not found, or <tt>null</tt> if a
- * Class was found.
- */
- getCmp : function(id) {
- return Ext.ComponentMgr.get(id);
- },
-
- /**
- * Returns the current orientation of the mobile device
- * @return {String} Either 'portrait' or 'landscape'
- */
- getOrientation: function() {
- return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
- },
-
- isIterable : function(v) {
- if (!v) {
- return false;
- }
- //check for array or arguments
- if (Ext.isArray(v) || v.callee) {
- return true;
- }
- //check for node list type
- if (/NodeList|HTMLCollection/.test(Object.prototype.toString.call(v))) {
- return true;
- }
-
- //NodeList has an item and length property
- //IXMLDOMNodeList has nextNode method, needs to be checked first.
- return ((typeof v.nextNode != 'undefined' || v.item) && Ext.isNumber(v.length)) || false;
- },
-
- /**
- * 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' && Ext.util.Format.trim(v).length == 0) ? NaN : v);
- return isNaN(v) ? defaultValue : v;
- },
-
- /**
- * <p>Returns true if the passed value is empty.</p>
- * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
- * <li>null</li>
- * <li>undefined</li>
- * <li>an empty array</li>
- * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
- * </ul></div>
- * @param {Mixed} value The value to test
- * @param {Boolean} allowBlank (optional) true to allow empty strings (defaults to false)
- * @return {Boolean}
- */
- isEmpty : function(value, allowBlank) {
- var isNull = value == null,
- emptyArray = (Ext.isArray(value) && !value.length),
- blankAllowed = !allowBlank ? value === '' : false;
-
- return isNull || emptyArray || blankAllowed;
- },
-
- /**
- * Returns true if the passed value is a JavaScript array, otherwise false.
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isArray : function(v) {
- return Object.prototype.toString.apply(v) === '[object Array]';
- },
-
- /**
- * Returns true if the passed object is a JavaScript date object, otherwise false.
- * @param {Object} object The object to test
- * @return {Boolean}
- */
- isDate : function(v) {
- return Object.prototype.toString.apply(v) === '[object Date]';
- },
-
- /**
- * Returns true if the passed value is a JavaScript Object, otherwise false.
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isObject : function(v) {
- return !!v && !v.tagName && Object.prototype.toString.call(v) === '[object Object]';
- },
-
- /**
- * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isPrimitive : function(v) {
- return Ext.isString(v) || Ext.isNumber(v) || Ext.isBoolean(v);
- },
-
- /**
- * Returns true if the passed value is a JavaScript Function, otherwise false.
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isFunction : function(v) {
- return Object.prototype.toString.apply(v) === '[object Function]';
- },
-
- /**
- * Returns true if the passed value is a number. Returns false for non-finite numbers.
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isNumber : function(v) {
- return Object.prototype.toString.apply(v) === '[object Number]' && isFinite(v);
- },
-
- /**
- * Returns true if the passed value is a string.
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isString : function(v) {
- return typeof v === 'string';
- },
-
- /**util
- * Returns true if the passed value is a boolean.
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isBoolean : function(v) {
- return Object.prototype.toString.apply(v) === '[object Boolean]';
- },
-
- /**
- * Returns true if the passed value is an HTMLElement
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isElement : function(v) {
- return v ? !!v.tagName : false;
- },
-
- /**
- * Returns true if the passed value is not undefined.
- * @param {Mixed} value The value to test
- * @return {Boolean}
- */
- isDefined : function(v){
- return typeof v !== 'undefined';
- },
-
- /**
- * 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() {
- var ln = arguments.length,
- i, arg;
-
- for (i = 0; i < ln; i++) {
- arg = arguments[i];
- if (arg) {
- if (Ext.isArray(arg)) {
- this.destroy.apply(this, arg);
- }
- else if (Ext.isFunction(arg.destroy)) {
- arg.destroy();
- }
- else if (arg.dom) {
- arg.remove();
- }
- }
- }
- }
-});
-
-/**
- * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
- * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
- * @type String
- */
-Ext.SSL_SECURE_URL = Ext.isSecure && 'about:blank';
-
-Ext.ns = Ext.namespace;
-
-Ext.ns(
- 'Ext.util',
- 'Ext.data',
- 'Ext.list',
- 'Ext.form',
- 'Ext.menu',
- 'Ext.state',
- 'Ext.layout',
- 'Ext.app',
- 'Ext.ux',
- 'Ext.plugins',
- 'Ext.direct',
- 'Ext.lib',
- 'Ext.gesture'
-);
-
-/**
- * @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.<br>
- * For example:
- * <pre><code>
-Employee = Ext.extend(Ext.util.Observable, {
- constructor: function(config){
- this.name = config.name;
- this.addEvents({
- "fired" : true,
- "quit" : true
- });
-
- // Copy configured listeners into *this* object so that the base class's
- // constructor will add them.
- this.listeners = config.listeners;
-
- // Call our superclass constructor to complete construction process.
- Employee.superclass.constructor.call(this, config)
- }
-});
-</code></pre>
- * This could then be used like this:<pre><code>
-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!");
- }
- }
-});
-</code></pre>
- */
-
-Ext.util.Observable = Ext.extend(Object, {
- /**
- * @cfg {Object} listeners (optional) <p>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.</p>
- * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
- * <br><p>While <i>some</i> 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
- * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
- * events directly from a child element of a Component, we need to specify the <code>element</code> option to
- * identify the Component property to add a DOM listener to:
- * <pre><code>
-new Ext.Panel({
- width: 400,
- height: 200,
- dockedItems: [{
- xtype: 'toolbar'
- }],
- listeners: {
- click: {
- element: 'el', //bind to the underlying el property on the panel
- fn: function(){ console.log('click el'); }
- },
- dblclick: {
- element: 'body', //bind to the underlying body property on the panel
- fn: function(){ console.log('dblclick body'); }
- }
- }
-});
-</code></pre>
- * </p>
- */
- // @private
- isObservable: true,
-
- constructor: function(config) {
- var me = this;
-
- Ext.apply(me, config);
- if (me.listeners) {
- me.on(me.listeners);
- delete me.listeners;
- }
- me.events = me.events || {};
-
- if (this.bubbleEvents) {
- this.enableBubble(this.bubbleEvents);
- }
- },
-
- // @private
- eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal)$/,
-
- /**
- * <p>Adds listeners to any Observable object (or Element) which are automatically removed when this Component
- * is destroyed.
- * @param {Observable|Element} item The item to which to add a listener/listeners.
- * @param {Object|String} ename The event name, or an object containing event name properties.
- * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
- * is the handler function.
- * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
- * is the scope (<code>this</code> reference) in which the handler function is executed.
- * @param {Object} opt Optional. If the <code>ename</code> parameter was an event name, this
- * is the {@link Ext.util.Observable#addListener addListener} options.
- */
- addManagedListener : function(item, ename, fn, scope, options) {
- var me = this,
- managedListeners = me.managedListeners = me.managedListeners || [],
- config;
-
- if (Ext.isObject(ename)) {
- options = ename;
- for (ename in options) {
- if (!options.hasOwnProperty(ename)) {
- continue;
- }
- config = options[ename];
- if (!me.eventOptionsRe.test(ename)) {
- me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
- }
- }
- }
- else {
- managedListeners.push({
- item: item,
- ename: ename,
- fn: fn,
- scope: scope,
- options: options
- });
-
- item.on(ename, fn, scope, options);
- }
- },
-
- /**
- * Removes listeners that were added by the {@link #mon} method.
- * @param {Observable|Element} item The item from which to remove a listener/listeners.
- * @param {Object|String} ename The event name, or an object containing event name properties.
- * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
- * is the handler function.
- * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
- * is the scope (<code>this</code> reference) in which the handler function is executed.
- */
- removeManagedListener : function(item, ename, fn, scope) {
- var me = this,
- o,
- config,
- managedListeners,
- managedListener,
- length,
- i;
-
- if (Ext.isObject(ename)) {
- o = ename;
- for (ename in o) {
- if (!o.hasOwnProperty(ename)) {
- continue;
- }
- config = o[ename];
- if (!me.eventOptionsRe.test(ename)) {
- me.removeManagedListener(item, ename, config.fn || config, config.scope || o.scope);
- }
- }
- }
-
- managedListeners = this.managedListeners ? this.managedListeners.slice() : [];
- length = managedListeners.length;
-
- for (i = 0; i < length; i++) {
- managedListener = managedListeners[i];
- if (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope)) {
- this.managedListeners.remove(managedListener);
- item.un(managedListener.ename, managedListener.fn, managedListener.scope);
- }
- }
- },
-
- /**
- * <p>Fires the specified event with the passed parameters (minus the event name).</p>
- * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
- * by calling {@link #enableBubble}.</p>
- * @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 me = this,
- a = Ext.toArray(arguments),
- ename = a[0].toLowerCase(),
- ret = true,
- ev = me.events[ename],
- queue = me.eventQueue,
- parent;
-
- if (me.eventsSuspended === true) {
- if (queue) {
- queue.push(a);
- }
- return false;
- }
- else if (ev && Ext.isObject(ev) && ev.bubble) {
- if (ev.fire.apply(ev, a.slice(1)) === false) {
- return false;
- }
- parent = me.getBubbleTarget && me.getBubbleTarget();
- if (parent && parent.isObservable) {
- if (!parent.events[ename] || !Ext.isObject(parent.events[ename]) || !parent.events[ename].bubble) {
- parent.enableBubble(ename);
- }
- return parent.fireEvent.apply(parent, a);
- }
- }
- else if (ev && Ext.isObject(ev)) {
- a.shift();
- ret = ev.fire.apply(ev, a);
- }
- return ret;
- },
-
- /**
- * Appends an event handler to this object.
- * @param {String} eventName The name of the event to listen for. May also be an object who's property names are event names. See
- * @param {Function} handler The method the event invokes.
- * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
- * <b>If omitted, defaults to the object which fired the event.</b>
- * @param {Object} options (optional) An object containing handler configuration.
- * properties. This may contain any of the following properties:<ul>
- * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
- * <b>If omitted, defaults to the object which fired the event.</b></div></li>
- * <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
- * <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
- * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
- * by the specified number of milliseconds. If the event fires again within that time, the original
- * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
- * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
- * if the event was bubbled up from a child Observable.</div></li>
- * <li><b>element</b> : String<div class="sub-desc"><b>This option is only valid for listeners bound to {@link Ext.Component Components}.</b>
- * The name of a Component property which references an element to add a listener to.
- * <p>This option is useful during Component construction to add DOM event listeners to elements of {@link Ext.Component Components} which
- * will exist only after the Component is rendered. For example, to add a click listener to a Panel's body:<pre><code>
-new Ext.Panel({
- title: 'The title',
- listeners: {
- click: this.handlePanelClick,
- element: 'body'
- }
-});
-</code></pre></p>
- * <p>When added in this way, the options available are the options applicable to {@link Ext.Element#addListener}</p></div></li>
- * </ul><br>
- * <p>
- * <b>Combining Options</b><br>
- * Using the options argument, it is possible to combine different types of listeners:<br>
- * <br>
- * A delayed, one-time listener.
- * <pre><code>
-myPanel.on('hide', this.handleClick, this, {
-single: true,
-delay: 100
-});</code></pre>
- * <p>
- * <b>Attaching multiple handlers in 1 call</b><br>
- * The method also allows for a single argument to be passed which is a config object containing properties
- * which specify multiple events. For example:<pre><code>
-myGridPanel.on({
- cellClick: this.onCellClick,
- mouseover: this.onMouseOver,
- mouseout: this.onMouseOut,
- scope: this // Important. Ensure "this" is correct during handler execution
-});
-</code></pre>.
- * <p>
- */
- addListener: function(ename, fn, scope, o) {
- var me = this,
- config,
- ev;
-
- if (Ext.isObject(ename)) {
- o = ename;
- for (ename in o) {
- if (!o.hasOwnProperty(ename)) {
- continue;
- }
- config = o[ename];
- if (!me.eventOptionsRe.test(ename)) {
- me.addListener(ename, config.fn || config, config.scope || o.scope, config.fn ? config : o);
- }
- }
- }
- else {
- ename = ename.toLowerCase();
- me.events[ename] = me.events[ename] || true;
- ev = me.events[ename] || true;
- if (Ext.isBoolean(ev)) {
- me.events[ename] = ev = new Ext.util.Event(me, ename);
- }
- ev.addListener(fn, scope, Ext.isObject(o) ? o: {});
- }
- },
-
- /**
- * Removes an event handler.
- * @param {String} eventName The type of event the handler was associated with.
- * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
- * @param {Object} scope (optional) The scope originally specified for the handler.
- */
- removeListener: function(ename, fn, scope) {
- var me = this,
- config,
- ev;
-
- if (Ext.isObject(ename)) {
- var o = ename;
- for (ename in o) {
- if (!o.hasOwnProperty(ename)) {
- continue;
- }
- config = o[ename];
- if (!me.eventOptionsRe.test(ename)) {
- me.removeListener(ename, config.fn || config, config.scope || o.scope);
- }
- }
- }
- else {
- ename = ename.toLowerCase();
- ev = me.events[ename];
- if (ev.isEvent) {
- ev.removeListener(fn, scope);
- }
- }
- },
-
- /**
- * Removes all listeners for this object including the managed listeners
- */
- clearListeners: function() {
- var events = this.events,
- ev,
- key;
-
- for (key in events) {
- if (!events.hasOwnProperty(key)) {
- continue;
- }
- ev = events[key];
- if (ev.isEvent) {
- ev.clearListeners();
- }
- }
-
- this.clearManagedListeners();
- },
-
- purgeListeners : function() {
- console.warn('MixedCollection: purgeListeners has been deprecated. Please use clearListeners.');
- return this.clearListeners.apply(this, arguments);
- },
-
- /**
- * Removes all managed listeners for this object.
- */
- clearManagedListeners : function() {
- var managedListeners = this.managedListeners || [],
- ln = managedListeners.length,
- i, managedListener;
-
- for (i = 0; i < ln; i++) {
- managedListener = managedListeners[i];
- managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
- }
-
- this.managedListener = [];
- },
-
- purgeManagedListeners : function() {
- console.warn('MixedCollection: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
- return this.clearManagedListeners.apply(this, arguments);
- },
-
- /**
- * 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 <code>true</code>
- * 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:<pre><code>
-this.addEvents('storeloaded', 'storecleared');
-</code></pre>
- */
- addEvents: function(o) {
- var me = this;
- me.events = me.events || {};
- if (Ext.isString(o)) {
- var a = arguments,
- i = a.length;
- while (i--) {
- me.events[a[i]] = me.events[a[i]] || true;
- }
- } else {
- Ext.applyIf(me.events, 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(ename) {
- var e = this.events[ename];
- return e.isEvent === true && e.listeners.length > 0;
- },
-
- /**
- * 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 <tt><b>queueSuspended</b></tt> 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;
-
- Ext.each(queued,
- function(e) {
- me.fireEvent.apply(me, e);
- });
- },
-
- /**
- * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
- * @param {Object} o The Observable whose events this object is to relay.
- * @param {Array} events Array of event names to relay.
- */
- relayEvents : function(origin, events, prefix) {
- prefix = prefix || '';
- var me = this,
- len = events.length,
- i, ename;
-
- function createHandler(ename){
- return function(){
- return me.fireEvent.apply(me, [prefix + ename].concat(Array.prototype.slice.call(arguments, 0, -1)));
- };
- }
-
- for(i = 0, len = events.length; i < len; i++){
- ename = events[i].substr(prefix.length);
- me.events[ename] = me.events[ename] || true;
- origin.on(ename, createHandler(ename), me);
- }
- },
-
- /**
- * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
- * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
- * <p>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.</p>
- * <p>Example:</p><pre><code>
-Ext.override(Ext.form.Field, {
-// Add functionality to Field's initComponent to enable the change event to bubble
-initComponent : Ext.createSequence(Ext.form.Field.prototype.initComponent, 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');
- }
-}
-});
-</code></pre>
- * @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: Ext.toArray(arguments);
- Ext.each(events,
- function(ename) {
- ename = ename.toLowerCase();
- var ce = me.events[ename] || true;
- if (Ext.isBoolean(ce)) {
- ce = new Ext.util.Event(me, ename);
- me.events[ename] = ce;
- }
- ce.bubble = true;
- });
- }
- }
-});
-
-Ext.override(Ext.util.Observable, {
- /**
- * 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 (<code><b>this</b></code> reference) in which the handler function is executed.
- * <b>If omitted, defaults to the object which fired the event.</b>
- * @param {Object} options (optional) An object containing handler configuration.
- * @method
- */
- on: Ext.util.Observable.prototype.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. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
- * @param {Object} scope (optional) The scope originally specified for the handler.
- * @method
- */
- un: Ext.util.Observable.prototype.removeListener,
-
- mon: Ext.util.Observable.prototype.addManagedListener,
- mun: Ext.util.Observable.prototype.removeManagedListener
-});
-
-/**
- * Removes <b>all</b> added captures from the Observable.
- * @param {Observable} o The Observable to release
- * @static
- */
-Ext.util.Observable.releaseCapture = function(o) {
- o.fireEvent = Ext.util.Observable.prototype.fireEvent;
-};
-
-/**
- * Starts capture on the specified Observable. All events will be passed
- * to the supplied function with the event name + standard signature of the event
- * <b>before</b> 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 (<code>this</code> 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 = Ext.createInterceptor(o.fireEvent, fn, scope);
-};
-
-/**
- * Sets observability on the passed class constructor.<p>
- * <p>This makes any event fired on any instance of the passed class also fire a single event through
- * the <i>class</i> allowing for central handling of events on many instances at once.</p>
- * <p>Usage:</p><pre><code>
-Ext.util.Observable.observe(Ext.data.Connection);
-Ext.data.Connection.on('beforerequest', function(con, options) {
- console.log('Ajax request made to ' + options.url);
-});</code></pre>
- * @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.observe = function(cls, listeners) {
- if (cls) {
- if (!cls.isObservable) {
- Ext.applyIf(cls, new Ext.util.Observable());
- Ext.util.Observable.capture(cls.prototype, cls.fireEvent, cls);
- }
- if (typeof listeners == 'object') {
- cls.on(listeners);
- }
- return cls;
- }
-};
-
-//deprecated, will be removed in 5.0
-Ext.util.Observable.observeClass = Ext.util.Observable.observe;
-
-Ext.util.Event = Ext.extend(Object, (function() {
- function createBuffered(handler, listener, o, scope) {
- listener.task = new Ext.util.DelayedTask();
- return function() {
- listener.task.delay(o.buffer, handler, scope, Ext.toArray(arguments));
- };
- };
-
- function createDelayed(handler, listener, o, scope) {
- return function() {
- var task = new Ext.util.DelayedTask();
- if (!listener.tasks) {
- listener.tasks = [];
- }
- listener.tasks.push(task);
- task.delay(o.delay || 10, handler, scope, Ext.toArray(arguments));
- };
- };
-
- function createSingle(handler, listener, o, scope) {
- return function() {
- listener.ev.removeListener(listener.fn, scope);
- return handler.apply(scope, arguments);
- };
- };
-
- return {
- isEvent: true,
-
- constructor: function(observable, name) {
- this.name = name;
- this.observable = observable;
- this.listeners = [];
- },
-
- addListener: function(fn, scope, options) {
- var me = this,
- listener;
- scope = scope || me.observable;
-
- if (!me.isListening(fn, scope)) {
- listener = 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(listener);
- }
- },
-
- createListener: function(fn, scope, o) {
- o = o || {};
- scope = scope || this.observable;
-
- var listener = {
- fn: fn,
- scope: scope,
- o: o,
- ev: this
- },
- handler = fn;
-
- if (o.delay) {
- handler = createDelayed(handler, listener, o, scope);
- }
- if (o.buffer) {
- handler = createBuffered(handler, listener, o, scope);
- }
- if (o.single) {
- handler = createSingle(handler, listener, o, scope);
- }
-
- listener.fireFn = handler;
- return listener;
- },
-
- findListener: function(fn, scope) {
- var listeners = this.listeners,
- i = listeners.length,
- listener,
- s;
-
- while (i--) {
- listener = listeners[i];
- if (listener) {
- s = listener.scope;
- if (listener.fn == fn && (s == scope || s == this.observable)) {
- return i;
- }
- }
- }
-
- return - 1;
- },
-
- isListening: function(fn, scope) {
- return this.findListener(fn, scope) !== -1;
- },
-
- removeListener: function(fn, scope) {
- var me = this,
- index,
- listener,
- k;
- index = me.findListener(fn, scope);
- if (index != -1) {
- listener = me.listeners[index];
-
- if (me.firing) {
- me.listeners = me.listeners.slice(0);
- }
-
- // cancel and remove a buffered handler that hasn't fired yet
- if (listener.task) {
- listener.task.cancel();
- delete listener.task;
- }
-
- // cancel and remove all delayed handlers that haven't fired yet
- k = listener.tasks && listener.tasks.length;
- if (k) {
- while (k--) {
- listener.tasks[k].cancel();
- }
- delete listener.tasks;
- }
-
- // remove this listener from the listeners array
- me.listeners.splice(index, 1);
- return true;
- }
-
- return false;
- },
-
- // Iterate to stop any buffered/delayed events
- clearListeners: function() {
- var listeners = this.listeners,
- i = listeners.length;
-
- while (i--) {
- this.removeListener(listeners[i].fn, listeners[i].scope);
- }
- },
-
- fire: function() {
- var me = this,
- listeners = me.listeners,
- count = listeners.length,
- i,
- args,
- listener;
-
- if (count > 0) {
- me.firing = true;
- for (i = 0; i < count; i++) {
- listener = listeners[i];
- args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
- if (listener.o) {
- args.push(listener.o);
- }
- if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
- return (me.firing = false);
- }
- }
- }
- me.firing = false;
- return true;
- }
- };
-})());
-
-/**
- * @class Ext.util.Stateful
- * @extends Ext.util.Observable
- * Represents any object whose data can be saved by a {@link Ext.data.Proxy Proxy}. Ext.Model
- * and Ext.View both inherit from this class as both can save state (Models save field state,
- * Views save configuration)
- */
-Ext.util.Stateful = Ext.extend(Ext.util.Observable, {
-
- /**
- * Internal flag used to track whether or not the model instance is currently being edited. Read-only
- * @property editing
- * @type Boolean
- */
- editing : false,
-
- /**
- * Readonly flag - true if this Record has been modified.
- * @type Boolean
- */
- dirty : false,
-
- /**
- * @cfg {String} persistanceProperty The property on this Persistable object that its data is saved to.
- * Defaults to 'data' (e.g. all persistable data resides in this.data.)
- */
- persistanceProperty: 'data',
-
- constructor: function(config) {
- Ext.applyIf(this, {
- data: {}
- });
-
- /**
- * Key: value pairs of all fields whose values have changed
- * @property modified
- * @type Object
- */
- this.modified = {};
-
- this[this.persistanceProperty] = {};
-
- Ext.util.Stateful.superclass.constructor.call(this, config);
- },
-
- /**
- * Returns the value of the given field
- * @param {String} fieldName The field to fetch the value for
- * @return {Mixed} The value
- */
- get: function(field) {
- return this[this.persistanceProperty][field];
- },
-
- /**
- * Sets the given field to the given value, marks the instance as dirty
- * @param {String|Object} fieldName The field to set, or an object containing key/value pairs
- * @param {Mixed} value The value to set
- */
- set: function(fieldName, value) {
- var fields = this.fields,
- convertFields = [],
- field, key, i;
-
- /*
- * If we're passed an object, iterate over that object. NOTE: we pull out fields with a convert function and
- * set those last so that all other possible data is set before the convert function is called
- */
- if (arguments.length == 1 && Ext.isObject(fieldName)) {
- for (key in fieldName) {
- if (!fieldName.hasOwnProperty(key)) {
- continue;
- }
-
- //here we check for the custom convert function. Note that if a field doesn't have a convert function,
- //we default it to its type's convert function, so we have to check that here. This feels rather dirty.
- field = fields.get(key);
- if (field && field.convert !== field.type.convert) {
- convertFields.push(key);
- continue;
- }
-
- this.set(key, fieldName[key]);
- }
-
- for (i = 0; i < convertFields.length; i++) {
- field = convertFields[i];
- this.set(field, fieldName[field]);
- }
-
- } else {
- if (fields) {
- field = fields.get(fieldName);
-
- if (field && field.convert) {
- value = field.convert(value, this);
- }
- }
-
- this[this.persistanceProperty][fieldName] = value;
-
- this.dirty = true;
-
- if (!this.editing) {
- this.afterEdit();
- }
- }
- },
-
- /**
- * Gets a hash of only the fields that have been modified since this Model was created or commited.
- * @return Object
- */
- getChanges : function(){
- var modified = this.modified,
- changes = {},
- field;
-
- for (field in modified) {
- if (modified.hasOwnProperty(field)){
- changes[field] = this[this.persistanceProperty][field];
- }
- }
-
- return changes;
- },
-
- /**
- * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
- * since the load or last commit.
- * @param {String} fieldName {@link Ext.data.Field#name}
- * @return {Boolean}
- */
- isModified : function(fieldName) {
- return !!(this.modified && this.modified.hasOwnProperty(fieldName));
- },
-
- /**
- * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>. This method
- * is used interally when adding <code>{@link #phantom}</code> records to a
- * {@link Ext.data.Store#writer writer enabled store}.</p>
- * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
- * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
- * have a create action composed for it during {@link Ext.data.Store#save store save}
- * operations.</p>
- */
- setDirty : function() {
- this.dirty = true;
-
- if (!this.modified) {
- this.modified = {};
- }
-
- this.fields.each(function(field) {
- this.modified[field.name] = this[this.persistanceProperty][field.name];
- }, this);
- },
-
- markDirty : function() {
- throw new Error("Stateful: markDirty has been deprecated. Please use setDirty.");
- },
-
- /**
- * Usually called by the {@link Ext.data.Store} to which this model instance has been {@link #join joined}.
- * Rejects all changes made to the model instance since either creation, or the last commit operation.
- * Modified fields are reverted to their original values.
- * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
- * to have their code notified of reject operations.</p>
- * @param {Boolean} silent (optional) True to skip notification of the owning
- * store of the change (defaults to false)
- */
- reject : function(silent) {
- var modified = this.modified,
- field;
-
- for (field in modified) {
- if (!modified.hasOwnProperty(field)) {
- continue;
- }
- if (typeof modified[field] != "function") {
- this[this.persistanceProperty][field] = modified[field];
- }
- }
-
- this.dirty = false;
- this.editing = false;
- delete this.modified;
-
- if (silent !== true) {
- this.afterReject();
- }
- },
-
- /**
- * Usually called by the {@link Ext.data.Store} which owns the model instance.
- * Commits all changes made to the instance since either creation or the last commit operation.
- * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
- * to have their code notified of commit operations.</p>
- * @param {Boolean} silent (optional) True to skip notification of the owning
- * store of the change (defaults to false)
- */
- commit : function(silent) {
- this.dirty = false;
- this.editing = false;
-
- delete this.modified;
-
- if (silent !== true) {
- this.afterCommit();
- }
- },
-
- /**
- * Creates a copy (clone) of this Model instance.
- * @param {String} id (optional) A new id, defaults to the id
- * of the instance being copied. See <code>{@link #id}</code>.
- * To generate a phantom instance with a new id use:<pre><code>
-var rec = record.copy(); // clone the record
-Ext.data.Model.id(rec); // automatically generate a unique sequential id
- * </code></pre>
- * @return {Record}
- */
- copy : function(newId) {
- return new this.constructor(Ext.apply({}, this[this.persistanceProperty]), newId || this.internalId);
- }
-});
-/**
- * @class Ext.util.HashMap
- * @extends Ext.util.Observable
- * <p>A simple unordered dictionary implementation to store key/value pairs.</p>
- *
- * @cfg {Function} keyFn A function that is used to retrieve a default key for a passed object.
- * A default is provided that returns the <b>id</b> property on the object. This function is only used
- * if the add method is called with a single argument.
- *
- * @constructor
- * @param {Object} config The configuration options
- */
-Ext.util.HashMap = Ext.extend(Ext.util.Observable, {
- constructor: function(config) {
- this.addEvents(
- /**
- * @event add
- * Fires when a new item is added to the hash
- * @param {Ext.util.HashMap} this.
- * @param {String} key The key of the added item.
- * @param {Object} value The value of the added item.
- */
- 'add',
- /**
- * @event clear
- * Fires when the hash is cleared.
- * @param {Ext.util.HashMap} this.
- */
- 'clear',
- /**
- * @event remove
- * Fires when an item is removed from the hash.
- * @param {Ext.util.HashMap} this.
- * @param {String} key The key of the removed item.
- * @param {Object} value The value of the removed item.
- */
- 'remove',
- /**
- * @event replace
- * Fires when an item is replaced in the hash.
- * @param {Ext.util.HashMap} this.
- * @param {String} key The key of the replaced item.
- * @param {Object} value The new value for the item.
- * @param {Object} old The old value for the item.
- */
- 'replace'
- );
-
- Ext.util.HashMap.superclass.constructor.call(this, config);
-
- this.clear(true);
- },
-
- /**
- * Gets the number of items in the hash.
- * @return {Number} The number of items in the hash.
- */
- getCount: function() {
- return this.length;
- },
-
- /**
- * Implementation for being able to extract the key from an object if only
- * a single argument is passed.
- * @private
- * @param {String} key The key
- * @param {Object} value The value
- * @return {Array} [key, value]
- */
- getData: function(key, value) {
- // if we have no value, it means we need to get the key from the object
- if (value === undefined) {
- value = key;
- key = this.getKey(value);
- }
-
- return [key, value];
- },
-
- /**
- * Extracts the key from an object. This is a default implementation, it may be overridden
- * @private
- * @param {Object} o The object to get the key from
- * @return {String} The key to use.
- */
- getKey: function(o) {
- return o.id;
- },
-
- /**
- * Add a new item to the hash. An exception will be thrown if the key already exists.
- * @param {String} key The key of the new item.
- * @param {Object} value The value of the new item.
- * @return {Object} The value of the new item added.
- */
- add: function(key, value) {
- var me = this,
- data;
-
- if (me.containsKey(key)) {
- throw new Error('This key already exists in the HashMap');
- }
-
- data = this.getData(key, value);
- key = data[0];
- value = data[1];
- me.map[key] = value;
- ++me.length;
- me.fireEvent('add', me, key, value);
- return value;
- },
-
- /**
- * Replaces an item in the hash. If the key doesn't exist, the
- * {@link #add} method will be used.
- * @param {String} key The key of the item.
- * @param {Object} value The new value for the item.
- * @return {Object} The new value of the item.
- */
- replace: function(key, value) {
- var me = this,
- map = me.map,
- old;
-
- if (!me.containsKey(key)) {
- me.add(key, value);
- }
- old = map[key];
- map[key] = value;
- me.fireEvent('replace', me, key, value, old);
- return value;
- },
-
- /**
- * Remove an item from the hash.
- * @param {Object} o The value of the item to remove.
- * @return {Boolean} True if the item was successfully removed.
- */
- remove: function(o) {
- var key = this.findKey(o);
- if (key !== undefined) {
- return this.removeByKey(key);
- }
- return false;
- },
-
- /**
- * Remove an item from the hash.
- * @param {String} key The key to remove.
- * @return {Boolean} True if the item was successfully removed.
- */
- removeByKey: function(key) {
- var me = this,
- value;
-
- if (me.containsKey(key)) {
- value = me.map[key];
- delete me.map[key];
- --me.length;
- me.fireEvent('remove', me, key, value);
- return true;
- }
- return false;
- },
-
- /**
- * Retrieves an item with a particular key.
- * @param {String} key The key to lookup.
- * @return {Object} The value at that key. If it doesn't exist, <tt>undefined</tt> is returned.
- */
- get: function(key) {
- return this.map[key];
- },
-
- /**
- * Removes all items from the hash.
- * @return {Ext.util.HashMap} this
- */
- clear: function(/* private */ initial) {
- var me = this;
- me.map = {};
- me.length = 0;
- if (initial !== true) {
- me.fireEvent('clear', me);
- }
- return me;
- },
-
- /**
- * Checks whether a key exists in the hash.
- * @param {String} key The key to check for.
- * @return {Boolean} True if they key exists in the hash.
- */
- containsKey: function(key) {
- return this.map[key] !== undefined;
- },
-
- /**
- * Checks whether a value exists in the hash.
- * @param {Object} value The value to check for.
- * @return {Boolean} True if the value exists in the dictionary.
- */
- contains: function(value) {
- return this.containsKey(this.findKey(value));
- },
-
- /**
- * Return all of the keys in the hash.
- * @return {Array} An array of keys.
- */
- getKeys: function() {
- return this.getArray(true);
- },
-
- /**
- * Return all of the values in the hash.
- * @return {Array} An array of values.
- */
- getValues: function() {
- return this.getArray(false);
- },
-
- /**
- * Gets either the keys/values in an array from the hash.
- * @private
- * @param {Boolean} isKey True to extract the keys, otherwise, the value
- * @return {Array} An array of either keys/values from the hash.
- */
- getArray: function(isKey) {
- var arr = [],
- key,
- map = this.map;
- for (key in map) {
- if (map.hasOwnProperty(key)) {
- arr.push(isKey ? key: map[key]);
- }
- }
- return arr;
- },
-
- /**
- * Executes the specified function once for each item in the hash.
- * Returning false from the function will cease iteration.
- *
- * The paramaters passed to the function are:
- * <div class="mdetail-params"><ul>
- * <li><b>key</b> : String<p class="sub-desc">The key of the item</p></li>
- * <li><b>value</b> : Number<p class="sub-desc">The value of the item</p></li>
- * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the hash</p></li>
- * </ul></div>
- * @param {Function} fn The function to execute.
- * @param {Object} scope The scope to execute in. Defaults to <tt>this</tt>.
- * @return {Ext.util.HashMap} this
- */
- each: function(fn, scope) {
- // copy items so they may be removed during iteration.
- var items = Ext.apply({}, this.map),
- key,
- length = this.length;
-
- scope = scope || this;
- for (key in items) {
- if (items.hasOwnProperty(key)) {
- if (fn.call(scope, key, items[key], length) === false) {
- break;
- }
- }
- }
- return this;
- },
-
- /**
- * Performs a shallow copy on this hash.
- * @return {Ext.util.HashMap} The new hash object.
- */
- clone: function() {
- var hash = new Ext.util.HashMap(),
- map = this.map,
- key;
-
- hash.suspendEvents();
- for (key in map) {
- if (map.hasOwnProperty(key)) {
- hash.add(key, map[key]);
- }
- }
- hash.resumeEvents();
- return hash;
- },
-
- /**
- * @private
- * Find the key for a value.
- * @param {Object} value The value to find.
- * @return {Object} The value of the item. Returns <tt>undefined</tt> if not found.
- */
- findKey: function(value) {
- var key,
- map = this.map;
-
- for (key in map) {
- if (map.hasOwnProperty(key) && map[key] === value) {
- return key;
- }
- }
- return undefined;
- }
-});
-/**
- * @class Ext.util.MixedCollection
- * @extends Ext.util.Observable
- * A Collection class that maintains both numeric indexes and keys and exposes events.
- * @constructor
- * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
- * function should add function references to the collection. Defaults to
- * <tt>false</tt>.
- * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
- * and return the key value for that item. This is used when available to look up the key on items that
- * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
- * equivalent to providing an implementation for the {@link #getKey} method.
- */
-Ext.util.MixedCollection = function(allowFunctions, keyFn) {
- this.items = [];
- this.map = {};
- this.keys = [];
- this.length = 0;
- this.addEvents(
- /**
- * @event clear
- * Fires when the collection is cleared.
- */
- 'clear',
- /**
- * @event add
- * Fires when an item is added to the collection.
- * @param {Number} index The index at which the item was added.
- * @param {Object} o The item added.
- * @param {String} key The key associated with the added item.
- */
- 'add',
- /**
- * @event replace
- * Fires when an item is replaced in the collection.
- * @param {String} key he key associated with the new added.
- * @param {Object} old The item being replaced.
- * @param {Object} new The new item.
- */
- 'replace',
- /**
- * @event remove
- * Fires when an item is removed from the collection.
- * @param {Object} o The item being removed.
- * @param {String} key (optional) The key associated with the removed item.
- */
- 'remove',
- 'sort'
- );
- this.allowFunctions = allowFunctions === true;
- if (keyFn) {
- this.getKey = keyFn;
- }
- Ext.util.MixedCollection.superclass.constructor.call(this);
-};
-
-Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
-
- /**
- * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
- * function should add function references to the collection. Defaults to
- * <tt>false</tt>.
- */
- allowFunctions : false,
-
- /**
- * Adds an item to the collection. Fires the {@link #add} event when complete.
- * @param {String} key <p>The key to associate with the item, or the new item.</p>
- * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
- * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
- * the MixedCollection will be able to <i>derive</i> the key for the new item.
- * In this case just pass the new item in this parameter.</p>
- * @param {Object} o The item to add.
- * @return {Object} The item added.
- */
- add : function(key, obj){
- var myObj = obj, myKey = key;
- if(arguments.length == 1){
- myObj = myKey;
- myKey = this.getKey(myObj);
- }
- if(typeof myKey != 'undefined' && myKey !== null){
- var old = this.map[myKey];
- if(typeof old != 'undefined'){
- return this.replace(myKey, myObj);
- }
- this.map[myKey] = myObj;
- }
- this.length++;
- this.items.push(myObj);
- this.keys.push(myKey);
- this.fireEvent('add', this.length-1, myObj, myKey);
- return myObj;
- },
-
- /**
- * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
- * simply returns <b><code>item.id</code></b> but you can provide your own implementation
- * to return a different value as in the following examples:<pre><code>
-// normal way
-var mc = new Ext.util.MixedCollection();
-mc.add(someEl.dom.id, someEl);
-mc.add(otherEl.dom.id, otherEl);
-//and so on
-
-// using getKey
-var mc = new Ext.util.MixedCollection();
-mc.getKey = function(el){
- return el.dom.id;
-};
-mc.add(someEl);
-mc.add(otherEl);
-
-// or via the constructor
-var mc = new Ext.util.MixedCollection(false, function(el){
- return el.dom.id;
-});
-mc.add(someEl);
-mc.add(otherEl);
- * </code></pre>
- * @param {Object} item The item for which to find the key.
- * @return {Object} The key for the passed item.
- */
- getKey : function(o){
- return o.id;
- },
-
- /**
- * Replaces an item in the collection. Fires the {@link #replace} event when complete.
- * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
- * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
- * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
- * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
- * with one having the same key value, then just pass the replacement item in this parameter.</p>
- * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
- * with that key.
- * @return {Object} The new item.
- */
- replace : function(key, o){
- if(arguments.length == 1){
- o = arguments[0];
- key = this.getKey(o);
- }
- var old = this.map[key];
- if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
- return this.add(key, o);
- }
- var index = this.indexOfKey(key);
- this.items[index] = o;
- this.map[key] = o;
- this.fireEvent('replace', key, old, o);
- return o;
- },
-
- /**
- * Adds all elements of an Array or an Object to the collection.
- * @param {Object/Array} objs An Object containing properties which will be added
- * to the collection, or an Array of values, each of which are added to the collection.
- * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
- * has been set to <tt>true</tt>.
- */
- addAll : function(objs){
- if(arguments.length > 1 || Ext.isArray(objs)){
- var args = arguments.length > 1 ? arguments : objs;
- for(var i = 0, len = args.length; i < len; i++){
- this.add(args[i]);
- }
- }else{
- for(var key in objs){
- if (!objs.hasOwnProperty(key)) {
- continue;
- }
- if(this.allowFunctions || typeof objs[key] != 'function'){
- this.add(key, objs[key]);
- }
- }
- }
- },
-
- /**
- * Executes the specified function once for every item in the collection, passing the following arguments:
- * <div class="mdetail-params"><ul>
- * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
- * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
- * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
- * </ul></div>
- * The function should return a boolean value. Returning false from the function will stop the iteration.
- * @param {Function} fn The function to execute for each item.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
- */
- each : function(fn, scope){
- var items = [].concat(this.items); // each safe for removal
- for(var i = 0, len = items.length; i < len; i++){
- if(fn.call(scope || items[i], items[i], i, len) === false){
- break;
- }
- }
- },
-
- /**
- * Executes the specified function once for every key in the collection, passing each
- * key, and its associated item as the first two parameters.
- * @param {Function} fn The function to execute for each item.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
- */
- eachKey : function(fn, scope){
- for(var i = 0, len = this.keys.length; i < len; i++){
- fn.call(scope || window, this.keys[i], this.items[i], i, len);
- }
- },
-
- /**
- * Returns the first item in the collection which elicits a true return value from the
- * passed selection function.
- * @param {Function} fn The selection function to execute for each item.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
- * @return {Object} The first item in the collection which returned true from the selection function.
- */
- findBy : function(fn, scope) {
- for(var i = 0, len = this.items.length; i < len; i++){
- if(fn.call(scope || window, this.items[i], this.keys[i])){
- return this.items[i];
- }
- }
- return null;
- },
-
-
- /**
- * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
- * @param {Number} index The index to insert the item at.
- * @param {String} key The key to associate with the new item, or the item itself.
- * @param {Object} o (optional) If the second parameter was a key, the new item.
- * @return {Object} The item inserted.
- */
- insert : function(index, key, obj){
- var myKey = key, myObj = obj;
- if(arguments.length == 2){
- myObj = myKey;
- myKey = this.getKey(myObj);
- }
- if(this.containsKey(myKey)){
- this.suspendEvents();
- this.removeByKey(myKey);
- this.resumeEvents();
- }
- if(index >= this.length){
- return this.add(myKey, myObj);
- }
- this.length++;
- this.items.splice(index, 0, myObj);
- if(typeof myKey != 'undefined' && myKey !== null){
- this.map[myKey] = myObj;
- }
- this.keys.splice(index, 0, myKey);
- this.fireEvent('add', index, myObj, myKey);
- return myObj;
- },
-
- /**
- * Remove an item from the collection.
- * @param {Object} o The item to remove.
- * @return {Object} The item removed or false if no item was removed.
- */
- remove : function(o){
- return this.removeAt(this.indexOf(o));
- },
-
- /**
- * Remove all items in the passed array from the collection.
- * @param {Array} items An array of items to be removed.
- * @return {Ext.util.MixedCollection} this object
- */
- removeAll : function(items){
- Ext.each(items || [], function(item) {
- this.remove(item);
- }, this);
-
- return this;
- },
-
- /**
- * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
- * @param {Number} index The index within the collection of the item to remove.
- * @return {Object} The item removed or false if no item was removed.
- */
- removeAt : function(index){
- if(index < this.length && index >= 0){
- this.length--;
- var o = this.items[index];
- this.items.splice(index, 1);
- var key = this.keys[index];
- if(typeof key != 'undefined'){
- delete this.map[key];
- }
- this.keys.splice(index, 1);
- this.fireEvent('remove', o, key);
- return o;
- }
- return false;
- },
-
- /**
- * Removed an item associated with the passed key fom the collection.
- * @param {String} key The key of the item to remove.
- * @return {Object} The item removed or false if no item was removed.
- */
- removeByKey : function(key){
- return this.removeAt(this.indexOfKey(key));
- },
-
- removeKey : function() {
- console.warn('MixedCollection: removeKey has been deprecated. Please use removeByKey.');
- return this.removeByKey.apply(this, arguments);
- },
-
- /**
- * Returns the number of items in the collection.
- * @return {Number} the number of items in the collection.
- */
- getCount : function(){
- return this.length;
- },
-
- /**
- * Returns index within the collection of the passed Object.
- * @param {Object} o The item to find the index of.
- * @return {Number} index of the item. Returns -1 if not found.
- */
- indexOf : function(o){
- return this.items.indexOf(o);
- },
-
- /**
- * Returns index within the collection of the passed key.
- * @param {String} key The key to find the index of.
- * @return {Number} index of the key.
- */
- indexOfKey : function(key){
- return this.keys.indexOf(key);
- },
-
- /**
- * Returns the item associated with the passed key OR index.
- * Key has priority over index. This is the equivalent
- * of calling {@link #key} first, then if nothing matched calling {@link #getAt}.
- * @param {String/Number} key The key or index of the item.
- * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
- * If an item was found, but is a Class, returns <tt>null</tt>.
- */
- get : function(key) {
- var mk = this.map[key],
- item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
- return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
- },
-
- item : function() {
- console.warn('MixedCollection: item has been deprecated. Please use get.');
- return this.get.apply(this, arguments);
- },
-
- /**
- * Returns the item at the specified index.
- * @param {Number} index The index of the item.
- * @return {Object} The item at the specified index.
- */
- getAt : function(index) {
- return this.items[index];
- },
-
- itemAt : function() {
- console.warn('MixedCollection: itemAt has been deprecated. Please use getAt.');
- return this.getAt.apply(this, arguments);
- },
-
- /**
- * Returns the item associated with the passed key.
- * @param {String/Number} key The key of the item.
- * @return {Object} The item associated with the passed key.
- */
- getByKey : function(key) {
- return this.map[key];
- },
-
- key : function() {
- console.warn('MixedCollection: key has been deprecated. Please use getByKey.');
- return this.getByKey.apply(this, arguments);
- },
-
- /**
- * Returns true if the collection contains the passed Object as an item.
- * @param {Object} o The Object to look for in the collection.
- * @return {Boolean} True if the collection contains the Object as an item.
- */
- contains : function(o){
- return this.indexOf(o) != -1;
- },
-
- /**
- * Returns true if the collection contains the passed Object as a key.
- * @param {String} key The key to look for in the collection.
- * @return {Boolean} True if the collection contains the Object as a key.
- */
- containsKey : function(key){
- return typeof this.map[key] != 'undefined';
- },
-
- /**
- * Removes all items from the collection. Fires the {@link #clear} event when complete.
- */
- clear : function(){
- this.length = 0;
- this.items = [];
- this.keys = [];
- this.map = {};
- this.fireEvent('clear');
- },
-
- /**
- * Returns the first item in the collection.
- * @return {Object} the first item in the collection..
- */
- first : function() {
- return this.items[0];
- },
-
- /**
- * Returns the last item in the collection.
- * @return {Object} the last item in the collection..
- */
- last : function() {
- return this.items[this.length-1];
- },
-
- /**
- * @private
- * Performs the actual sorting based on a direction and a sorting function. Internally,
- * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
- * the sorted array data back into this.items and this.keys
- * @param {String} property Property to sort by ('key', 'value', or 'index')
- * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
- * @param {Function} fn (optional) Comparison function that defines the sort order.
- * Defaults to sorting by numeric value.
- */
- _sort : function(property, dir, fn){
- var i, len,
- dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
-
- //this is a temporary array used to apply the sorting function
- c = [],
- keys = this.keys,
- items = this.items;
-
- //default to a simple sorter function if one is not provided
- fn = fn || function(a, b) {
- return a - b;
- };
-
- //copy all the items into a temporary array, which we will sort
- for(i = 0, len = items.length; i < len; i++){
- c[c.length] = {
- key : keys[i],
- value: items[i],
- index: i
- };
- }
-
- //sort the temporary array
- c.sort(function(a, b){
- var v = fn(a[property], b[property]) * dsc;
- if(v === 0){
- v = (a.index < b.index ? -1 : 1);
- }
- return v;
- });
-
- //copy the temporary array back into the main this.items and this.keys objects
- for(i = 0, len = c.length; i < len; i++){
- items[i] = c[i].value;
- keys[i] = c[i].key;
- }
-
- this.fireEvent('sort', this);
- },
-
- /**
- * Sorts this collection by <b>item</b> value with the passed comparison function.
- * @param {Array/String} property Set of {@link Ext.util.Sorter} objects to sort by, or a property of each item
- * in the collection to sort on if using the 2 argument form
- * @param {String} direction Optional direction (used in the 2 argument signature of this method). Defaults to "ASC"
- */
- sort : function(property, direction) {
- //in case we were passed an array of sorters
- var sorters = property;
-
- //support for the simple case of sorting by property/direction
- if (Ext.isString(property)) {
- sorters = [new Ext.util.Sorter({
- property : property,
- direction: direction || "ASC"
- })];
- } else if (property instanceof Ext.util.Sorter) {
- sorters = [property];
- } else if (Ext.isObject(property)) {
- sorters = [new Ext.util.Sorter(property)];
- }
-
- var length = sorters.length;
-
- if (length == 0) {
- return;
- }
-
- //construct an amalgamated sorter function which combines all of the Sorters passed
- var sorterFn = function(r1, r2) {
- var result = sorters[0].sort(r1, r2),
- length = sorters.length,
- i;
-
- //if we have more than one sorter, OR any additional sorter functions together
- for (i = 1; i < length; i++) {
- result = result || sorters[i].sort.call(this, r1, r2);
- }
-
- return result;
- };
-
- this.sortBy(sorterFn);
- },
-
- /**
- * Sorts the collection by a single sorter function
- * @param {Function} sorterFn The function to sort by
- */
- sortBy: function(sorterFn) {
- var items = this.items,
- keys = this.keys,
- length = items.length,
- temp = [],
- i;
-
- //first we create a copy of the items array so that we can sort it
- for (i = 0; i < length; i++) {
- temp[i] = {
- key : keys[i],
- value: items[i],
- index: i
- };
- }
-
- temp.sort(function(a, b) {
- var v = sorterFn(a.value, b.value);
- if (v === 0) {
- v = (a.index < b.index ? -1 : 1);
- }
-
- return v;
- });
-
- //copy the temporary array back into the main this.items and this.keys objects
- for (i = 0; i < length; i++) {
- items[i] = temp[i].value;
- keys[i] = temp[i].key;
- }
-
- this.fireEvent('sort', this);
- },
-
- /**
- * Reorders each of the items based on a mapping from old index to new index. Internally this
- * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
- * @param {Object} mapping Mapping from old item index to new item index
- */
- reorder: function(mapping) {
- this.suspendEvents();
-
- var items = this.items,
- index = 0,
- length = items.length,
- order = [],
- remaining = [],
- oldIndex;
-
- //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
- for (oldIndex in mapping) {
- order[mapping[oldIndex]] = items[oldIndex];
- }
-
- for (index = 0; index < length; index++) {
- if (mapping[index] == undefined) {
- remaining.push(items[index]);
- }
- }
-
- for (index = 0; index < length; index++) {
- if (order[index] == undefined) {
- order[index] = remaining.shift();
- }
- }
-
- this.clear();
- this.addAll(order);
-
- this.resumeEvents();
- this.fireEvent('sort', this);
- },
-
- /**
- * Sorts this collection by <b>key</b>s.
- * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
- * @param {Function} fn (optional) Comparison function that defines the sort order.
- * Defaults to sorting by case insensitive string.
- */
- sortByKey : function(dir, fn){
- this._sort('key', dir, fn || function(a, b){
- var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
- return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
- });
- },
-
- keySort : function() {
- console.warn('MixedCollection: keySort has been deprecated. Please use sortByKey.');
- return this.sortByKey.apply(this, arguments);
- },
-
-
- /**
- * Returns a range of items in this collection
- * @param {Number} startIndex (optional) The starting index. Defaults to 0.
- * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
- * @return {Array} An array of items
- */
- getRange : function(start, end){
- var items = this.items;
- if(items.length < 1){
- return [];
- }
- start = start || 0;
- end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
- var i, r = [];
- if(start <= end){
- for(i = start; i <= end; i++) {
- r[r.length] = items[i];
- }
- }else{
- for(i = start; i >= end; i--) {
- r[r.length] = items[i];
- }
- }
- return r;
- },
-
- /**
- * <p>Filters the objects in this collection by a set of {@link Ext.util.Filter Filter}s, or by a single
- * property/value pair with optional parameters for substring matching and case sensitivity. See
- * {@link Ext.util.Filter Filter} for an example of using Filter objects (preferred). Alternatively,
- * MixedCollection can be easily filtered by property like this:</p>
-<pre><code>
-//create a simple store with a few people defined
-var people = new Ext.util.MixedCollection();
-people.addAll([
- {id: 1, age: 25, name: 'Ed'},
- {id: 2, age: 24, name: 'Tommy'},
- {id: 3, age: 24, name: 'Arne'},
- {id: 4, age: 26, name: 'Aaron'}
-]);
-
-//a new MixedCollection containing only the items where age == 24
-var middleAged = people.filter('age', 24);
-</code></pre>
- *
- *
- * @param {Array/String} property A property on your objects, or an array of {@link Ext.util.Filter Filter} objects
- * @param {String/RegExp} value Either string that the property values
- * should start with or a RegExp to test against the property
- * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
- * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
- * @return {MixedCollection} The new filtered collection
- */
- filter : function(property, value, anyMatch, caseSensitive) {
- var filters = [];
-
- //support for the simple case of filtering by property/value
- if (Ext.isString(property)) {
- filters.push(new Ext.util.Filter({
- property : property,
- value : value,
- anyMatch : anyMatch,
- caseSensitive: caseSensitive
- }));
- } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) {
- filters = filters.concat(property);
- }
-
- //at this point we have an array of zero or more Ext.util.Filter objects to filter with,
- //so here we construct a function that combines these filters by ANDing them together
- var filterFn = function(record) {
- var isMatch = true,
- length = filters.length,
- i;
-
- for (i = 0; i < length; i++) {
- var filter = filters[i],
- fn = filter.filterFn,
- scope = filter.scope;
-
- isMatch = isMatch && fn.call(scope, record);
- }
-
- return isMatch;
- };
-
- return this.filterBy(filterFn);
- },
-
- /**
- * Filter by a function. Returns a <i>new</i> collection that has been filtered.
- * The passed function will be called with each object in the collection.
- * If the function returns true, the value is included otherwise it is filtered.
- * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
- * @return {MixedCollection} The new filtered collection
- */
- filterBy : function(fn, scope) {
- var newMC = new Ext.util.MixedCollection(),
- keys = this.keys,
- items = this.items,
- length = items.length,
- i;
-
- newMC.getKey = this.getKey;
-
- for (i = 0; i < length; i++) {
- if (fn.call(scope||this, items[i], keys[i])) {
- newMC.add(keys[i], items[i]);
- }
- }
-
- return newMC;
- },
-
- /**
- * Finds the index of the first matching object in this collection by a specific property/value.
- * @param {String} property The name of a property on your objects.
- * @param {String/RegExp} value A string that the property values
- * should start with or a RegExp to test against the property.
- * @param {Number} start (optional) The index to start searching at (defaults to 0).
- * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
- * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
- * @return {Number} The matched index or -1
- */
- findIndex : function(property, value, start, anyMatch, caseSensitive){
- if(Ext.isEmpty(value, false)){
- return -1;
- }
- value = this.createValueMatcher(value, anyMatch, caseSensitive);
- return this.findIndexBy(function(o){
- return o && value.test(o[property]);
- }, null, start);
- },
-
- /**
- * Find the index of the first matching object in this collection by a function.
- * If the function returns <i>true</i> it is considered a match.
- * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
- * @param {Number} start (optional) The index to start searching at (defaults to 0).
- * @return {Number} The matched index or -1
- */
- findIndexBy : function(fn, scope, start){
- var k = this.keys, it = this.items;
- for(var i = (start||0), len = it.length; i < len; i++){
- if(fn.call(scope||this, it[i], k[i])){
- return i;
- }
- }
- return -1;
- },
-
- /**
- * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
- * and by Ext.data.Store#filter
- * @private
- * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
- * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
- * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
- * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
- */
- createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
- if (!value.exec) { // not a regex
- var er = Ext.util.Format.escapeRegex;
- value = String(value);
-
- if (anyMatch === true) {
- value = er(value);
- } else {
- value = '^' + er(value);
- if (exactMatch === true) {
- value += '$';
- }
- }
- value = new RegExp(value, caseSensitive ? '' : 'i');
- }
- return value;
- },
-
- /**
- * Creates a shallow copy of this collection
- * @return {MixedCollection}
- */
- clone : function(){
- var r = new Ext.util.MixedCollection();
- var k = this.keys, it = this.items;
- for(var i = 0, len = it.length; i < len; i++){
- r.add(k[i], it[i]);
- }
- r.getKey = this.getKey;
- return r;
- }
-});
-/**
- * This method calls {@link #item item()}.
- * Returns the item associated with the passed key OR index. Key has priority
- * over index. This is the equivalent of calling {@link #key} first, then if
- * nothing matched calling {@link #getAt}.
- * @param {String/Number} key The key or index of the item.
- * @return {Object} If the item is found, returns the item. If the item was
- * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
- * returns <tt>null</tt>.
- */
-// Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
-
-/**
- * @class Ext.AbstractManager
- * @extends Object
- * @ignore
- * Base Manager class - extended by ComponentMgr and PluginMgr
- */
-Ext.AbstractManager = Ext.extend(Object, {
- typeName: 'type',
-
- constructor: function(config) {
- Ext.apply(this, config || {});
-
- /**
- * Contains all of the items currently managed
- * @property all
- * @type Ext.util.MixedCollection
- */
- this.all = new Ext.util.HashMap();
-
- this.types = {};
- },
-
- /**
- * 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, <code>undefined</code> if not found, or <code>null</code> if a
- * Class was found.
- */
- get : function(id) {
- return this.all.get(id);
- },
-
- /**
- * Registers an item to be managed
- * @param {Mixed} item The item to register
- */
- register: function(item) {
- this.all.add(item);
- },
-
- /**
- * Unregisters a component by removing it from this manager
- * @param {Mixed} item The item to unregister
- */
- unregister: function(item) {
- this.all.remove(item);
- },
-
- /**
- * <p>Registers a new Component constructor, keyed by a new
- * {@link Ext.Component#xtype}.</p>
- * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
- * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
- * child Components.
- * see {@link Ext.Container#items}</p>
- * @param {String} xtype The mnemonic string by which the Component class may be looked up.
- * @param {Constructor} cls The new Component class.
- */
- registerType : function(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(Ext.util.Format.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 (<code>this</code> 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);
- }
- });
- },
-
- /**
- * Executes the specified function once for each item in the collection.
- * Returning false from the function will cease iteration.
- *
- * The paramaters passed to the function are:
- * <div class="mdetail-params"><ul>
- * <li><b>key</b> : String<p class="sub-desc">The key of the item</p></li>
- * <li><b>value</b> : Number<p class="sub-desc">The value of the item</p></li>
- * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
- * </ul></div>
- * @param {Object} fn The function to execute.
- * @param {Object} scope The scope to execute in. Defaults to <tt>this</tt>.
- */
- each: function(fn, scope){
- this.all.each(fn, scope || this);
- },
-
- /**
- * Gets the number of items in the collection.
- * @return {Number} The number of items in the collection.
- */
- getCount: function(){
- return this.all.getCount();
- }
-});
-
-/**
- * @class Ext.util.DelayedTask
- * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
- * performing setTimeout where a new timeout cancels the old timeout. When called, the
- * task will wait the specified time period before executing. If durng that time period,
- * the task is called again, the original call will be cancelled. This continues so that
- * the function is only called a single time for each iteration.</p>
- * <p>This method is especially useful for things like detecting whether a user has finished
- * typing in a text field. An example would be performing validation on a keypress. You can
- * use this class to buffer the keypress events for a certain number of milliseconds, and
- * perform only if they stop for that amount of time. Usage:</p><pre><code>
-var task = new Ext.util.DelayedTask(function(){
- alert(Ext.getDom('myInputField').value.length);
-});
-// Wait 500ms before calling our function. If the user presses another key
-// during that 500ms, it will be cancelled and we'll wait another 500ms.
-Ext.get('myInputField').on('keypress', function(){
- task.{@link #delay}(500);
-});
- * </code></pre>
- * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
- * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
- * also setup a delayed task for you to buffer events.</p>
- * @constructor The parameters to this constructor serve as defaults and are not required.
- * @param {Function} fn (optional) The default function to call.
- * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
- * function is called. If not specified, <code>this</code> will refer to the browser window.
- * @param {Array} args (optional) The default Array of arguments.
- */
-Ext.util.DelayedTask = function(fn, scope, args) {
- var me = this,
- id,
- call = function() {
- clearInterval(id);
- id = null;
- fn.apply(scope, args || []);
- };
-
- /**
- * Cancels any pending timeout and queues a new one
- * @param {Number} delay The milliseconds to delay
- * @param {Function} newFn (optional) Overrides function passed to constructor
- * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
- * is specified, <code>this</code> will refer to the browser window.
- * @param {Array} newArgs (optional) Overrides args passed to constructor
- */
- this.delay = function(delay, newFn, newScope, newArgs) {
- me.cancel();
- fn = newFn || fn;
- scope = newScope || scope;
- args = newArgs || args;
- id = setInterval(call, delay);
- };
-
- /**
- * Cancel the last queued timeout
- */
- this.cancel = function(){
- if (id) {
- clearInterval(id);
- id = null;
- }
- };
-};
-/**
- * @class Ext.util.GeoLocation
- * @extends Ext.util.Observable
- *
- * Provides a cross browser class for retrieving location information.<br/>
- * <br/>
- * Based on the <a href="http://dev.w3.org/geo/api/spec-source.html">Geolocation API Specification</a>.<br/>
- * If the browser does not implement that specification (Internet Explorer 6-8), it can fallback on Google Gears
- * as long as the browser has it installed, and the following javascript file from google is included on the page:
- * <pre><code><script type="text/javascript" src="http://code.google.com/apis/gears/gears_init.js"></script></code></pre>
- * <br/>
- * Note: Location implementations are only required to return timestamp, longitude, latitude, and accuracy.<br/>
- * Other properties (altitude, altitudeAccuracy, heading, speed) can be null or sporadically returned.<br/>
- * <br/>
- * When instantiated, by default this class immediately begins tracking location information,
- * firing a {@link #locationupdate} event when new location information is available. To disable this
- * location tracking (which may be battery intensive on mobile devices), set {@link #autoUpdate} to false.<br/>
- * When this is done, only calls to {@link #updateLocation} will trigger a location retrieval.<br/>
- * <br/>
- * A {@link #locationerror} event is raised when an error occurs retrieving the location, either due to a user
- * denying the application access to it, or the browser not supporting it.<br/>
- * <br/>
- * The below code shows a GeoLocation making a single retrieval of location information.
- * <pre><code>
-var geo = new Ext.util.GeoLocation({
- autoUpdate: false,
- listeners: {
- locationupdate: function (geo) {
- alert('New latitude: ' + geo.latitude);
- },
- locationerror: function ( geo,
- bTimeout,
- bPermissionDenied,
- bLocationUnavailable,
- message) {
- if(bTimeout){
- alert('Timeout occurred.');
- }
- else{
- alert('Error occurred.');
- }
- }
- }
-});
-geo.updateLocation();</code></pre>
- */
-Ext.util.GeoLocation = Ext.extend(Ext.util.Observable, {
- /**
- * @cfg {Boolean} autoUpdate
- * Defaults to true.<br/>
- * When set to true, continually monitor the location of the device
- * (beginning immediately) and fire {@link #locationupdate}/{@link #locationerror} events.<br/>
- * <br/>
- * When using google gears, if the user denies access or another error occurs, this will be reset to false.
- */
- autoUpdate: true,
-
- //Position interface
- /**
- * Read-only property representing the last retrieved
- * geographical coordinate specified in degrees.
- * @type Number
- */
- latitude: null,
- /**
- * Read-only property representing the last retrieved
- * geographical coordinate specified in degrees.
- * @type Number
- */
- longitude: null,
- /**
- * Read-only property representing the last retrieved
- * accuracy level of the latitude and longitude coordinates,
- * specified in meters.<br/>
- * This will always be a non-negative number.<br/>
- * This corresponds to a 95% confidence level.
- * @type Number
- */
- accuracy: null,
- /**
- * Read-only property representing the last retrieved
- * height of the position, specified in meters above the ellipsoid
- * <a href="http://dev.w3.org/geo/api/spec-source.html#ref-wgs">[WGS84]</a>.
- * @type Number/null
- */
- altitude: null,
- /**
- * Read-only property representing the last retrieved
- * accuracy level of the altitude coordinate, specified in meters.<br/>
- * If altitude is not null then this will be a non-negative number.
- * Otherwise this returns null.<br/>
- * This corresponds to a 95% confidence level.
- * @type Number/null
- */
- altitudeAccuracy: null,
- /**
- * Read-only property representing the last retrieved
- * direction of travel of the hosting device,
- * specified in non-negative degrees between 0 and 359,
- * counting clockwise relative to the true north.<br/>
- * If speed is 0 (device is stationary), then this returns NaN
- * @type Number/null
- */
- heading: null,
- /**
- * Read-only property representing the last retrieved
- * current ground speed of the device, specified in meters per second.<br/>
- * If this feature is unsupported by the device, this returns null.<br/>
- * If the device is stationary, this returns 0,
- * otherwise it returns a non-negative number.
- * @type Number/null
- */
- speed: null,
- /**
- * Read-only property representing when the last retrieved
- * positioning information was acquired by the device.
- * @type Date
- */
- timestamp: null,
-
- //PositionOptions interface
- /**
- * @cfg {Boolean} allowHighAccuracy
- * Defaults to false.<br/>
- * When set to true, provide a hint that the application would like to receive
- * the best possible results. This may result in slower response times or increased power consumption.
- * The user might also deny this capability, or the device might not be able to provide more accurate
- * results than if this option was set to false.
- */
- allowHighAccuracy: false,
-
- /**
- * @cfg {Number} timeout
- * Defaults to Infinity.<br/>
- * The maximum number of milliseconds allowed to elapse between a location update operation
- * and the corresponding {@link #locationupdate} event being raised. If a location was not successfully
- * acquired before the given timeout elapses (and no other internal errors have occurred in this interval),
- * then a {@link #locationerror} event will be raised indicating a timeout as the cause.<br/>
- * Note that the time that is spent obtaining the user permission is <b>not</b> included in the period
- * covered by the timeout. The timeout attribute only applies to the location acquisition operation.<br/>
- * In the case of calling updateLocation, the {@link #locationerror} event will be raised only once.<br/>
- * If {@link #autoUpdate} is set to true, the {@link #locationerror} event could be raised repeatedly.
- * The first timeout is relative to the moment {@link #autoUpdate} was set to true
- * (or this {@link Ext.util.GeoLocation} was initialized with the {@link #autoUpdate} config option set to true).
- * Subsequent timeouts are relative to the moment when the device determines that it's position has changed.
- */
- timeout: Infinity,
- /**
- * @cfg {Number} maximumAge
- * Defaults to 0.<br/>
- * This option indicates that the application is willing to accept cached location information whose age
- * is no greater than the specified time in milliseconds. If maximumAge is set to 0, an attempt to retrieve
- * new location information is made immediately.<br/>
- * Setting the maximumAge to Infinity returns a cached position regardless of its age.<br/>
- * If the device does not have cached location information available whose age is no
- * greater than the specified maximumAge, then it must acquire new location information.<br/>
- * For example, if location information no older than 10 minutes is required, set this property to 600000.
- */
- maximumAge: 0,
- /**
- * Changes the {@link #maximumAge} option and restarts any active
- * location monitoring with the updated setting.
- * @param {Number} maximumAge The value to set the maximumAge option to.
- */
- setMaximumAge: function(maximumAge) {
- this.maximumAge = maximumAge;
- this.setAutoUpdate(this.autoUpdate);
- },
- /**
- * Changes the {@link #timeout} option and restarts any active
- * location monitoring with the updated setting.
- * @param {Number} timeout The value to set the timeout option to.
- */
- setTimeout: function(timeout) {
- this.timeout = timeout;
- this.setAutoUpdate(this.autoUpdate);
- },
- /**
- * Changes the {@link #allowHighAccuracy} option and restarts any active
- * location monitoring with the updated setting.
- * @param {Number} allowHighAccuracy The value to set the allowHighAccuracy option to.
- */
- setAllowHighAccuracy: function(allowHighAccuracy) {
- this.allowHighAccuracy = allowHighAccuracy;
- this.setAutoUpdate(this.autoUpdate);
- },
-
-
- // private Object geolocation provider
- provider : null,
- // private Number tracking current watchPosition
- watchOperation : null,
-
- constructor : function(config) {
- Ext.apply(this, config);
-
-
- this.coords = this; //@deprecated
-
- if (Ext.supports.GeoLocation) {
- this.provider = this.provider ||
- (navigator.geolocation ? navigator.geolocation :
- (window.google || {}).gears ? google.gears.factory.create('beta.geolocation') : null);
- }
-
- this.addEvents(
- /**
- * @private
- * @event update
- * @param {Ext.util.GeoLocation/False} coords
- * Will return false if geolocation fails (disabled, denied access, timed out).
- * @param {Ext.util.GeoLocation} this
- * @deprecated
- */
- 'update',
- /**
- * @event locationerror
- * Raised when a location retrieval operation failed.<br/>
- * In the case of calling updateLocation, this event will be raised only once.<br/>
- * If {@link #autoUpdate} is set to true, this event could be raised repeatedly.
- * The first error is relative to the moment {@link #autoUpdate} was set to true
- * (or this {@link Ext.util.GeoLocation} was initialized with the {@link #autoUpdate} config option set to true).
- * Subsequent errors are relative to the moment when the device determines that it's position has changed.
- * @param {Ext.util.GeoLocation} this
- * @param {Boolean} timeout
- * Boolean indicating a timeout occurred
- * @param {Boolean} permissionDenied
- * Boolean indicating the user denied the location request
- * @param {Boolean} locationUnavailable
- * Boolean indicating that the location of the device could not be determined.<br/>
- * For instance, one or more of the location providers used in the location acquisition
- * process reported an internal error that caused the process to fail entirely.
- * @param {String} message
- * An error message describing the details of the error encountered.<br/>
- * This attribute is primarily intended for debugging and should not be used
- * directly in an application user interface.
- */
- 'locationerror',
- /**
- * @event locationupdate
- * Raised when a location retrieval operation has been completed successfully.
- * @param {Ext.util.GeoLocation} this
- * Retrieve the current location information from the GeoLocation object by using the read-only
- * properties latitude, longitude, accuracy, altitude, altitudeAccuracy, heading, and speed.
- */
- 'locationupdate'
- );
-
- Ext.util.GeoLocation.superclass.constructor.call(this);
-
- if(this.autoUpdate){
- var me = this;
- setTimeout(function(){
- me.setAutoUpdate(me.autoUpdate);
- }, 0);
- }
- },
-
- /**
- * Enabled/disables the auto-retrieval of the location information.<br/>
- * If called with autoUpdate=true, it will execute an immediate location update
- * and continue monitoring for location updates.<br/>
- * If autoUpdate=false, any current location change monitoring will be disabled.
- * @param {Boolean} autoUpdate Whether to start/stop location monitoring.
- * @return {Boolean} If enabling autoUpdate, returns false if the location tracking
- * cannot begin due to an error supporting geolocation.
- * A locationerror event is also fired.
- */
- setAutoUpdate : function(autoUpdate) {
- if (this.watchOperation !== null) {
- this.provider.clearWatch(this.watchOperation);
- this.watchOperation = null;
- }
- if (!autoUpdate) {
- return true;
- }
- if (!Ext.supports.GeoLocation) {
- this.fireEvent('locationerror', this, false, false, true, null);
- return false;
- }
- try{
- this.watchOperation = this.provider.watchPosition(
- Ext.createDelegate(this.fireUpdate, this),
- Ext.createDelegate(this.fireError, this),
- this.parseOptions());
- }
- catch(e){
- this.autoUpdate = false;
- this.fireEvent('locationerror', this, false, false, true, e.message);
- return false;
- }
- return true;
- },
-
- /**
- * Executes a onetime location update operation,
- * raising either a {@link #locationupdate} or {@link #locationerror} event.<br/>
- * Does not interfere with or restart ongoing location monitoring.
- * @param {Function} callback
- * A callback method to be called when the location retrieval has been completed.<br/>
- * Will be called on both success and failure.<br/>
- * The method will be passed one parameter, {@link Ext.GeoLocation} (<b>this</b> reference),
- * set to null on failure.
- * <pre><code>
-geo.updateLocation(function (geo) {
- alert('Latitude: ' + (geo != null ? geo.latitude : 'failed'));
-});
-</code></pre>
- * @param {Object} scope (optional)
- * (optional) The scope (<b>this</b> reference) in which the handler function is executed.
- * <b>If omitted, defaults to the object which fired the event.</b>
- * <!--positonOptions undocumented param, see W3C spec-->
- */
- updateLocation : function(callback, scope, positionOptions) {
- var me = this;
-
- var failFunction = function(message, error){
- if(error){
- me.fireError(error);
- }
- else{
- me.fireEvent('locationerror', me, false, false, true, message);
- }
- if(callback){
- callback.call(scope || me, null, me); //last parameter for legacy purposes
- }
- me.fireEvent('update', false, me); //legacy, deprecated
- };
-
- if (!Ext.supports.GeoLocation) {
- setTimeout(function() {
- failFunction(null);
- }, 0);
- return;
- }
-
- try{
- this.provider.getCurrentPosition(
- //success callback
- function(position){
- me.fireUpdate(position);
- if(callback){
- callback.call(scope || me, me, me); //last parameter for legacy purposes
- }
- me.fireEvent('update', me, me); //legacy, deprecated
- },
- //error callback
- function(error){
- failFunction(null, error);
- },
- positionOptions ? positionOptions : this.parseOptions());
- }
- catch(e){
- setTimeout(function(){
- failFunction(e.message);
- }, 0);
- }
- },
-
- // private
- fireUpdate: function(position){
- this.timestamp = position.timestamp;
- this.latitude = position.coords.latitude;
- this.longitude = position.coords.longitude;
- this.accuracy = position.coords.accuracy;
- this.altitude = position.coords.altitude;
- this.altitudeAccuracy = position.coords.altitudeAccuracy;
-
- //google doesn't provide these two
- this.heading = typeof position.coords.heading == 'undefined' ? null : position.coords.heading;
- this.speed = typeof position.coords.speed == 'undefined' ? null : position.coords.speed;
- this.fireEvent('locationupdate', this);
- },
- fireError: function(error){
- this.fireEvent('locationerror', this,
- error.code == error.TIMEOUT,
- error.code == error.PERMISSION_DENIED,
- error.code == error.POSITION_UNAVAILABLE,
- error.message == undefined ? null : error.message);
- },
- parseOptions: function(){
- var ret = {
- maximumAge: this.maximumAge,
- allowHighAccuracy: this.allowHighAccuracy
- };
- //Google doesn't like Infinity
- if(this.timeout !== Infinity){
- ret.timeout = this.timeout;
- }
- return ret;
- },
-
- /**
- * @private
- * Returns cached coordinates, and updates if there are no cached coords yet.
- * @deprecated
- */
- getLocation : function(callback, scope) {
- var me = this;
- if(this.latitude !== null){
- callback.call(scope || me, me, me);
- }
- else {
- me.updateLocation(callback, scope);
- }
- }
-});
-/**
- * @class Ext.util.Point
- * @extends Object
- *
- * Represents a 2D point with x and y properties, useful for comparison and instantiation
- * from an event:
- * <pre><code>
- * var point = Ext.util.Point.fromEvent(e);
- * </code></pre>
- */
-
-Ext.util.Point = Ext.extend(Object, {
- constructor: function(x, y) {
- this.x = (x != null && !isNaN(x)) ? x : 0;
- this.y = (y != null && !isNaN(y)) ? y : 0;
-
- return this;
- },
-
- /**
- * Copy a new instance of this point
- * @return {Ext.util.Point} the new point
- */
- copy: function() {
- return new Ext.util.Point(this.x, this.y);
- },
-
- /**
- * Copy the x and y values of another point / object to this point itself
- * @param {}
- * @return {Ext.util.Point} this This point
- */
- copyFrom: function(p) {
- this.x = p.x;
- this.y = p.y;
-
- return this;
- },
-
- /**
- * Returns a human-eye-friendly string that represents this point,
- * useful for debugging
- * @return {String}
- */
- toString: function() {
- return "Point[" + this.x + "," + this.y + "]";
- },
-
- /**
- * Compare this point and another point
- * @param {Ext.util.Point/Object} The point to compare with, either an instance
- * of Ext.util.Point or an object with x and y properties
- * @return {Boolean} Returns whether they are equivalent
- */
- equals: function(p) {
- return (this.x == p.x && this.y == p.y);
- },
-
- /**
- * Whether the given point is not away from this point within the given threshold amount
- * @param {Ext.util.Point/Object} The point to check with, either an instance
- * of Ext.util.Point or an object with x and y properties
- * @param {Object/Number} threshold Can be either an object with x and y properties or a number
- * @return {Boolean}
- */
- isWithin: function(p, threshold) {
- if (!Ext.isObject(threshold)) {
- threshold = {x: threshold};
- threshold.y = threshold.x;
- }
-
- return (this.x <= p.x + threshold.x && this.x >= p.x - threshold.x &&
- this.y <= p.y + threshold.y && this.y >= p.y - threshold.y);
- },
-
- /**
- * Translate this point by the given amounts
- * @param {Number} x Amount to translate in the x-axis
- * @param {Number} y Amount to translate in the y-axis
- * @return {Boolean}
- */
- translate: function(x, y) {
- if (x != null && !isNaN(x))
- this.x += x;
-
- if (y != null && !isNaN(y))
- this.y += y;
- },
-
- /**
- * Compare this point with another point when the x and y values of both points are rounded. E.g:
- * [100.3,199.8] will equals to [100, 200]
- * @param {Ext.util.Point/Object} The point to compare with, either an instance
- * of Ext.util.Point or an object with x and y properties
- * @return {Boolean}
- */
- roundedEquals: function(p) {
- return (Math.round(this.x) == Math.round(p.x) && Math.round(this.y) == Math.round(p.y));
- }
-});
-
-/**
- * Returns a new instance of Ext.util.Point base on the pageX / pageY values of the given event
- * @static
- * @param {Event} e The event
- * @returns Ext.util.Point
- */
-Ext.util.Point.fromEvent = function(e) {
- var a = (e.changedTouches && e.changedTouches.length > 0) ? e.changedTouches[0] : e;
- return new Ext.util.Point(a.pageX, a.pageY);
-};
-Ext.util.Offset = Ext.extend(Object, {
- constructor: function(x, y) {
- this.x = (x != null && !isNaN(x)) ? x : 0;
- this.y = (y != null && !isNaN(y)) ? y : 0;
-
- return this;
- },
-
- copy: function() {
- return new Ext.util.Offset(this.x, this.y);
- },
-
- copyFrom: function(p) {
- this.x = p.x;
- this.y = p.y;
- },
-
- toString: function() {
- return "Offset[" + this.x + "," + this.y + "]";
- },
-
- equals: function(offset) {
- if(!(offset instanceof Ext.util.Offset))
- throw new Error('offset must be an instance of Ext.util.Offset');
-
- return (this.x == offset.x && this.y == offset.y);
- },
-
- round: function(to) {
- if (!isNaN(to)) {
- var factor = Math.pow(10, to);
- this.x = Math.round(this.x * factor) / factor;
- this.y = Math.round(this.y * factor) / factor;
- } else {
- this.x = Math.round(this.x);
- this.y = Math.round(this.y);
- }
- },
-
- isZero: function() {
- return this.x == 0 && this.y == 0;
- }
-});
-
-Ext.util.Offset.fromObject = function(obj) {
- return new Ext.util.Offset(obj.x, obj.y);
-};
-/**
- * @class Ext.util.Region
- * @extends Object
- *
- * Represents a rectangular region and provides a number of utility methods
- * to compare regions.
- */
-Ext.util.Region = Ext.extend(Object, {
- /**
- * @constructor
- * @param {Number} top Top
- * @param {Number} right Right
- * @param {Number} bottom Bottom
- * @param {Number} left Left
- */
- constructor : 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;
- },
-
- /**
- * Checks if this region completely contains the region that is passed in.
- * @param {Ext.util.Region} region
- */
- contains : function(region) {
- var me = this;
- return (region.left >= me.left &&
- region.right <= me.right &&
- region.top >= me.top &&
- region.bottom <= me.bottom);
-
- },
-
- /**
- * Checks if this region intersects the region passed in.
- * @param {Ext.util.Region} region
- * @return {Ext.util.Region/Boolean} Returns the intersected region or false if there is no intersection.
- */
- 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.util.Region(t, r, b, l);
- }
- else {
- return false;
- }
- },
-
- /**
- * Returns the smallest region that contains the current AND targetRegion.
- * @param {Ext.util.Region} region
- */
- 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.util.Region(t, r, b, l);
- },
-
- /**
- * Modifies the current region to be constrained to the targetRegion.
- * @param {Ext.util.Region} targetRegion
- */
- constrainTo : function(r) {
- var me = this,
- constrain = Ext.util.Numbers.constrain;
- me.top = constrain(me.top, r.top, r.bottom);
- me.bottom = constrain(me.bottom, r.top, r.bottom);
- me.left = constrain(me.left, r.left, r.right);
- me.right = constrain(me.right, r.left, r.right);
- return me;
- },
-
- /**
- * Modifies the current region to be adjusted by offsets.
- * @param {Number} top top offset
- * @param {Number} right right offset
- * @param {Number} bottom bottom offset
- * @param {Number} left left offset
- */
- adjust : function(t, r, b, l) {
- var me = this;
- me.top += t;
- me.left += l;
- me.right += r;
- me.bottom += b;
- return me;
- },
-
- /**
- * Get the offset amount of a point outside the region
- * @param {String} axis optional
- * @param {Ext.util.Point} p the point
- * @return {Ext.util.Offset}
- */
- getOutOfBoundOffset: function(axis, p) {
- if (!Ext.isObject(axis)) {
- if (axis == 'x') {
- return this.getOutOfBoundOffsetX(p);
- } else {
- return this.getOutOfBoundOffsetY(p);
- }
- } else {
- p = axis;
- var d = new Ext.util.Offset();
- d.x = this.getOutOfBoundOffsetX(p.x);
- d.y = this.getOutOfBoundOffsetY(p.y);
- return d;
- }
-
- },
-
- /**
- * Get the offset amount on the x-axis
- * @param {Number} p the offset
- * @return {Number}
- */
- getOutOfBoundOffsetX: function(p) {
- if (p <= this.left) {
- return this.left - p;
- } else if (p >= this.right) {
- return this.right - p;
- }
-
- return 0;
- },
-
- /**
- * Get the offset amount on the y-axis
- * @param {Number} p the offset
- * @return {Number}
- */
- getOutOfBoundOffsetY: function(p) {
- if (p <= this.top) {
- return this.top - p;
- } else if (p >= this.bottom) {
- return this.bottom - p;
- }
-
- return 0;
- },
-
- /**
- * Check whether the point / offset is out of bound
- * @param {String} axis optional
- * @param {Ext.util.Point/Number} p the point / offset
- * @return {Boolean}
- */
- isOutOfBound: function(axis, p) {
- if (!Ext.isObject(axis)) {
- if (axis == 'x') {
- return this.isOutOfBoundX(p);
- } else {
- return this.isOutOfBoundY(p);
- }
- } else {
- p = axis;
- return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
- }
- },
-
- /**
- * Check whether the offset is out of bound in the x-axis
- * @param {Number} p the offset
- * @return {Boolean}
- */
- isOutOfBoundX: function(p) {
- return (p < this.left || p > this.right);
- },
-
- /**
- * Check whether the offset is out of bound in the y-axis
- * @param {Number} p the offset
- * @return {Boolean}
- */
- isOutOfBoundY: function(p) {
- return (p < this.top || p > this.bottom);
- },
-
- /*
- * Restrict a point within the region by a certain factor.
- * @param {String} axis Optional
- * @param {Ext.util.Point/Ext.util.Offset/Object} p
- * @param {Number} factor
- * @return {Ext.util.Point/Ext.util.Offset/Object/Number}
- */
- restrict: function(axis, p, factor) {
- if (Ext.isObject(axis)) {
- var newP;
-
- factor = p;
- p = axis;
-
- if (p.copy) {
- newP = p.copy();
- }
- else {
- newP = {
- x: p.x,
- y: p.y
- };
- }
-
- newP.x = this.restrictX(p.x, factor);
- newP.y = this.restrictY(p.y, factor);
- return newP;
- } else {
- if (axis == 'x') {
- return this.restrictX(p, factor);
- } else {
- return this.restrictY(p, factor);
- }
- }
- },
-
- /*
- * Restrict an offset within the region by a certain factor, on the x-axis
- * @param {Number} p
- * @param {Number} factor The factor, optional, defaults to 1
- * @return
- */
- restrictX : function(p, factor) {
- if (!factor) {
- factor = 1;
- }
-
- if (p <= this.left) {
- p -= (p - this.left) * factor;
- }
- else if (p >= this.right) {
- p -= (p - this.right) * factor;
- }
- return p;
- },
-
- /*
- * Restrict an offset within the region by a certain factor, on the y-axis
- * @param {Number} p
- * @param {Number} factor The factor, optional, defaults to 1
- */
- restrictY : function(p, factor) {
- if (!factor) {
- factor = 1;
- }
-
- if (p <= this.top) {
- p -= (p - this.top) * factor;
- }
- else if (p >= this.bottom) {
- p -= (p - this.bottom) * factor;
- }
- return p;
- },
-
- /*
- * Get the width / height of this region
- * @return {Object} an object with width and height properties
- */
- getSize: function() {
- return {
- width: this.right - this.left,
- height: this.bottom - this.top
- };
- },
-
- /**
- * Copy a new instance
- * @return {Ext.util.Region}
- */
- copy: function() {
- return new Ext.util.Region(this.top, this.right, this.bottom, this.left);
- },
-
- /**
- * Dump this to an eye-friendly string, great for debugging
- * @return {String}
- */
- toString: function() {
- return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
- },
-
-
- /**
- * Translate this region by the given offset amount
- * @param {Ext.util.Offset/Object} offset
- * @return {Ext.util.Region} this This Region
- */
- translateBy: function(offset) {
- this.left += offset.x;
- this.right += offset.x;
- this.top += offset.y;
- this.bottom += offset.y;
-
- return this;
- },
-
- /**
- * Round all the properties of this region
- * @return {Ext.util.Region} this This Region
- */
- round: function() {
- this.top = Math.round(this.top);
- this.right = Math.round(this.right);
- this.bottom = Math.round(this.bottom);
- this.left = Math.round(this.left);
-
- return this;
- },
-
- /**
- * Check whether this region is equivalent to the given region
- * @param {Ext.util.Region} region The region to compare with
- * @return {Boolean}
- */
- equals: function(region) {
- return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left)
- }
-});
-
-/**
- * @static
- * @param {Mixed} el A string, DomElement or Ext.Element representing an element
- * on the page.
- * @returns {Ext.util.Region} region
- * Retrieves an Ext.util.Region for a particular element.
- */
-Ext.util.Region.getRegion = function(el) {
- return Ext.fly(el).getPageBox(true);
-};
-
-/**
- * @static
- * @param {Object} o An object with top, right, bottom, left properties
- * @return {Ext.util.Region} region The region constructed based on the passed object
- */
-Ext.util.Region.from = function(o) {
- return new Ext.util.Region(o.top, o.right, o.bottom, o.left);
-};
-/**
- * @class Ext.Template
- * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
- * for greater performance.</p>
- * An instance of this class may be created by passing to the constructor either
- * a single argument, or multiple arguments:
- * <div class="mdetail-params"><ul>
- * <li><b>single argument</b> : String/Array
- * <div class="sub-desc">
- * The single argument may be either a String or an Array:<ul>
- * <li><tt>String</tt> : </li><pre><code>
-var t = new Ext.Template("<div>Hello {0}.</div>");
-t.{@link #append}('some-element', ['foo']);
- </code></pre>
- * <li><tt>Array</tt> : </li>
- * An Array will be combined with <code>join('')</code>.
-<pre><code>
-var t = new Ext.Template([
- '<div name="{id}">',
- '<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>',
- '</div>',
-]);
-t.{@link #compile}();
-t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
- </code></pre>
- * </ul></div></li>
- * <li><b>multiple arguments</b> : String, Object, Array, ...
- * <div class="sub-desc">
- * Multiple arguments will be combined with <code>join('')</code>.
- * <pre><code>
-var t = new Ext.Template(
- '<div name="{id}">',
- '<span class="{cls}">{name} {value}</span>',
- '</div>',
- // a configuration object:
- {
- compiled: true, // {@link #compile} immediately
- }
-);
- </code></pre>
- * <p><b>Notes</b>:</p>
- * <div class="mdetail-params"><ul>
- * <li>Formatting and <code>disableFormats</code> are not applicable for Sencha Touch.</li>
- * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
- * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
- * when no formatting is required.</li>
- * </ul></div>
- * </div></li>
- * </ul></div>
- * @param {Mixed} config
- */
-Ext.Template = Ext.extend(Object, {
- constructor: function(html) {
- var me = this,
- args = arguments,
- buffer = [],
- value, i, length;
-
- me.initialConfig = {};
-
- if (Ext.isArray(html)) {
- html = html.join("");
- }
- else if (args.length > 1) {
- for (i = 0, length = args.length; i < length; i++) {
- value = args[i];
- if (typeof value == 'object') {
- Ext.apply(me.initialConfig, value);
- Ext.apply(me, value);
- } else {
- buffer.push(value);
- }
- }
- html = buffer.join('');
- }
-
- // @private
- me.html = html;
-
- if (me.compiled) {
- me.compile();
- }
- },
- isTemplate: true,
- /**
- * @cfg {Boolean} disableFormats true to disable format functions in the template. If the template doesn't contain format functions, setting
- * disableFormats to true will reduce apply time (defaults to false)
- */
- disableFormats: false,
-
- re: /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/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,
- useFormat = me.disableFormats !== true,
- fm = Ext.util.Format,
- tpl = me;
-
- if (me.compiled) {
- return me.compiled(values);
- }
- function fn(m, name, format, args) {
- if (format && useFormat) {
- if (args) {
- args = [values[name]].concat(new Function('return ['+ args +'];')());
- } else {
- args = [values[name]];
- }
- if (format.substr(0, 5) == "this.") {
- return tpl[format.substr(5)].apply(tpl, args);
- }
- else {
- return fm[format].apply(fm, args);
- }
- }
- else {
- return values[name] !== undefined ? values[name] : "";
- }
- }
- return me.html.replace(me.re, fn);
- },
-
- /**
- * Sets the HTML used as the template and optionally compiles it.
- * @param {String} html
- * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
- * @return {Ext.Template} this
- */
- set: function(html, compile) {
- var me = this;
- me.html = html;
- me.compiled = null;
- return compile ? me.compile() : me;
- },
-
- compileARe: /\\/g,
- compileBRe: /(\r\n|\n)/g,
- compileCRe: /'/g,
- /**
- * Compiles the template into an internal function, eliminating the RegEx overhead.
- * @return {Ext.Template} this
- * @hide repeat doc
- */
- compile: function() {
- var me = this,
- fm = Ext.util.Format,
- useFormat = me.disableFormats !== true,
- body, bodyReturn;
-
- function fn(m, name, format, args) {
- if (format && useFormat) {
- args = args ? ',' + args: "";
- if (format.substr(0, 5) != "this.") {
- format = "fm." + format + '(';
- }
- else {
- format = 'this.' + format.substr(5) + '(';
- }
- }
- else {
- args = '';
- format = "(values['" + name + "'] == undefined ? '' : ";
- }
- return "'," + format + "values['" + name + "']" + args + ") ,'";
- }
-
- bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
- body = "this.compiled = function(values){ return ['" + bodyReturn + "'].join('');};";
- eval(body);
- return me;
- },
-
- /**
- * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
- * @param {Mixed} el The context element
- * @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'})
- * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- insertFirst: function(el, values, returnElement) {
- return this.doInsert('afterBegin', el, values, returnElement);
- },
-
- /**
- * Applies the supplied values to the template and inserts the new node(s) before el.
- * @param {Mixed} el The context element
- * @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'})
- * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- insertBefore: function(el, values, returnElement) {
- return this.doInsert('beforeBegin', el, values, returnElement);
- },
-
- /**
- * Applies the supplied values to the template and inserts the new node(s) after el.
- * @param {Mixed} el The context element
- * @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'})
- * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- insertAfter: function(el, values, returnElement) {
- return this.doInsert('afterEnd', el, values, returnElement);
- },
-
- /**
- * Applies the supplied <code>values</code> to the template and appends
- * the new node(s) to the specified <code>el</code>.
- * <p>For example usage {@link #Template see the constructor}.</p>
- * @param {Mixed} el The context element
- * @param {Object/Array} values
- * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
- * or an object (i.e. <code>{foo: 'bar'}</code>).
- * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- append: function(el, values, returnElement) {
- return this.doInsert('beforeEnd', el, values, returnElement);
- },
-
- doInsert: function(where, el, values, returnEl) {
- el = Ext.getDom(el);
- var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
- return returnEl ? Ext.get(newNode, true) : newNode;
- },
-
- /**
- * Applies the supplied values to the template and overwrites the content of el with the new node(s).
- * @param {Mixed} el The context element
- * @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'})
- * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
- * @return {HTMLElement/Ext.Element} The new node or Element
- */
- overwrite: function(el, values, returnElement) {
- el = Ext.getDom(el);
- el.innerHTML = this.applyTemplate(values);
- return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
- }
-});
-/**
- * Alias for {@link #applyTemplate}
- * Returns an HTML fragment of this template with the specified <code>values</code> applied.
- * @param {Object/Array} values
- * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
- * or an object (i.e. <code>{foo: 'bar'}</code>).
- * @return {String} The HTML fragment
- * @member Ext.Template
- * @method apply
- */
-Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
-
-/**
- * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
- * @param {String/HTMLElement} el A DOM element or its id
- * @param {Object} config A configuration object
- * @return {Ext.Template} The created template
- * @static
- */
-Ext.Template.from = function(el, config) {
- el = Ext.getDom(el);
- return new Ext.Template(el.value || el.innerHTML, config || '');
-};
-
-/**
- * @class Ext.XTemplate
- * @extends Ext.Template
- * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
- * <li>Autofilling arrays using templates and sub-templates</li>
- * <li>Conditional processing with basic comparison operators</li>
- * <li>Basic math function support</li>
- * <li>Execute arbitrary inline code with special built-in template variables</li>
- * <li>Custom member functions</li>
- * <li>Many special tags and built-in operators that aren't defined as part of
- * the API, but are supported in the templates that can be created</li>
- * </ul></div></p>
- * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
- * <li>{@link Ext.DataView}</li>
- * </ul></div></p>
- *
- * The {@link Ext.Template} describes
- * the acceptable parameters to pass to the constructor. The following
- * examples demonstrate all of the supported features.</p>
- *
- * <div class="mdetail-params"><ul>
- *
- * <li><b><u>Sample Data</u></b>
- * <div class="sub-desc">
- * <p>This is the data object used for reference in each code example:</p>
- * <pre><code>
-var data = {
-name: 'Tommy Maintz',
-title: 'Lead Developer',
-company: 'Ext JS, Inc',
-email: 'tommy at extjs.com',
-address: '5 Cups Drive',
-city: 'Palo Alto',
-state: 'CA',
-zip: '44102',
-drinks: ['Coffee', 'Soda', 'Water'],
-kids: [{
- name: 'Joshua',
- age:3
- },{
- name: 'Matthew',
- age:2
- },{
- name: 'Solomon',
- age:0
-}]
-};
- </code></pre>
- * </div>
- * </li>
- *
- *
- * <li><b><u>Auto filling of arrays</u></b>
- * <div class="sub-desc">
- * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
- * to process the provided data object:
- * <ul>
- * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
- * repeating the template block inside the <tt>tpl</tt> tag for each item in the
- * array.</li>
- * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
- * <li>While processing an array, the special variable <tt>{#}</tt>
- * will provide the current array index + 1 (starts at 1, not 0).</li>
- * </ul>
- * </p>
- * <pre><code>
-<tpl <b>for</b>=".">...</tpl> // loop through array at root node
-<tpl <b>for</b>="foo">...</tpl> // loop through array at foo node
-<tpl <b>for</b>="foo.bar">...</tpl> // loop through array at foo.bar node
- </code></pre>
- * Using the sample data above:
- * <pre><code>
-var tpl = new Ext.XTemplate(
- '<p>Kids: ',
- '<tpl <b>for</b>=".">', // process the data.kids node
- '<p>{#}. {name}</p>', // use current array index to autonumber
- '</tpl></p>'
-);
-tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
- </code></pre>
- * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
- * to access specified members of the provided data object to populate the template:</p>
- * <pre><code>
-var tpl = new Ext.XTemplate(
- '<p>Name: {name}</p>',
- '<p>Title: {title}</p>',
- '<p>Company: {company}</p>',
- '<p>Kids: ',
- '<tpl <b>for="kids"</b>>', // interrogate the kids property within the data
- '<p>{name}</p>',
- '</tpl></p>'
-);
-tpl.overwrite(panel.body, data); // pass the root node of the data object
- </code></pre>
- * <p>Flat arrays that contain values (and not objects) can be auto-rendered
- * using the special <b><tt>{.}</tt></b> variable inside a loop. This variable
- * will represent the value of the array at the current index:</p>
- * <pre><code>
-var tpl = new Ext.XTemplate(
- '<p>{name}\'s favorite beverages:</p>',
- '<tpl for="drinks">',
- '<div> - {.}</div>',
- '</tpl>'
-);
-tpl.overwrite(panel.body, data);
- </code></pre>
- * <p>When processing a sub-template, for example while looping through a child array,
- * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
- * <pre><code>
-var tpl = new Ext.XTemplate(
- '<p>Name: {name}</p>',
- '<p>Kids: ',
- '<tpl for="kids">',
- '<tpl if="age &gt; 1">',
- '<p>{name}</p>',
- '<p>Dad: {<b>parent</b>.name}</p>',
- '</tpl>',
- '</tpl></p>'
-);
-tpl.overwrite(panel.body, data);
- </code></pre>
- * </div>
- * </li>
- *
- *
- * <li><b><u>Conditional processing with basic comparison operators</u></b>
- * <div class="sub-desc">
- * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
- * to provide conditional checks for deciding whether or not to render specific
- * parts of the template. Notes:<div class="sub-desc"><ul>
- * <li>Double quotes must be encoded if used within the conditional</li>
- * <li>There is no <tt>else</tt> operator — if needed, two opposite
- * <tt>if</tt> statements should be used.</li>
- * </ul></div>
- * <pre><code>
-<tpl if="age > 1 && age < 10">Child</tpl>
-<tpl if="age >= 10 && age < 18">Teenager</tpl>
-<tpl <b>if</b>="this.isGirl(name)">...</tpl>
-<tpl <b>if</b>="id==\'download\'">...</tpl>
-<tpl <b>if</b>="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
-// no good:
-<tpl if="name == "Tommy"">Hello</tpl>
-// encode " if it is part of the condition, e.g.
-<tpl if="name == &quot;Tommy&quot;">Hello</tpl>
- * </code></pre>
- * Using the sample data above:
- * <pre><code>
-var tpl = new Ext.XTemplate(
- '<p>Name: {name}</p>',
- '<p>Kids: ',
- '<tpl for="kids">',
- '<tpl if="age &gt; 1">',
- '<p>{name}</p>',
- '</tpl>',
- '</tpl></p>'
-);
-tpl.overwrite(panel.body, data);
- </code></pre>
- * </div>
- * </li>
- *
- *
- * <li><b><u>Basic math support</u></b>
- * <div class="sub-desc">
- * <p>The following basic math operators may be applied directly on numeric
- * data values:</p><pre>
- * + - * /
- * </pre>
- * For example:
- * <pre><code>
-var tpl = new Ext.XTemplate(
- '<p>Name: {name}</p>',
- '<p>Kids: ',
- '<tpl for="kids">',
- '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
- '<p>{#}: {name}</p>', // <-- Auto-number each item
- '<p>In 5 Years: {age+5}</p>', // <-- Basic math
- '<p>Dad: {parent.name}</p>',
- '</tpl>',
- '</tpl></p>'
-);
-tpl.overwrite(panel.body, data);
- </code></pre>
- * </div>
- * </li>
- *
- *
- * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
- * <div class="sub-desc">
- * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
- * in the scope of the template. There are some special variables available in that code:
- * <ul>
- * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
- * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
- * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
- * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
- * loop you are in (1-based).</li>
- * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
- * of the array you are looping.</li>
- * </ul>
- * This example demonstrates basic row striping using an inline code block and the
- * <tt>xindex</tt> variable:</p>
- * <pre><code>
-var tpl = new Ext.XTemplate(
- '<p>Name: {name}</p>',
- '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
- '<p>Kids: ',
- '<tpl for="kids">',
- '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
- '{name}',
- '</div>',
- '</tpl></p>'
- );
-tpl.overwrite(panel.body, data);
- </code></pre>
- * </div>
- * </li>
- *
- * <li><b><u>Template member functions</u></b>
- * <div class="sub-desc">
- * <p>One or more member functions can be specified in a configuration
- * object passed into the XTemplate constructor for more complex processing:</p>
- * <pre><code>
-var tpl = new Ext.XTemplate(
- '<p>Name: {name}</p>',
- '<p>Kids: ',
- '<tpl for="kids">',
- '<tpl if="this.isGirl(name)">',
- '<p>Girl: {name} - {age}</p>',
- '</tpl>',
- // use opposite if statement to simulate 'else' processing:
- '<tpl if="this.isGirl(name) == false">',
- '<p>Boy: {name} - {age}</p>',
- '</tpl>',
- '<tpl if="this.isBaby(age)">',
- '<p>{name} is a baby!</p>',
- '</tpl>',
- '</tpl></p>',
- {
- // XTemplate configuration:
- compiled: true,
- // member functions:
- isGirl: function(name){
- return name == 'Sara Grace';
- },
- isBaby: function(age){
- return age < 1;
- }
- }
-);
-tpl.overwrite(panel.body, data);
- </code></pre>
- * </div>
- * </li>
- *
- * </ul></div>
- *
- * @param {Mixed} config
- */
-
-Ext.XTemplate = Ext.extend(Ext.Template, {
- argsRe: /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
- nameRe: /^<tpl\b[^>]*?for="(.*?)"/,
- ifRe: /^<tpl\b[^>]*?if="(.*?)"/,
- execRe: /^<tpl\b[^>]*?exec="(.*?)"/,
- constructor: function() {
- Ext.XTemplate.superclass.constructor.apply(this, arguments);
-
- var me = this,
- html = me.html,
- argsRe = me.argsRe,
- nameRe = me.nameRe,
- ifRe = me.ifRe,
- execRe = me.execRe,
- id = 0,
- tpls = [],
- VALUES = 'values',
- PARENT = 'parent',
- XINDEX = 'xindex',
- XCOUNT = 'xcount',
- RETURN = 'return ',
- WITHVALUES = 'with(values){ ',
- m, matchName, matchIf, matchExec, exp, fn, exec, name, i;
-
- html = ['<tpl>', html, '</tpl>'].join('');
-
- while ((m = html.match(argsRe))) {
- exp = null;
- fn = null;
- exec = null;
- matchName = m[0].match(nameRe);
- matchIf = m[0].match(ifRe);
- matchExec = m[0].match(execRe);
-
- exp = matchIf ? matchIf[1] : null;
- if (exp) {
- fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + 'try{' + RETURN + Ext.util.Format.htmlDecode(exp) + ';}catch(e){return;}}');
- }
-
- exp = matchExec ? matchExec[1] : null;
- if (exp) {
- exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + Ext.util.Format.htmlDecode(exp) + ';}');
- }
-
- name = matchName ? matchName[1] : null;
- if (name) {
- if (name === '.') {
- name = VALUES;
- } else if (name === '..') {
- name = PARENT;
- }
- name = new Function(VALUES, PARENT, 'try{' + WITHVALUES + RETURN + name + ';}}catch(e){return;}');
- }
-
- tpls.push({
- id: id,
- target: name,
- exec: exec,
- test: fn,
- body: m[1] || ''
- });
-
- html = html.replace(m[0], '{xtpl' + id + '}');
- id = id + 1;
- }
-
- for (i = tpls.length - 1; i >= 0; --i) {
- me.compileTpl(tpls[i]);
- }
- me.master = tpls[tpls.length - 1];
- me.tpls = tpls;
- },
-
- // @private
- applySubTemplate: function(id, values, parent, xindex, xcount) {
- var me = this, t = me.tpls[id];
- return t.compiled.call(me, values, parent, xindex, xcount);
- },
- /**
- * @cfg {RegExp} codeRe The regular expression used to match code variables (default: matches <tt>{[expression]}</tt>).
- */
- codeRe: /\{\[((?:\\\]|.|\n)*?)\]\}/g,
-
- re: /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?\}/g,
-
- // @private
- compileTpl: function(tpl) {
- var fm = Ext.util.Format,
- me = this,
- useFormat = me.disableFormats !== true,
- body, bodyReturn, evaluatedFn;
-
- function fn(m, name, format, args, math) {
- var v;
- // name is what is inside the {}
- // Name begins with xtpl, use a Sub Template
- if (name.substr(0, 4) == 'xtpl') {
- return "',this.applySubTemplate(" + name.substr(4) + ", values, parent, xindex, xcount),'";
- }
- // name = "." - Just use the values object.
- if (name == '.') {
- v = 'typeof values == "string" ? values : ""';
- }
-
- // name = "#" - Use the xindex
- else if (name == '#') {
- v = 'xindex';
- }
- else if (name.substr(0, 7) == "parent.") {
- v = name;
- }
- // name has a . in it - Use object literal notation, starting from values
- else if (name.indexOf('.') != -1) {
- v = "values." + name;
- }
-
- // name is a property of values
- else {
- v = "values['" + name + "']";
- }
- if (math) {
- v = '(' + v + math + ')';
- }
- if (format && useFormat) {
- args = args ? ',' + args : "";
- if (format.substr(0, 5) != "this.") {
- format = "fm." + format + '(';
- }
- else {
- format = 'this.' + format.substr(5) + '(';
- }
- }
- else {
- args = '';
- format = "(" + v + " === undefined ? '' : ";
- }
- return "'," + format + v + args + "),'";
- }
-
- function codeFn(m, code) {
- // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
- return "',(" + code.replace(me.compileARe, "'") + "),'";
- }
-
- bodyReturn = tpl.body.replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn).replace(me.codeRe, codeFn);
- body = "evaluatedFn = function(values, parent, xindex, xcount){return ['" + bodyReturn + "'].join('');};";
- eval(body);
-
- tpl.compiled = function(values, parent, xindex, xcount) {
- var vs,
- length,
- buffer,
- i;
-
- if (tpl.test && !tpl.test.call(me, values, parent, xindex, xcount)) {
- return '';
- }
-
- vs = tpl.target ? tpl.target.call(me, values, parent) : values;
- if (!vs) {
- return '';
- }
-
- parent = tpl.target ? values : parent;
- if (tpl.target && Ext.isArray(vs)) {
- buffer = [], length = vs.length;
- if (tpl.exec) {
- for (i = 0; i < length; i++) {
- buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length);
- tpl.exec.call(me, vs[i], parent, i + 1, length);
- }
- } else {
- for (i = 0; i < length; i++) {
- buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length);
- }
- }
- return buffer.join('');
- }
-
- if (tpl.exec) {
- tpl.exec.call(me, vs, parent, xindex, xcount);
- }
- return evaluatedFn.call(me, vs, parent, xindex, xcount);
- }
-
- return this;
- },
-
- /**
- * Returns an HTML fragment of this template with the specified values applied.
- * @param {Object} 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
- */
- applyTemplate: function(values) {
- return this.master.compiled.call(this, values, {}, 1, 1);
- },
-
- /**
- * Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
- * @return {Function} The compiled function
- */
- compile: function() {
- return this;
- }
-});
-/**
- * Alias for {@link #applyTemplate}
- * 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
- * @member Ext.XTemplate
- * @method apply
- */
-Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
-
-/**
- * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
- * @param {String/HTMLElement} el A DOM element or its id
- * @return {Ext.Template} The created template
- * @static
- */
-Ext.XTemplate.from = function(el, config) {
- el = Ext.getDom(el);
- return new Ext.XTemplate(el.value || el.innerHTML, config || {});
-};
-
-
-/**
- * @class Ext.util.Sorter
- * @extends Object
- * Represents a single sorter that can be applied to a Store
- */
-Ext.util.Sorter = Ext.extend(Object, {
- /**
- * @cfg {String} property The property to sort by. Required unless {@link #sorter} is provided
- */
-
- /**
- * @cfg {Function} sorterFn A specific sorter function to execute. Can be passed instead of {@link #property}
- */
-
- /**
- * @cfg {String} root Optional root property. This is mostly useful when sorting a Store, in which case we set the
- * root to 'data' to make the filter pull the {@link #property} out of the data object of each item
- */
-
- /**
- * @cfg {String} direction The direction to sort by. Defaults to ASC
- */
- direction: "ASC",
-
- constructor: function(config) {
- Ext.apply(this, config);
-
- if (this.property == undefined && this.sorterFn == undefined) {
- throw "A Sorter requires either a property or a sorter function";
- }
-
- this.sort = this.createSortFunction(this.sorterFn || this.defaultSorterFn);
- },
-
- /**
- * @private
- * Creates and returns a function which sorts an array by the given property and direction
- * @return {Function} A function which sorts by the property/direction combination provided
- */
- createSortFunction: function(sorterFn) {
- var me = this,
- property = me.property,
- direction = me.direction,
- modifier = direction.toUpperCase() == "DESC" ? -1 : 1;
-
- //create a comparison function. Takes 2 objects, returns 1 if object 1 is greater,
- //-1 if object 2 is greater or 0 if they are equal
- return function(o1, o2) {
- return modifier * sorterFn.call(me, o1, o2);
- };
- },
-
- /**
- * @private
- * Basic default sorter function that just compares the defined property of each object
- */
- defaultSorterFn: function(o1, o2) {
- var v1 = this.getRoot(o1)[this.property],
- v2 = this.getRoot(o2)[this.property];
-
- return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
- },
-
- /**
- * @private
- * Returns the root property of the given item, based on the configured {@link #root} property
- * @param {Object} item The item
- * @return {Object} The root property of the object
- */
- getRoot: function(item) {
- return this.root == undefined ? item : item[this.root];
- }
-});
-/**
- * @class Ext.util.Filter
- * @extends Object
- * <p>Represents a filter that can be applied to a {@link Ext.data.MixedCollection MixedCollection}. Can either simply
- * filter on a property/value pair or pass in a filter function with custom logic. Filters are always used in the context
- * of MixedCollections, though {@link Ext.data.Store Store}s frequently create them when filtering and searching on their
- * records. Example usage:</p>
-<pre><code>
-//set up a fictional MixedCollection containing a few people to filter on
-var allNames = new Ext.util.MixedCollection();
-allNames.addAll([
- {id: 1, name: 'Ed', age: 25},
- {id: 2, name: 'Jamie', age: 37},
- {id: 3, name: 'Abe', age: 32},
- {id: 4, name: 'Aaron', age: 26},
- {id: 5, name: 'David', age: 32}
-]);
-
-var ageFilter = new Ext.util.Filter({
- property: 'age',
- value : 32
-});
-
-var longNameFilter = new Ext.util.Filter({
- filterFn: function(item) {
- return item.name.length > 4;
- }
-});
-
-//a new MixedCollection with the 3 names longer than 4 characters
-var longNames = allNames.filter(longNameFilter);
-
-//a new MixedCollection with the 2 people of age 24:
-var youngFolk = allNames.filter(ageFilter);
-</code></pre>
- * @constructor
- * @param {Object} config Config object
- */
-Ext.util.Filter = Ext.extend(Object, {
- /**
- * @cfg {String} property The property to filter on. Required unless a {@link #filter} is passed
- */
-
- /**
- * @cfg {Function} filterFn A custom filter function which is passed each item in the {@link Ext.util.MixedCollection}
- * in turn. Should return true to accept each item or false to reject it
- */
-
- /**
- * @cfg {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
- */
- anyMatch: false,
-
- /**
- * @cfg {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
- * Ignored if anyMatch is true.
- */
- exactMatch: false,
-
- /**
- * @cfg {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
- */
- caseSensitive: false,
-
- /**
- * @cfg {String} root Optional root property. This is mostly useful when filtering a Store, in which case we set the
- * root to 'data' to make the filter pull the {@link #property} out of the data object of each item
- */
-
- constructor: function(config) {
- Ext.apply(this, config);
-
- //we're aliasing filter to filterFn mostly for API cleanliness reasons, despite the fact it dirties the code here.
- //Ext.util.Sorter takes a sorterFn property but allows .sort to be called - we do the same here
- this.filter = this.filter || this.filterFn;
-
- if (this.filter == undefined) {
- if (this.property == undefined || this.value == undefined) {
- // Commented this out temporarily because it stops us using string ids in models. TODO: Remove this once
- // Model has been updated to allow string ids
-
- // throw "A Filter requires either a property or a filterFn to be set";
- } else {
- this.filter = this.createFilterFn();
- }
-
- this.filterFn = this.filter;
- }
- },
-
- /**
- * @private
- * Creates a filter function for the configured property/value/anyMatch/caseSensitive options for this Filter
- */
- createFilterFn: function() {
- var me = this,
- matcher = me.createValueMatcher(),
- property = me.property;
-
- return function(item) {
- return matcher.test(me.getRoot.call(me, item)[property]);
- };
- },
-
- /**
- * @private
- * Returns the root property of the given item, based on the configured {@link #root} property
- * @param {Object} item The item
- * @return {Object} The root property of the object
- */
- getRoot: function(item) {
- return this.root == undefined ? item : item[this.root];
- },
-
- /**
- * @private
- * Returns a regular expression based on the given value and matching options
- */
- createValueMatcher : function() {
- var me = this,
- value = me.value,
- anyMatch = me.anyMatch,
- exactMatch = me.exactMatch,
- caseSensitive = me.caseSensitive,
- escapeRe = Ext.util.Format.escapeRegex;
-
- if (!value.exec) { // not a regex
- value = String(value);
-
- if (anyMatch === true) {
- value = escapeRe(value);
- } else {
- value = '^' + escapeRe(value);
- if (exactMatch === true) {
- value += '$';
- }
- }
- value = new RegExp(value, caseSensitive ? '' : 'i');
- }
-
- return value;
- }
-});
-/**
- * @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:
- * <pre><code>
-var sayHi = function(name){
- alert('Hi, ' + name);
-}
-
-sayHi('Fred'); // alerts "Hi, Fred"
-
-// create a new function that validates input without
-// directly modifying the original function:
-var sayHiToFriend = Ext.createInterceptor(sayHi, function(name){
- return name == 'Brian';
-});
-
-sayHiToFriend('Fred'); // no alert
-sayHiToFriend('Brian'); // alerts "Hi, Brian"
- </code></pre>
- * @param {Function} origFn The original function.
- * @param {Function} newFn The function to call before the original
- * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
- * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
- * @param {Mixed} returnValue (optional) The value to return if the passed function return false (defaults to null).
- * @return {Function} The new function
- */
- createInterceptor: function(origFn, newFn, scope, returnValue) {
- 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) :
- returnValue || null;
- };
- }
- },
-
- /**
- * Creates a delegate (callback) that sets the scope to obj.
- * Call directly on any function. Example: <code>Ext.createDelegate(this.myFunction, this, [arg1, arg2])</code>
- * Will create a function that is automatically scoped to obj so that the <tt>this</tt> variable inside the
- * callback points to obj. Example usage:
- * <pre><code>
-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.');
-}
-
-var btn = new Ext.Button({
- text: 'Say Hi',
- renderTo: Ext.getBody()
-});
-
-// 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']));
- </code></pre>
- * @param {Function} fn The function to delegate.
- * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
- * <b>If omitted, defaults to the browser window.</b>
- * @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);
- };
- },
-
- /**
- * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
- * <pre><code>
-var sayHi = function(name){
- alert('Hi, ' + name);
-}
-
-// 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);
- </code></pre>
- * @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 (<code><b>this</b></code> reference) in which the function is executed.
- * <b>If omitted, defaults to the browser window.</b>
- * @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;
- },
-
-
- /**
- * 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 = Ext.createSequence(sayHi, function(name){
- alert('Bye, ' + name);
-});
-
-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
- */
- 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;
- };
- }
- }
-};
-
-/**
- * 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 (<code><b>this</b></code> reference) in which the function is executed.
- * <b>If omitted, defaults to the browser window.</b>
- * @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
- */
-
-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 (<code><b>this</b></code> reference) in which the passed function is executed.
- * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
- * @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 (<code><b>this</b></code> reference) in which the function is executed.
- * <b>If omitted, defaults to the browser window.</b>
- * @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.Date
- * @singleton
- */
-
-Ext.util.Date = {
- /**
- * Returns the number of milliseconds between two dates
- * @param {Date} dateA
- * @param {Date} dateB (optional) Defaults to now
- * @return {Number} The diff in milliseconds
- */
- getElapsed: function(dateA, dateB) {
- return Math.abs(dateA - (dateB || new Date));
- }
-};
-/**
- * @class Ext.util.Numbers
- * @singleton
- */
-
-Ext.util.Numbers = {
-
- // detect toFixed implementation bug in IE
- toFixedBroken: (0.9).toFixed() != 1,
-
- /**
- * 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} number The number to check
- * @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(number, min, max) {
- number = parseFloat(number);
- if (!isNaN(min)) {
- number = Math.max(number, min);
- }
- if (!isNaN(max)) {
- number = Math.min(number, max);
- }
- return number;
- },
-
- /**
- * Formats a number using fixed-point notation
- * @param {Number} value The number to format
- * @param {Number} precision The number of digits to show after the decimal point
- */
- toFixed : function(value, precision) {
- if(Ext.util.Numbers.toFixedBroken) {
- precision = precision || 0;
- var pow = Math.pow(10, precision);
- return Math.round(value * pow) / pow;
- }
- return value.toFixed(precision);
- }
-};
-
-/**
- * @class Ext.util.Format
- * Reusable data formatting functions
- * @singleton
- */
-Ext.util.Format = {
- defaultDateFormat: 'm/d/Y',
- escapeRe: /('|\\)/g,
- trimRe: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
- formatRe: /\{(\d+)\}/g,
- escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
-
- /**
- * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
- * @param {String} value The string to truncate
- * @param {Number} length The maximum length to allow before truncating
- * @param {Boolean} word True to try to find a common word 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),
- index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
- if (index != -1 && index >= (len - 15)) {
- return vs.substr(0, index) + "...";
- }
- }
- return value.substr(0, len - 3) + "...";
- }
- return value;
- },
-
- /**
- * Escapes the passed string for use in a regular expression
- * @param {String} str
- * @return {String}
- */
- escapeRegex : function(s) {
- return s.replace(Ext.util.Format.escapeRegexRe, "\\$1");
- },
-
- /**
- * 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(Ext.util.Format.escapeRe, "\\$1");
- },
-
- /**
- * 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.
- * <pre><code>
- // alternate sort directions
- sort = Ext.util.Format.toggle(sort, 'ASC', 'DESC');
-
- // instead of conditional logic:
- sort = (sort == 'ASC' ? 'DESC' : 'ASC');
- </code></pre>
- * @param {String} string The current string
- * @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
- */
- toggle : function(string, value, other) {
- return string == value ? other : value;
- },
-
- /**
- * Trims whitespace from either end of a string, leaving spaces within the string intact. Example:
- * <pre><code>
- var s = ' foo bar ';
- alert('-' + s + '-'); //alerts "- foo bar -"
- alert('-' + Ext.util.Format.trim(s) + '-'); //alerts "-foo bar-"
- </code></pre>
- * @param {String} string The string to escape
- * @return {String} The trimmed string
- */
- trim : function(string) {
- return string.replace(Ext.util.Format.trimRe, "");
- },
-
- /**
- * Pads the left side of a string with a specified character. This is especially useful
- * for normalizing number and date strings. Example usage:
- *
- * <pre><code>
-var s = Ext.util.Format.leftPad('123', 5, '0');
-// s now contains the string: '00123'
- </code></pre>
- * @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);
- ch = ch || " ";
- while (result.length < size) {
- result = ch + result;
- }
- return result;
- },
-
- /**
- * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
- * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
- * <pre><code>
-var cls = 'my-class', text = 'Some text';
-var s = Ext.util.Format.format('<div class="{0}">{1}</div>', cls, text);
-// s now contains the string: '<div class="my-class">Some text</div>'
- </code></pre>
- * @param {String} string The tokenized string to be formatted
- * @param {String} value1 The value to replace token {0}
- * @param {String} value2 Etc...
- * @return {String} The formatted string
- * @static
- */
- format : function (format) {
- var args = Ext.toArray(arguments, 1);
- return format.replace(Ext.util.Format.formatRe, function(m, i) {
- return args[i];
- });
- },
-
- /**
- * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
- * @param {String} value The string to encode
- * @return {String} The encoded text
- */
- htmlEncode: function(value) {
- return ! value ? value: String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
- },
-
- /**
- * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
- * @param {String} value The string to decode
- * @return {String} The decoded text
- */
- htmlDecode: function(value) {
- return ! value ? value: String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
- },
-
- /**
- * Parse a value into a formatted date using the specified format pattern.
- * @param {String/Date} value The value to format (Strings must conform to the format expected by the javascript
- * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method)
- * @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) {
- return "";
- }
- if (!Ext.isDate(v)) {
- v = new Date(Date.parse(v));
- }
- return v.dateFormat(format || Ext.util.Format.defaultDateFormat);
- }
-};
-
-/**
- * @class Ext.LoadMask
- * A simple utility class for generically masking elements while loading data. If the {@link #store}
- * config option is specified, the masking will be automatically synchronized with the store's loading
- * process and the mask element will be cached for reuse.
- * <p>Example usage:</p>
- *<pre><code>
-// Basic mask:
-var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
-myMask.show();
-</code></pre>
- * @constructor
- * Create a new LoadMask
- * @param {Mixed} el The element or DOM node, or its id
- * @param {Object} config The config object
- */
-Ext.LoadMask = Ext.extend(Ext.util.Observable, {
- /**
- * @cfg {Ext.data.Store} store
- * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
- * hidden on either load sucess, or load fail.
- */
-
- /**
- * @cfg {String} msg
- * The text to display in a centered loading message box (defaults to 'Loading...')
- */
- msg : 'Loading...',
- /**
- * @cfg {String} msgCls
- * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
- */
- msgCls : 'x-mask-loading',
-
- /**
- * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
- * @type Boolean
- */
- disabled: false,
-
- constructor : function(el, config) {
- this.el = Ext.get(el);
- Ext.apply(this, config);
-
- this.addEvents('show', 'hide');
- if (this.store) {
- this.bindStore(this.store, true);
- }
- Ext.LoadMask.superclass.constructor.call(this);
- },
-
- /**
- * Changes the data store bound to this LoadMask.
- * @param {Store} store The store to bind to this LoadMask
- */
- bindStore : function(store, initial) {
- if (!initial && this.store) {
- this.mun(this.store, {
- scope: this,
- beforeload: this.onBeforeLoad,
- load: this.onLoad,
- exception: this.onLoad
- });
- if(!store) {
- this.store = null;
- }
- }
- if (store) {
- store = Ext.StoreMgr.lookup(store);
- this.mon(store, {
- scope: this,
- beforeload: this.onBeforeLoad,
- load: this.onLoad,
- exception: this.onLoad
- });
-
- }
- this.store = store;
- if (store && store.isLoading()) {
- this.onBeforeLoad();
- }
- },
-
- /**
- * Disables the mask to prevent it from being displayed
- */
- disable : function() {
- this.disabled = true;
- },
-
- /**
- * Enables the mask so that it can be displayed
- */
- enable : function() {
- this.disabled = false;
- },
-
- /**
- * Method to determine whether this LoadMask is currently disabled.
- * @return {Boolean} the disabled state of this LoadMask.
- */
- isDisabled : function() {
- return this.disabled;
- },
-
- // private
- onLoad : function() {
- this.el.unmask();
- this.fireEvent('hide', this, this.el, this.store);
- },
-
- // private
- onBeforeLoad : function() {
- if (!this.disabled) {
- this.el.mask('<div class="x-loading-spinner"><span class="x-loading-top"></span><span class="x-loading-right"></span><span class="x-loading-bottom"></span><span class="x-loading-left"></span></div><div class="x-loading-msg">' + this.msg + '</div>', this.msgCls, false);
- this.fireEvent('show', this, this.el, this.store);
- }
- },
-
- /**
- * Show this LoadMask over the configured Element.
- */
- show: function() {
- this.onBeforeLoad();
- },
-
- /**
- * Hide this LoadMask.
- */
- hide: function() {
- this.onLoad();
- },
-
- // private
- destroy : function() {
- this.hide();
- this.clearListeners();
- }
-});
-
-/**
- * @class Array
- */
-Ext.applyIf(Array.prototype, {
- /**
- * Checks whether or not the specified object exists in the array.
- * @param {Object} o The object to check for
- * @param {Number} from (Optional) The index at which to begin the search
- * @return {Number} The index of o in the array (or -1 if it is not found)
- */
- indexOf: function(o, from) {
- var len = this.length;
- from = from || 0;
- from += (from < 0) ? len: 0;
- for (; from < len; ++from) {
- if (this[from] === o) {
- return from;
- }
- }
- return - 1;
- },
-
- /**
- * Removes the specified object from the array. If the object is not found nothing happens.
- * @param {Object} o The object to remove
- * @return {Array} this array
- */
- remove: function(o) {
- var index = this.indexOf(o);
- if (index != -1) {
- this.splice(index, 1);
- }
- return this;
- },
-
- contains: function(o) {
- return this.indexOf(o) !== -1;
- }
-});
-
-/**
- * @class Ext.ComponentMgr
- * @extends Ext.AbstractManager
- * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
- * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
- * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
- * <p>This object also provides a registry of available Component <i>classes</i>
- * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
- * The <code>{@link Ext.Component#xtype xtype}</code> provides a way to avoid instantiating child Components
- * when creating a full, nested config object for a complete Ext page.</p>
- * <p>A child Component may be specified simply as a <i>config object</i>
- * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
- * needs rendering, the correct type can be looked up for lazy instantiation.</p>
- * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
- * @singleton
- */
-Ext.ComponentMgr = new Ext.AbstractManager({
- typeName: 'xtype',
-
- /**
- * Creates a new Component from the specified config object using the
- * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
- * @param {Object} config A configuration object for the Component you wish to create.
- * @param {Constructor} defaultType The constructor to provide the default Component type if
- * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
- * @return {Ext.Component} The newly instantiated Component.
- */
- create : function(config, defaultType){
- if (config.isComponent) {
- return config;
- } else {
- var type = config.xtype || defaultType,
- Class = this.types[type];
- if (!Class) {
- throw "Attempting to create a component with an xtype that has not been registered: " + type
- }
- return new Class(config);
- }
- return config.render ? config : new (config);
- },
-
- registerType : function(type, cls) {
- this.types[type] = cls;
- cls[this.typeName] = type;
- cls.prototype[this.typeName] = type;
- }
-});
-
-/**
- * Shorthand for {@link Ext.ComponentMgr#registerType}
- * @param {String} xtype The {@link Ext.component#xtype mnemonic string} by which the Component class
- * may be looked up.
- * @param {Constructor} cls The new Component class.
- * @member Ext
- * @method reg
- */
-Ext.reg = function() {
- return Ext.ComponentMgr.registerType.apply(Ext.ComponentMgr, arguments);
-}; // this will be called a lot internally, shorthand to keep the bytes down
-
-/**
- * Shorthand for {@link Ext.ComponentMgr#create}
- * Creates a new Component from the specified config object using the
- * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
- * @param {Object} config A configuration object for the Component you wish to create.
- * @param {Constructor} defaultType The constructor to provide the default Component type if
- * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
- * @return {Ext.Component} The newly instantiated Component.
- * @member Ext
- * @method create
- */
-Ext.create = function() {
- return Ext.ComponentMgr.create.apply(Ext.ComponentMgr, arguments);
-};
-
-/**
- * @class Ext.ComponentQuery
- * @extends Object
- *
- * Provides searching of Components within Ext.ComponentMgr (globally) or a specific
- * Ext.Container on the document with a similar syntax to a CSS selector.
- *
- * Xtypes can be retrieved by their name with an optional . prefix
-<ul>
- <li>component or .component</li>
- <li>gridpanel or .gridpanel</li>
-</ul>
- *
- * An itemId or id must be prefixed with a #.
-<ul>
- <li>#myContainer</li>
-</ul>
- *
- *
- * Attributes must be wrapped in brackets
-<ul>
- <li>component[autoScroll]</li>
- <li>panel[title="Test"]</li>
-</ul>
- *
- * Member expressions from candidate Components may be tested. If the expression returns a <i>truthy</i> value,
- * the candidate Component will be included in the query:<pre><code>
-var disabledFields = myFormPanel.query("{isDisabled()}");
-</code></pre>
- *
- * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}:<code><pre>
-// Function receives array and returns a filtered array.
-Ext.ComponentQuery.pseudos.invalid = function(items) {
- var i = 0, l = items.length, c, result = [];
- for (; i < l; i++) {
- if (!(c = items[i]).isValid()) {
- result.push(c);
- }
- }
- return result;
-};
-
-var invalidFields = myFormPanel.query('field:invalid');
-if (invalidFields.length) {
- invalidFields[0].getEl().scrollIntoView(myFormPanel.body);
- for (var i = 0, l = invalidFields.length; i < l; i++) {
- invalidFields[i].getEl().frame("red");
- }
-}
-</pre></code>
- *
- * Queries return an array of components.
- * Here are some example queries.
-<pre><code>
- // retrieve all Ext.Panel's on the document by xtype
- var panelsArray = Ext.ComponentQuery.query('.panel');
-
- // retrieve all Ext.Panels within the container with an id myCt
- var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt .panel');
-
- // retrieve all direct children which are Ext.Panels within myCt
- var directChildPanel = Ext.ComponentQuery.query('#myCt > .panel');
-
- // retrieve all gridpanels and listviews
- var gridsAndLists = Ext.ComponentQuery.query('gridpanel, listview');
-</code></pre>
- * @singleton
- */
-Ext.ComponentQuery = new function() {
- var cq = this,
-
- // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied
- // as a member on each item in the passed array.
- filterFnPattern = [
- 'var r = [],',
- 'i = 0,',
- 'it = arguments[0],',
- 'l = it.length,',
- 'c;',
- 'for (; i < l; i++) {',
- 'c = it[i].{0};',
- 'if (c) {',
- 'r.push(c);',
- '}',
- '}',
- 'return r;'
- ].join(''),
-
- filterItems = function(items, operation) {
- // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...]
- // The operation's method loops over each item in the candidate array and
- // returns an array of items which match its criteria
- return operation.method.apply(this, [ items ].concat(operation.args));
- },
-
- getItems = function(items, mode) {
- var result = [],
- i,
- ln = items.length,
- candidate,
- deep = mode != '>';
- for (i = 0; i < ln; i++) {
- candidate = items[i];
- if (candidate.getRefItems) {
- result = result.concat(candidate.getRefItems(deep));
- }
- }
- return result;
- },
-
- getAncestors = function(items) {
- var result = [],
- i,
- ln = items.length,
- candidate;
- for (i = 0; i < ln; i++) {
- candidate = items[i];
- while (!!(candidate = candidate.ownerCt)) {
- result.push(candidate);
- }
- }
- return result;
- },
-
- // Filters the passed candidate array and returns only items which match the passed xtype
- filterByXType = function(items, xtype, shallow) {
- if (xtype == '*') {
- return items.slice();
- }
- else {
- var result = [],
- i,
- ln = items.length,
- candidate;
- for (i = 0; i < ln; i++) {
- candidate = items[i];
- if (candidate.isXType(xtype, shallow)) {
- result.push(candidate);
- }
- }
- return result;
- }
- },
-
- // Filters the passed candidate array and returns only items which have the passed className
- filterByClassName = function(items, className) {
- var result = [],
- i,
- ln = items.length,
- candidate;
- for (i = 0; i < ln; i++) {
- candidate = items[i];
- if (candidate.el ? candidate.el.hasCls(className) : candidate.initCls().contains(className)) {
- result.push(candidate);
- }
- }
- return result;
- },
-
- // Filters the passed candidate array and returns only items which have the specified property match
- filterByAttribute = function(items, property, operator, value) {
- var result = [],
- i,
- ln = items.length,
- candidate;
- for (i = 0; i < ln; i++) {
- candidate = items[i];
- if ((value === undefined) ? !!candidate[property] : (candidate[property] == value)) {
- result.push(candidate);
- }
- }
- return result;
- },
-
- // Filters the passed candidate array and returns only items which have the specified itemId or id
- filterById = function(items, id) {
- var result = [],
- i,
- ln = items.length,
- candidate;
- for (i = 0; i < ln; i++) {
- candidate = items[i];
- if (candidate.getItemId() == id) {
- result.push(candidate);
- }
- }
- return result;
- },
-
- // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in
- filterByPseudo = function(items, name, value) {
- return cq.pseudos[name](items, value);
- },
-
- // Determines leading mode
- // > for direct child, and ^ to switch to ownerCt axis
- modeRe = /^(\s?([>\^])\s?|\s|$)/,
-
- // Matches a token with possibly (true|false) appended for the "shallow" parameter
- // or {memberExpression}
- tokenRe = /^(?:(#)?([\w-]+|\*)(?:\((true|false)\))?)|(?:\{([^\}]+)\})/,
-
- matchers = [{
- // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter
- re: /^\.([\w-]+)(?:\((true|false)\))?/,
- method: filterByXType
- },{
- // checks for [attribute=value]
- re: /^(?:[\[\{](?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
- method: filterByAttribute
- }, {
- // checks for #cmpItemId
- re: /^#([\w-]+)/,
- method: filterById
- }, {
- re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
- method: filterByPseudo
- }];
-
- /**
- * @class Ext.ComponentQuery.Query
- * @extends Object
- * @private
- */
- cq.Query = Ext.extend(Object, {
- constructor: function(cfg) {
- cfg = cfg || {};
- Ext.apply(this, cfg);
- },
-
- execute : function(root) {
- var operations = this.operations,
- ln = operations.length,
- operation, i,
- workingItems;
-
- // no root, use all Components in the document
- if (!root) {
- workingItems = Ext.ComponentMgr.all.items.slice();
- }
-
- // We are going to loop over our operations and take care of them
- // one by one.
- for (i = 0; i < ln; i++) {
- operation = operations[i];
-
- // The mode operation requires some custom handling.
- // All other operations essentially filter down our current
- // working items, while mode replaces our current working
- // items by getting children from each one of our current
- // working items. The type of mode determines the type of
- // children we get. (e.g. > only gets direct children)
- if (operation.mode == '^') {
- workingItems = getAncestors(workingItems || [root]);
- }
- else if (operation.mode) {
- workingItems = getItems(workingItems || [root], operation.mode);
- }
- else {
- workingItems = filterItems(workingItems || getItems([root]), operation);
- }
-
- // If this is the last operation, it means our current working
- // items are the final matched items. Thus return them!
- if (i == ln -1) {
- return workingItems;
- }
- }
- return [];
- },
-
- is: function(component) {
- var operations = this.operations,
- ln = operations.length,
- i,
- workingItems = Ext.isArray(component) ? component : [ component ];
-
- // Filter the Component array by each operation in turn.
- // Quit immediately if the results are ever filtered to zero length
- for (i = 0; i < ln && workingItems.length; i++) {
- workingItems = filterItems(workingItems, operations[i]);
- }
- return workingItems.length != 0;
- }
- });
-
- Ext.apply(this, {
-
- // private cache of selectors and matching ComponentQuery.Query objects
- cache: {},
-
- // private cache of pseudo class filter functions
- pseudos: {},
-
- /**
- * <p>Returns an array of matched Components from within the passed root Container.</p>
- * <p>This method filters returned Components in a similar way to how CSS selector based DOM
- * queries work using a textual selector string.</p>
- * <p>See class summary for details.</p>
- * @param selector The selector string to filter returned Components
- * @param root The Container within which to perform the query. If omitted, all Components
- * within the document are included in the search.
- * @returns {Array} The matched Components.
- */
- query: function(selector, root) {
- var selectors = selector.split(','),
- ln = selectors.length,
- i, query, results = [],
- noDupResults = [], dupMatcher = {}, resultsLn, cmp;
-
- for (i = 0; i < ln; i++) {
- selector = Ext.util.Format.trim(selectors[i]);
- query = this.cache[selector];
- if (!query) {
- this.cache[selector] = query = this.parse(selector);
- }
- results = results.concat(query.execute(root));
- }
-
- // multiple selectors, potential to find duplicates
- // lets filter them out.
- if (ln > 1) {
- resultsLn = results.length;
- for (i = 0; i < resultsLn; i++) {
- cmp = results[i];
- if (!dupMatcher[cmp.id]) {
- noDupResults.push(cmp);
- dupMatcher[cmp.id] = true;
- }
- }
- results = noDupResults;
- }
- return results;
- },
-
- /**
- * Tests whether the passed Component matches the selector string.
- * @param component The Components to test
- * @param selector The selector string to test against.
- * @returns {Boolean} True if the Component matches the selector.
- */
- is: function(component, selector) {
- if (!selector) {
- return true;
- }
- var query = this.cache[selector];
- if (!query) {
- this.cache[selector] = query = this.parse(selector);
- }
- return query.is(component);
- },
-
- parse: function(selector) {
- var operations = [],
- ln = matchers.length,
- lastSelector,
- tokenMatch,
- matchedChar,
- modeMatch,
- selectorMatch,
- args,
- i, matcher;
-
- // We are going to parse the beginning of the selector over and
- // over again, slicing off the selector any portions we converted into an
- // operation, until it is an empty string.
- while (selector && lastSelector != selector) {
- lastSelector = selector;
-
- // First we check if we are dealing with a token like #, * or an xtype
- tokenMatch = selector.match(tokenRe);
-
- if (tokenMatch) {
- matchedChar = tokenMatch[1];
-
- // If the token is prefixed with a # we push a filterById operation to our stack
- if (matchedChar == '#') {
- operations.push({
- method: filterById,
- args: [Ext.util.Format.trim(tokenMatch[2])]
- });
- }
- // If the token is prefixed with a . we push a filterByClassName operation to our stack
- // Not enabled yet. just needs \. adding to the tokenRe prefix
- else if (matchedChar == '.') {
- operations.push({
- method: filterByClassName,
- args: [Ext.util.Format.trim(tokenMatch[2])]
- });
- }
- // If the token is an expression, eg {isHidden()} we push a generated function operation to our stack
- else if (tokenMatch[4]) {
- operations.push({
- method: new Function(Ext.util.Format.format(filterFnPattern, tokenMatch[4])),
- args: []
- });
- }
- // If the token is a * or an xtype string, we push a filterByXType
- // operation to the stack.
- else {
- operations.push({
- method: filterByXType,
- args: [Ext.util.Format.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
- });
- }
-
- // Now we slice of the part we just converted into an operation
- selector = selector.replace(tokenMatch[0], '');
- }
-
- // If the next part of the query is not a space or > or ^, it means we
- // are going to check for more things that our current selection
- // has to comply to.
- while (!(modeMatch = selector.match(modeRe))) {
- // Lets loop over each type of matcher and execute it
- // on our current selector.
- for (i = 0; selector && i < ln; i++) {
- matcher = matchers[i];
- selectorMatch = selector.match(matcher.re);
-
- // If we have a match, add an operation with the method
- // associated with this matcher, and pass the regular
- // expression matches are arguments to the operation.
- if (selectorMatch) {
- operations.push({
- method: matcher.method,
- args: selectorMatch.splice(1)
- });
- selector = selector.replace(selectorMatch[0], '');
- break; // Break on match
- }
- // Exhausted all matches: It's an error
- if (i == (ln - 1)) {
- throw "Invalid ComponentQuery selector: \"" + arguments[0] + "\"";
- }
- }
- }
-
- // Now we are going to check for a mode change. This means a space
- // or a > to determine if we are going to select all the children
- // of the currently matched items, or a ^ if we are going to use the
- // ownerCt axis as the candidate source.
- if (modeMatch[1]) { // Assignment, and test for truthiness!
- operations.push({
- mode: modeMatch[2]||modeMatch[1]
- });
- selector = selector.replace(modeMatch[0], '');
- }
- }
-
- // Now that we have all our operations in an array, we are going
- // to create a new Query using these operations.
- return new cq.Query({
- operations: operations
- });
- }
- });
-};
-/**
- * @class Ext.PluginMgr
- * @extends Ext.AbstractManager
- * <p>Provides a registry of available Plugin <i>classes</i> indexed by a mnemonic code known as the Plugin's ptype.
- * The <code>{@link Ext.Component#xtype xtype}</code> provides a way to avoid instantiating child Components
- * when creating a full, nested config object for a complete Ext page.</p>
- * <p>A child Component may be specified simply as a <i>config object</i>
- * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
- * needs rendering, the correct type can be looked up for lazy instantiation.</p>
- * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
- * @singleton
- */
-Ext.PluginMgr = new Ext.AbstractManager({
- typeName: 'ptype',
-
- /**
- * Creates a new Plugin from the specified config object using the
- * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate.
- * @param {Object} config A configuration object for the Plugin you wish to create.
- * @param {Constructor} defaultType The constructor to provide the default Plugin type if
- * the config object does not contain a <code>ptype</code>. (Optional if the config contains a <code>ptype</code>).
- * @return {Ext.Component} The newly instantiated Plugin.
- */
- create : function(config, defaultType){
- var PluginCls = this.types[config.ptype || defaultType];
- if (PluginCls.init) {
- return PluginCls;
- } else {
- return new PluginCls(config);
- }
- },
-
- /**
- * Returns all plugins registered with the given type. Here, 'type' refers to the type of plugin, not its ptype.
- * @param {String} type The type to search for
- * @param {Boolean} defaultsOnly True to only return plugins of this type where the plugin's isDefault property is truthy
- * @return {Array} All matching plugins
- */
- findByType: function(type, defaultsOnly) {
- var matches = [],
- types = this.types;
-
- for (var name in types) {
- if (!types.hasOwnProperty(name)) {
- continue;
- }
- var item = types[name];
-
- if (item.type == type && (!defaultsOnly || (defaultsOnly === true && item.isDefault))) {
- matches.push(item);
- }
- }
-
- return matches;
- }
-});
-
-/**
- * Shorthand for {@link Ext.PluginMgr#registerType}
- * @param {String} ptype The {@link Ext.component#ptype mnemonic string} by which the Plugin class
- * may be looked up.
- * @param {Constructor} cls The new Plugin class.
- * @member Ext
- * @method preg
- */
-Ext.preg = function() {
- return Ext.PluginMgr.registerType.apply(Ext.PluginMgr, arguments);
-};
-
-/**
- * @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 = {
- optionsRe: /^(?:capture|scope|delay|buffer|single|stopEvent|disableLocking|preventDefault|stopPropagation|normalized|args|delegate|horizontal|vertical|dragThreshold|holdThreshold|doubleTapThreshold|cancelThreshold|singleTapThreshold|fireClickEvent)$/,
- touchRe: /^(?:pinch|pinchstart|pinchend|tap|singletap|doubletap|swipe|swipeleft|swiperight|drag|dragstart|dragend|touchdown|touchstart|touchmove|touchend|taphold|tapstart|tapcancel)$/i,
-
- /**
- * 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:<ul>
- * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
- * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
- * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
- * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
- * </ul>
- * @param {Object} scope (optional) The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.
- * @param {Object} options (optional) An object containing handler configuration properties.
- * This may contain any of the following properties:<ul>
- * <li>scope : Object<div class="sub-desc">The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.</div></li>
- * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
- * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
- * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
- * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
- * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
- * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
- * <li>single : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
- * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
- * by the specified number of milliseconds. If the event fires again within that time, the original
- * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
- * <li>target : Element<div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
- * </ul><br>
- * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
- */
- addListener : function(element, eventName, fn, scope, o){
- // handle our listener config object syntax
- if (Ext.isObject(eventName)) {
- this.handleListenerConfig(element, eventName);
- return;
- }
-
- var dom = Ext.getDom(element);
-
- // if the element doesnt exist throw an error
- if (!dom){
- throw "Error listening for \"" + eventName + '\". Element "' + element + '" doesn\'t exist.';
- }
-
- if (!fn) {
- throw 'Error listening for "' + eventName + '". No handler function specified';
- }
-
- var touch = this.touchRe.test(eventName);
-
- // create the wrapper function
- var wrap = this.createListenerWrap(dom, eventName, fn, scope, o, touch);
-
- // add all required data into the event cache
- this.getEventListenerCache(dom, eventName).push({
- fn: fn,
- wrap: wrap,
- scope: scope
- });
-
- if (touch) {
- Ext.gesture.Manager.addEventListener(dom, eventName, wrap, o);
- }
- else {
- // now add the event listener to the actual element!
- o = o || {};
- dom.addEventListener(eventName, wrap, o.capture || false);
- }
- },
-
- /**
- * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
- * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
- * @param {String/HTMLElement} el The id or html element from which to remove the listener.
- * @param {String} eventName The name of the event.
- * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
- * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
- * then this must refer to the same object.
- */
- removeListener : function(element, eventName, fn, scope) {
- // handle our listener config object syntax
- if (Ext.isObject(eventName)) {
- this.handleListenerConfig(element, eventName, true);
- return;
- }
-
- var dom = Ext.getDom(element),
- cache = this.getEventListenerCache(dom, eventName),
- i = cache.length, j,
- listener, wrap, tasks;
-
- while (i--) {
- listener = cache[i];
-
- if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
- wrap = listener.wrap;
-
- // clear buffered calls
- if (wrap.task) {
- clearTimeout(wrap.task);
- delete wrap.task;
- }
-
- // clear delayed calls
- j = wrap.tasks && wrap.tasks.length;
- if (j) {
- while (j--) {
- clearTimeout(wrap.tasks[j]);
- }
- delete wrap.tasks;
- }
-
- if (this.touchRe.test(eventName)) {
- Ext.gesture.Manager.removeEventListener(dom, eventName, wrap);
- }
- else {
- // now add the event listener to the actual element!
- dom.removeEventListener(eventName, wrap, false);
- }
-
- // remove listener from cache
- cache.splice(i, 1);
- }
- }
- },
-
- /**
- * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners}
- * directly on an Element in favor of calling this version.
- * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
- */
- removeAll : function(element){
- var dom = Ext.getDom(element),
- cache = this.getElementEventCache(dom),
- ev;
-
- for (ev in cache) {
- if (!cache.hasOwnProperty(ev)) {
- continue;
- }
- this.removeListener(dom, ev);
- }
- Ext.cache[dom.id].events = {};
- },
-
- purgeElement : function(element, recurse, eventName) {
- var dom = Ext.getDom(element),
- i = 0, len;
-
- if(eventName) {
- this.removeListener(dom, eventName);
- }
- else {
- this.removeAll(dom);
- }
-
- if(recurse && dom && dom.childNodes) {
- for(len = element.childNodes.length; i < len; i++) {
- this.purgeElement(element.childNodes[i], recurse, eventName);
- }
- }
- },
-
- handleListenerConfig : function(element, config, remove) {
- var key, value;
-
- // loop over all the keys in the object
- for (key in config) {
- if (!config.hasOwnProperty(key)) {
- continue;
- }
- // if the key is something else then an event option
- if (!this.optionsRe.test(key)) {
- value = config[key];
- // if the value is a function it must be something like click: function(){}, scope: this
- // which means that there might be multiple event listeners with shared options
- if (Ext.isFunction(value)) {
- // shared options
- this[(remove ? 'remove' : 'add') + 'Listener'](element, key, value, config.scope, config);
- }
- // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
- else {
- // individual options
- this[(remove ? 'remove' : 'add') + 'Listener'](element, key, config.fn, config.scope, config);
- }
- }
- }
- },
-
- getId : function(element) {
- // if we bind listeners to either the document or the window
- // we have to put them in their own id cache since they both
- // can't get id's on the actual element
- var skip = false,
- id;
-
- element = Ext.getDom(element);
-
- if (element === document || element === window) {
- skip = true;
- }
-
- id = Ext.id(element);
-
- if (!Ext.cache[id]){
- Ext.Element.addToCache(new Ext.Element(element), id);
- if(skip){
- Ext.cache[id].skipGarbageCollection = true;
- }
- }
- return id;
- },
-
- // private
- createListenerWrap : function(dom, ename, fn, scope, o, touch) {
- o = !Ext.isObject(o) ? {} : o;
-
- var f = ["if(!window.Ext) {return;}"];
-
- if (touch) {
- f.push('e = new Ext.TouchEventObjectImpl(e, args);');
- }
- else {
- if(o.buffer || o.delay) {
- f.push('e = new Ext.EventObjectImpl(e);');
- } else {
- f.push('e = Ext.EventObject.setEvent(e);');
- }
- }
-
- if (o.delegate) {
- f.push('var t = e.getTarget("' + o.delegate + '", this);');
- f.push('if(!t) {return;}');
- } else {
- f.push('var t = e.target;');
- }
-
- if (o.target) {
- f.push('if(e.target !== o.target) {return;}');
- }
-
- if(o.stopEvent) {
- f.push('e.stopEvent();');
- } else {
- if(o.preventDefault) {
- f.push('e.preventDefault();');
- }
- if(o.stopPropagation) {
- f.push('e.stopPropagation();');
- }
- }
-
- if(o.normalized === false) {
- f.push('e = e.browserEvent;');
- }
-
- if(o.buffer) {
- f.push('(wrap.task && clearTimeout(wrap.task));');
- f.push('wrap.task = setTimeout(function(){');
- }
-
- if(o.delay) {
- f.push('wrap.tasks = wrap.tasks || [];');
- f.push('wrap.tasks.push(setTimeout(function(){');
- }
-
- // finally call the actual handler fn
- f.push('fn.call(scope || dom, e, t, o);');
-
- if(o.single) {
- f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
- }
-
- if(o.delay) {
- f.push('}, ' + o.delay + '));');
- }
-
- if(o.buffer) {
- f.push('}, ' + o.buffer + ');');
- }
-
- var gen = new Function('e', 'o', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join("\n"));
-
- return function(e, args) {
- gen.call(dom, e, o, fn, scope, ename, dom, arguments.callee, args);
- };
- },
-
- getEventListenerCache : function(element, eventName) {
- var eventCache = this.getElementEventCache(element);
- return eventCache[eventName] || (eventCache[eventName] = []);
- },
-
- getElementEventCache : function(element) {
- var elementCache = Ext.cache[this.getId(element)];
- return elementCache.events || (elementCache.events = {});
- },
-
- /**
- * 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 (<code>this</code> 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
- * <code>{single: true}</code> be used so that the handler is removed on first invocation.
- */
- onDocumentReady : function(fn, scope, options){
- var me = this,
- readyEvent = me.readyEvent,
- intervalId;
-
- if(Ext.isReady){ // if it already fired
- readyEvent || (readyEvent = new Ext.util.Event());
- readyEvent.addListener(fn, scope, options);
- readyEvent.fire();
- readyEvent.listeners = []; // clearListeners no longer compatible. Force single: true?
- }
- else {
- if(!readyEvent) {
- readyEvent = me.readyEvent = new Ext.util.Event();
-
- // the method that will actually fire the event and clean up any listeners and intervals
- var fireReady = function() {
- Ext.isReady = true;
-
- //document.removeEventListener('DOMContentLoaded', arguments.callee, false);
- window.removeEventListener('load', arguments.callee, false);
-
- // remove interval if there is one
- if (intervalId) {
- clearInterval(intervalId);
- }
-
- // Put this in a timeout to give the browser a chance to hide address
- // bars or do other things that would screw up viewport measurements
- setTimeout(function() {
- Ext.supports.init();
- //Ext.TouchEventManager.init();
- Ext.gesture.Manager.init();
- Ext.orientation = Ext.Element.getOrientation();
-
- // fire the ready event!!
- readyEvent.fire({
- orientation: Ext.orientation,
- width: Ext.Element.getViewportWidth(),
- height: Ext.Element.getViewportHeight()
- });
- readyEvent.listeners = [];
- }, 50);
- };
-
- // for browsers that support DOMContentLoaded
- //document.addEventListener('DOMContentLoaded', fireReady, false);
-
- // // even though newer versions support DOMContentLoaded, we have to be sure
- intervalId = setInterval(function(){
- if(/loaded|complete/.test(document.readyState)) {
- clearInterval(intervalId);
- intervalId = null;
- fireReady();
- }
- }, 10);
-
- // final fallback method
- window.addEventListener('load', fireReady, false);
- }
-
- options = options || {};
- options.delay = options.delay || 1;
- readyEvent.addListener(fn, scope, options);
- }
- },
-
- /**
- * Adds a listener to be notified when the browser window is resized and provides resize event buffering (50 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 (<code>this</code> 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) {
- var me = this,
- resizeEvent = me.resizeEvent;
-
- if(!resizeEvent){
- me.resizeEvent = resizeEvent = new Ext.util.Event();
- var onResize = function() {
- resizeEvent.fire(Ext.Element.getViewportWidth(), Ext.Element.getViewportHeight());
- };
- this.addListener(window, 'resize', onResize, this);
- }
-
- resizeEvent.addListener(fn, scope, options);
- },
-
- onOrientationChange : function(fn, scope, options) {
- var me = this,
- orientationEvent = me.orientationEvent;
-
- if (!orientationEvent) {
- me.orientationEvent = orientationEvent = new Ext.util.Event();
-
- var onOrientationChange = function(viewport, size) {
- Ext.orientation = Ext.Viewport.getOrientation();
-
- orientationEvent.fire(Ext.orientation, size.width, size.height);
- };
-
- Ext.Viewport.on('resize', onOrientationChange, this);
- }
-
- orientationEvent.addListener(fn, scope, options);
- },
-
- unOrientationChange : function(fn, scope, options) {
- var me = this,
- orientationEvent = me.orientationEvent;
-
- if (orientationEvent) {
- orientationEvent.removeListener(fn, scope, options);
- }
- }
-};
-
-/**
-* 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) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
-* @param {Object} options (optional) An object containing standard {@link #addListener} options
-* @member Ext.EventManager
-* @method on
-*/
-Ext.EventManager.on = Ext.EventManager.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. <b>This must be a reference to the function passed into the {@link #on} call.</b>
- * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
- * then this must refer to the same object.
- * @member Ext.EventManager
- * @method un
- */
-Ext.EventManager.un = Ext.EventManager.removeListener;
-
-/**
- * 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 (<code>this</code> 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
- * <code>{single: true}</code> be used so that the handler is removed on first invocation.
- * @member Ext
- * @method onReady
- */
-Ext.onReady = Ext.EventManager.onDocumentReady;
-
-Ext.EventObjectImpl = Ext.extend(Object, {
- constructor : function(e) {
- if (e) {
- this.setEvent(e.browserEvent || e);
- }
- },
-
- /** @private */
- setEvent : function(e){
- var me = this;
- if (e == me || (e && e.browserEvent)){ // already wrapped
- return e;
- }
- me.browserEvent = e;
- if(e) {
- me.type = e.type;
-
- // cache the target for the delayed and or buffered events
- var node = e.target;
- me.target = node && node.nodeType == 3 ? node.parentNode : node;
-
- // same for XY
- me.xy = [e.pageX, e.pageY];
- me.timestamp = e.timeStamp;
- } else {
- me.target = null;
- me.xy = [0, 0];
- }
- return me;
- },
-
- /**
- * Stop the event (preventDefault and stopPropagation)
- */
- stopEvent : function(){
- this.stopPropagation();
- this.preventDefault();
- },
-
- /**
- * Prevents the browsers default handling of the event.
- */
- preventDefault : function(){
- if(this.browserEvent) {
- this.browserEvent.preventDefault();
- }
- },
-
- /**
- * Cancels bubbling of the event.
- */
- stopPropagation : function() {
- if(this.browserEvent) {
- this.browserEvent.stopPropagation();
- }
- },
-
- /**
- * Gets the x coordinate of the event.
- * @return {Number}
- */
- getPageX : function(){
- return this.xy[0];
- },
-
- /**
- * Gets the y coordinate of the event.
- * @return {Number}
- */
- getPageY : function(){
- return this.xy[1];
- },
-
- /**
- * Gets the page coordinates of the event.
- * @return {Array} The xy values like [x, y]
- */
- getXY : function(){
- return this.xy;
- },
-
- /**
- * 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}
- */
- getTarget : function(selector, maxDepth, returnEl) {
- return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
- },
-
- getTime : function() {
- return this.timestamp;
- }
-});
-
-/**
- * @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.
- * <p>For example:</p>
- * <pre><code>
-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);
- </code></pre>
- * @singleton
- */
-Ext.EventObject = new Ext.EventObjectImpl();
-/**
- * @class Ext.is
- *
- * Determines information about the current platform the application is running on.
- *
- * @singleton
- */
-Ext.is = {
- init : function(navigator) {
- var platforms = this.platforms,
- ln = platforms.length,
- i, platform;
-
- navigator = navigator || window.navigator;
-
- for (i = 0; i < ln; i++) {
- platform = platforms[i];
- this[platform.identity] = platform.regex.test(navigator[platform.property]);
- }
-
- /**
- * @property Desktop True if the browser is running on a desktop machine
- * @type {Boolean}
- */
- this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
- /**
- * @property Tablet True if the browser is running on a tablet (iPad)
- */
- this.Tablet = this.iPad;
- /**
- * @property Phone True if the browser is running on a phone.
- * @type {Boolean}
- */
- this.Phone = !this.Desktop && !this.Tablet;
- /**
- * @property iOS True if the browser is running on iOS
- * @type {Boolean}
- */
- this.iOS = this.iPhone || this.iPad || this.iPod;
-
- /**
- * @property Standalone Detects when application has been saved to homescreen.
- * @type {Boolean}
- */
- this.Standalone = !!window.navigator.standalone;
- },
-
- /**
- * @property iPhone True when the browser is running on a iPhone
- * @type {Boolean}
- */
- platforms: [{
- property: 'platform',
- regex: /iPhone/i,
- identity: 'iPhone'
- },
-
- /**
- * @property iPod True when the browser is running on a iPod
- * @type {Boolean}
- */
- {
- property: 'platform',
- regex: /iPod/i,
- identity: 'iPod'
- },
-
- /**
- * @property iPad True when the browser is running on a iPad
- * @type {Boolean}
- */
- {
- property: 'userAgent',
- regex: /iPad/i,
- identity: 'iPad'
- },
-
- /**
- * @property Blackberry True when the browser is running on a Blackberry
- * @type {Boolean}
- */
- {
- property: 'userAgent',
- regex: /Blackberry/i,
- identity: 'Blackberry'
- },
-
- /**
- * @property Android True when the browser is running on an Android device
- * @type {Boolean}
- */
- {
- property: 'userAgent',
- regex: /Android/i,
- identity: 'Android'
- },
-
- /**
- * @property Mac True when the browser is running on a Mac
- * @type {Boolean}
- */
- {
- property: 'platform',
- regex: /Mac/i,
- identity: 'Mac'
- },
-
- /**
- * @property Windows True when the browser is running on Windows
- * @type {Boolean}
- */
- {
- property: 'platform',
- regex: /Win/i,
- identity: 'Windows'
- },
-
- /**
- * @property Linux True when the browser is running on Linux
- * @type {Boolean}
- */
- {
- property: 'platform',
- regex: /Linux/i,
- identity: 'Linux'
- }]
-};
-
-Ext.is.init();
-
-/**
- * @class Ext.supports
- *
- * Determines information about features are supported in the current environment
- *
- * @singleton
- */
-Ext.supports = {
- init : function() {
- var doc = document,
- div = doc.createElement('div'),
- tests = this.tests,
- ln = tests.length,
- i, test;
-
- div.innerHTML = [
- '<div style="height:30px;width:50px;">',
- '<div style="height:20px;width:20px;"></div>',
- '</div>',
- '<div style="float:left; background-color:transparent;"></div>'
- ].join('');
-
- doc.body.appendChild(div);
-
- for (i = 0; i < ln; i++) {
- test = tests[i];
- this[test.identity] = test.fn.call(this, doc, div);
- }
-
- doc.body.removeChild(div);
- },
-
- /**
- * @property OrientationChange True if the device supports orientation change
- * @type {Boolean}
- */
- OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
-
- /**
- * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
- * @type {Boolean}
- */
- DeviceMotion: ('ondevicemotion' in window),
-
- /**
- * @property Touch True if the device supports touch
- * @type {Boolean}
- */
- // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
- // and Safari 4.0 (they all have 'ontouchstart' in the window object).
- Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),
-
- tests: [
- /**
- * @property Transitions True if the device supports CSS3 Transitions
- * @type {Boolean}
- */
- {
- identity: 'Transitions',
- fn: function(doc, div) {
- var prefix = [
- 'webkit',
- 'Moz',
- 'o',
- 'ms',
- 'khtml'
- ],
- TE = 'TransitionEnd',
- transitionEndName = [
- prefix[0] + TE,
- 'transitionend', //Moz bucks the prefixing convention
- prefix[2] + TE,
- prefix[3] + TE,
- prefix[4] + TE
- ],
- ln = prefix.length,
- i = 0,
- out = false;
- div = Ext.get(div);
- for (; i < ln; i++) {
- if (div.getStyle(prefix[i] + "TransitionProperty")) {
- Ext.supports.CSS3Prefix = prefix[i];
- Ext.supports.CSS3TransitionEnd = transitionEndName[i];
- out = true;
- break;
- }
- }
- return out;
- }
- },
-
- /**
- * @property RightMargin True if the device supports right margin
- * @type {Boolean}
- */
- {
- identity: 'RightMargin',
- fn: function(doc, div, view) {
- view = doc.defaultView;
- return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
- }
- },
-
- /**
- * @property TransparentColor True if the device supports transparent color
- * @type {Boolean}
- */
- {
- identity: 'TransparentColor',
- fn: function(doc, div, view) {
- view = doc.defaultView;
- return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
- }
- },
-
- /**
- * @property SVG True if the device supports SVG
- * @type {Boolean}
- */
- {
- identity: 'SVG',
- fn: function(doc) {
- return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
- }
- },
-
- /**
- * @property Canvas True if the device supports Canvas
- * @type {Boolean}
- */
- {
- identity: 'Canvas',
- fn: function(doc) {
- return !!doc.createElement('canvas').getContext;
- }
- },
-
- /**
- * @property VML True if the device supports VML
- * @type {Boolean}
- */
- {
- identity: 'VML',
- fn: function(doc) {
- var d = doc.createElement("div");
- d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
- return (d.childNodes.length == 2);
- }
- },
-
- /**
- * @property Float True if the device supports CSS float
- * @type {Boolean}
- */
- {
- identity: 'Float',
- fn: function(doc, div) {
- return !!div.lastChild.style.cssFloat;
- }
- },
-
- /**
- * @property AudioTag True if the device supports the HTML5 audio tag
- * @type {Boolean}
- */
- {
- identity: 'AudioTag',
- fn: function(doc) {
- return !!doc.createElement('audio').canPlayType;
- }
- },
-
- /**
- * @property History True if the device supports HTML5 history
- * @type {Boolean}
- */
- {
- identity: 'History',
- fn: function() {
- return !!(window.history && history.pushState);
- }
- },
-
- /**
- * @property CSS3DTransform True if the device supports CSS3DTransform
- * @type {Boolean}
- */
- {
- identity: 'CSS3DTransform',
- fn: function() {
- return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
- }
- },
-
- /**
- * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
- * @type {Boolean}
- */
- {
- identity: 'CSS3LinearGradient',
- fn: function(doc, div) {
- var property = 'background-image:',
- webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
- w3c = 'linear-gradient(left top, black, white)',
- moz = '-moz-' + w3c,
- options = [property + webkit, property + w3c, property + moz];
-
- div.style.cssText = options.join(';');
-
- return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
- }
- },
-
- /**
- * @property CSS3BorderRadius True if the device supports CSS3 border radius
- * @type {Boolean}
- */
- {
- identity: 'CSS3BorderRadius',
- fn: function(doc, div) {
- var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
- pass = false,
- i;
-
- for (i = 0; i < domPrefixes.length; i++) {
- if (document.body.style[domPrefixes[i]] !== undefined) {
- return pass = true;
- }
- }
-
- return pass;
- }
- },
-
- /**
- * @property GeoLocation True if the device supports GeoLocation
- * @type {Boolean}
- */
- {
- identity: 'GeoLocation',
- fn: function() {
- return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
- }
- }
- ]
-};
-
-
-/**
- * @author Ed Spencer
- * @class Ext.data.Batch
- * @extends Ext.util.Observable
- *
- * <p>Provides a mechanism to run one or more {@link Ext.data.Operation operations} in a given order. Fires the 'operationcomplete' event
- * after the completion of each Operation, and the 'complete' event when all Operations have been successfully executed. Fires an 'exception'
- * event if any of the Operations encounter an exception.</p>
- *
- * <p>Usually these are only used internally by {@link Ext.data.Proxy} classes</p>
- *
- * @constructor
- * @param {Object} config Optional config object
- */
-Ext.data.Batch = Ext.extend(Ext.util.Observable, {
- /**
- * True to immediately start processing the batch as soon as it is constructed (defaults to false)
- * @property autoStart
- * @type Boolean
- */
- autoStart: false,
-
- /**
- * The index of the current operation being executed
- * @property current
- * @type Number
- */
- current: -1,
-
- /**
- * The total number of operations in this batch. Read only
- * @property total
- * @type Number
- */
- total: 0,
-
- /**
- * True if the batch is currently running
- * @property isRunning
- * @type Boolean
- */
- isRunning: false,
-
- /**
- * True if this batch has been executed completely
- * @property isComplete
- * @type Boolean
- */
- isComplete: false,
-
- /**
- * True if this batch has encountered an exception. This is cleared at the start of each operation
- * @property hasException
- * @type Boolean
- */
- hasException: false,
-
- /**
- * True to automatically pause the execution of the batch if any operation encounters an exception (defaults to true)
- * @property pauseOnException
- * @type Boolean
- */
- pauseOnException: true,
-
- constructor: function(config) {
- this.addEvents(
- /**
- * @event complete
- * Fired when all operations of this batch have been completed
- * @param {Ext.data.Batch} batch The batch object
- * @param {Object} operation The last operation that was executed
- */
- 'complete',
-
- /**
- * @event exception
- * Fired when a operation encountered an exception
- * @param {Ext.data.Batch} batch The batch object
- * @param {Object} operation The operation that encountered the exception
- */
- 'exception',
-
- /**
- * @event operationcomplete
- * Fired when each operation of the batch completes
- * @param {Ext.data.Batch} batch The batch object
- * @param {Object} operation The operation that just completed
- */
- 'operationcomplete',
-
- //TODO: Remove this once we deprecate this function in 1.0. See below for further details
- 'operation-complete'
- );
-
- Ext.data.Batch.superclass.constructor.call(this, config);
-
- /**
- * Ordered array of operations that will be executed by this batch
- * @property operations
- * @type Array
- */
- this.operations = [];
- },
-
- /**
- * Adds a new operation to this batch
- * @param {Object} operation The {@link Ext.data.Operation Operation} object
- */
- add: function(operation) {
- this.total++;
-
- operation.setBatch(this);
-
- this.operations.push(operation);
- },
-
- /**
- * Kicks off the execution of the batch, continuing from the next operation if the previous
- * operation encountered an exception, or if execution was paused
- */
- start: function() {
- this.hasException = false;
- this.isRunning = true;
-
- this.runNextOperation();
- },
-
- /**
- * @private
- * Runs the next operation, relative to this.current.
- */
- runNextOperation: function() {
- this.runOperation(this.current + 1);
- },
-
- /**
- * Pauses execution of the batch, but does not cancel the current operation
- */
- pause: function() {
- this.isRunning = false;
- },
-
- /**
- * Executes a operation by its numeric index
- * @param {Number} index The operation index to run
- */
- runOperation: function(index) {
- var operations = this.operations,
- operation = operations[index];
-
- if (operation == undefined) {
- this.isRunning = false;
- this.isComplete = true;
- this.fireEvent('complete', this, operations[operations.length - 1]);
- } else {
- this.current = index;
-
- var onProxyReturn = function(operation) {
- var hasException = operation.hasException();
-
- if (hasException) {
- this.hasException = true;
- this.fireEvent('exception', this, operation);
- } else {
- //TODO: deprecate the dashed version of this event name 'operation-complete' as it breaks convention
- //to be removed in 1.0
- this.fireEvent('operation-complete', this, operation);
-
- this.fireEvent('operationcomplete', this, operation);
- }
-
- if (hasException && this.pauseOnException) {
- this.pause();
- } else {
- operation.setCompleted();
-
- this.runNextOperation();
- }
- };
-
- operation.setStarted();
-
- this.proxy[operation.action](operation, onProxyReturn, this);
- }
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.Model
- * @extends Ext.util.Stateful
- *
- * <p>A Model represents some object that your application manages. For example, one might define a Model for Users, Products,
- * Cars, or any other real-world object that we want to model in the system. Models are registered via the {@link Ext.ModelMgr model manager},
- * and are used by {@link Ext.data.Store stores}, which are in turn used by many of the data-bound components in Ext.</p>
- *
- * <p>Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: [
- {name: 'name', type: 'string'},
- {name: 'age', type: 'int'},
- {name: 'phone', type: 'string'},
- {name: 'alive', type: 'boolean', defaultValue: true}
- ],
-
- changeName: function() {
- var oldName = this.get('name'),
- newName = oldName + " The Barbarian";
-
- this.set('name', newName);
- }
-});
-</code></pre>
-*
-* <p>The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link Ext.ModelMgr ModelMgr}, and all
-* other functions and properties are copied to the new Model's prototype.</p>
-*
-* <p>Now we can create instances of our User model and call any model logic we defined:</p>
-*
-<pre><code>
-var user = Ext.ModelMgr.create({
- name : 'Conan',
- age : 24,
- phone: '555-555-5555'
-}, 'User');
-
-user.changeName();
-user.get('name'); //returns "Conan The Barbarian"
-</code></pre>
- *
- * <p><u>Validations</u></p>
- *
- * <p>Models have built-in support for validations, which are executed against the validator functions in
- * {@link Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to models:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: [
- {name: 'name', type: 'string'},
- {name: 'age', type: 'int'},
- {name: 'phone', type: 'string'},
- {name: 'gender', type: 'string'},
- {name: 'username', type: 'string'},
- {name: 'alive', type: 'boolean', defaultValue: true}
- ],
-
- validations: [
- {type: 'presence', field: 'age'},
- {type: 'length', field: 'name', min: 2},
- {type: 'inclusion', field: 'gender', list: ['Male', 'Female']},
- {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
- {type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
- ]
-});
-</code></pre>
- *
- * <p>The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
- * object:</p>
- *
-<pre><code>
-var instance = Ext.ModelMgr.create({
- name: 'Ed',
- gender: 'Male',
- username: 'edspencer'
-}, 'User');
-
-var errors = instance.validate();
-</code></pre>
- *
- * <p><u>Associations</u></p>
- *
- * <p>Models can have associations with other Models via {@link Ext.data.BelongsToAssociation belongsTo} and
- * {@link Ext.data.HasManyAssociation hasMany} associations. For example, let's say we're writing a blog administration
- * application which deals with Users, Posts and Comments. We can express the relationships between these models like this:</p>
- *
-<pre><code>
-Ext.regModel('Post', {
- fields: ['id', 'user_id'],
-
- belongsTo: 'User',
- hasMany : {model: 'Comment', name: 'comments'}
-});
-
-Ext.regModel('Comment', {
- fields: ['id', 'user_id', 'post_id'],
-
- belongsTo: 'Post'
-});
-
-Ext.regModel('User', {
- fields: ['id'],
-
- hasMany: [
- 'Post',
- {model: 'Comment', name: 'comments'}
- ]
-});
-</code></pre>
- *
- * <p>See the docs for {@link Ext.data.BelongsToAssociation} and {@link Ext.data.HasManyAssociation} for details on the usage
- * and configuration of associations. Note that associations can also be specified like this:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: ['id'],
-
- associations: [
- {type: 'hasMany', model: 'Post', name: 'posts'},
- {type: 'hasMany', model: 'Comment', name: 'comments'}
- ]
-});
-</code></pre>
- *
- * <p><u>Using a Proxy</u></p>
- *
- * <p>Models are great for representing types of data and relationships, but sooner or later we're going to want to
- * load or save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.Proxy Proxy},
- * which can be set directly on the Model:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: ['id', 'name', 'email'],
-
- proxy: {
- type: 'rest',
- url : '/users'
- }
-});
-</code></pre>
- *
- * <p>Here we've set up a {@link Ext.data.RestProxy Rest Proxy}, which knows how to load and save data to and from a
- * RESTful backend. Let's see how this works:</p>
- *
-<pre><code>
-var user = Ext.ModelMgr.create({name: 'Ed Spencer', email: 'ed at sencha.com'}, 'User');
-
-user.save(); //POST /users
-</code></pre>
- *
- * <p>Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this
- * Model's data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't
- * have an id, and performs the appropriate action - in this case issuing a POST request to the url we configured
- * (/users). We configure any Proxy on any Model and always follow this API - see {@link Ext.data.Proxy} for a full
- * list.</p>
- *
- * <p>Loading data via the Proxy is equally easy:</p>
- *
-<pre><code>
-//get a reference to the User model class
-var User = Ext.ModelMgr.getModel('User');
-
-//Uses the configured RestProxy to make a GET request to /users/123
-User.load(123, {
- success: function(user) {
- console.log(user.getId()); //logs 123
- }
-});
-</code></pre>
- *
- * <p>Models can also be updated and destroyed easily:</p>
- *
-<pre><code>
-//the user Model we loaded in the last snippet:
-user.set('name', 'Edward Spencer');
-
-//tells the Proxy to save the Model. In this case it will perform a PUT request to /users/123 as this Model already has an id
-user.save({
- success: function() {
- console.log('The User was updated');
- }
-});
-
-//tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
-user.destroy({
- success: function() {
- console.log('The User was destroyed!');
- }
-});
-</code></pre>
- *
- * <p><u>Usage in Stores</u></p>
- *
- * <p>It is very common to want to load a set of Model instances to be displayed and manipulated in the UI. We do this
- * by creating a {@link Ext.data.Store Store}:</p>
- *
-<pre><code>
-var store = new Ext.data.Store({
- model: 'User'
-});
-
-//uses the Proxy we set up on Model to load the Store data
-store.load();
-</code></pre>
- *
- * <p>A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain
- * a set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the
- * {@link Ext.data.Store Store docs} for more information on Stores.</p>
- *
- * @constructor
- * @param {Object} data An object containing keys corresponding to this model's fields, and their associated values
- * @param {Number} id Optional unique ID to assign to this model instance
- */
-Ext.data.Model = Ext.extend(Ext.util.Stateful, {
- evented: false,
- isModel: true,
-
- /**
- * <tt>true</tt> when the record does not yet exist in a server-side database (see
- * {@link #setDirty}). Any record which has a real database pk set as its id property
- * is NOT a phantom -- it's real.
- * @property phantom
- * @type {Boolean}
- */
- phantom : false,
-
- /**
- * The name of the field treated as this Model's unique id (defaults to 'id').
- * @property idProperty
- * @type String
- */
- idProperty: 'id',
-
- constructor: function(data, id) {
- data = data || {};
-
- /**
- * An internal unique ID for each Model instance, used to identify Models that don't have an ID yet
- * @property internalId
- * @type String
- * @private
- */
- this.internalId = (id || id === 0) ? id : Ext.data.Model.id(this);
-
- Ext.data.Model.superclass.constructor.apply(this);
-
- //add default field values if present
- var fields = this.fields.items,
- length = fields.length,
- field, name, i;
-
- for (i = 0; i < length; i++) {
- field = fields[i];
- name = field.name;
-
- if (data[name] == undefined) {
- data[name] = field.defaultValue;
- }
- }
-
- this.set(data);
- this.dirty = false;
-
- if (this.getId()) {
- this.phantom = false;
- }
-
- if (typeof this.init == 'function') {
- this.init();
- }
- },
-
- /**
- * Validates the current data against all of its configured {@link #validations} and returns an
- * {@link Ext.data.Errors Errors} object
- * @return {Ext.data.Errors} The errors object
- */
- validate: function() {
- var errors = new Ext.data.Errors(),
- validations = this.validations,
- validators = Ext.data.validations,
- length, validation, field, valid, type, i;
-
- if (validations) {
- length = validations.length;
-
- for (i = 0; i < length; i++) {
- validation = validations[i];
- field = validation.field || validation.name;
- type = validation.type;
- valid = validators[type](validation, this.get(field));
-
- if (!valid) {
- errors.add({
- field : field,
- message: validation.message || validators[type + 'Message']
- });
- }
- }
- }
-
- return errors;
- },
-
- /**
- * Returns the configured Proxy for this Model
- * @return {Ext.data.Proxy} The proxy
- */
- getProxy: function() {
- return this.constructor.proxy;
- },
-
- /**
- * Saves the model instance using the configured proxy
- * @param {Object} options Options to pass to the proxy
- * @return {Ext.data.Model} The Model instance
- */
- save: function(options) {
- var me = this,
- action = me.phantom ? 'create' : 'update';
-
- options = options || {};
-
- Ext.apply(options, {
- records: [me],
- action : action
- });
-
- var operation = new Ext.data.Operation(options),
- successFn = options.success,
- failureFn = options.failure,
- callbackFn = options.callback,
- scope = options.scope,
- record;
-
- var callback = function(operation) {
- record = operation.getRecords()[0];
-
- if (operation.wasSuccessful()) {
- //we need to make sure we've set the updated data here. Ideally this will be redundant once the
- //ModelCache is in place
- me.set(record.data);
- record.dirty = false;
-
- if (typeof successFn == 'function') {
- successFn.call(scope, record, operation);
- }
- } else {
- if (typeof failureFn == 'function') {
- failureFn.call(scope, record, operation);
- }
- }
-
- if (typeof callbackFn == 'function') {
- callbackFn.call(scope, record, operation);
- }
- };
-
- me.getProxy()[action](operation, callback, me);
-
- return me;
- },
-
- /**
- * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}
- * @return {Number} The id
- */
- getId: function() {
- return this.get(this.idProperty);
- },
-
- /**
- * Sets the model instance's id field to the given id
- * @param {Number} id The new id
- */
- setId: function(id) {
- this.set(this.idProperty, id);
- },
-
- /**
- * Tells this model instance that it has been added to a store
- * @param {Ext.data.Store} store The store that the model has been added to
- */
- join : function(store) {
- /**
- * The {@link Ext.data.Store} to which this Record belongs.
- * @property store
- * @type {Ext.data.Store}
- */
- this.store = store;
- },
-
- /**
- * Tells this model instance that it has been removed from the store
- * @param {Ext.data.Store} store The store to unjoin
- */
- unjoin: function(store) {
- delete this.store;
- },
-
- /**
- * @private
- * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
- * afterEdit method is called
- */
- afterEdit : function() {
- this.callStore('afterEdit');
- },
-
- /**
- * @private
- * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
- * afterReject method is called
- */
- afterReject : function() {
- this.callStore("afterReject");
- },
-
- /**
- * @private
- * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
- * afterCommit method is called
- */
- afterCommit: function() {
- this.callStore('afterCommit');
- },
-
- /**
- * @private
- * Helper function used by afterEdit, afterReject and afterCommit. Calls the given method on the
- * {@link Ext.data.Store store} that this instance has {@link #join joined}, if any. The store function
- * will always be called with the model instance as its single argument.
- * @param {String} fn The function to call on the store
- */
- callStore: function(fn) {
- var store = this.store;
-
- if (store != undefined && typeof store[fn] == "function") {
- store[fn](this);
- }
- }
-});
-
-Ext.apply(Ext.data.Model, {
- /**
- * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext.data.ProxyMgr#create}
- * @param {String/Object/Ext.data.Proxy} proxy The proxy
- */
- setProxy: function(proxy) {
- //make sure we have an Ext.data.Proxy object
- proxy = Ext.data.ProxyMgr.create(proxy);
-
- proxy.setModel(this);
- this.proxy = proxy;
-
- return proxy;
- },
-
- /**
- * <b>Static</b>. Asynchronously loads a model instance by id. Sample usage:
-<pre><code>
-MyApp.User = Ext.regModel('User', {
- fields: [
- {name: 'id', type: 'int'},
- {name: 'name', type: 'string'}
- ]
-});
-
-MyApp.User.load(10, {
- scope: this,
- failure: function(record, operation) {
- //do something if the load failed
- },
- success: function(record, operation) {
- //do something if the load succeeded
- },
- callback: function(record, operation) {
- //do something whether the load succeeded or failed
- }
-});
-</code></pre>
- * @param {Number} id The id of the model to load
- * @param {Object} config Optional config object containing success, failure and callback functions, plus optional scope
- * @member Ext.data.Model
- * @method load
- * @static
- */
- load: function(id, config) {
- config = Ext.applyIf(config || {}, {
- action: 'read',
- id : id
- });
-
- var operation = new Ext.data.Operation(config),
- callbackFn = config.callback,
- successFn = config.success,
- failureFn = config.failure,
- scope = config.scope,
- record, callback;
-
- callback = function(operation) {
- record = operation.getRecords()[0];
-
- if (operation.wasSuccessful()) {
- if (typeof successFn == 'function') {
- successFn.call(scope, record, operation);
- }
- } else {
- if (typeof failureFn == 'function') {
- failureFn.call(scope, record, operation);
- }
- }
-
- if (typeof callbackFn == 'function') {
- callbackFn.call(scope, record, operation);
- }
- };
-
- this.proxy.read(operation, callback, this);
- }
-});
-
-/**
- * Generates a sequential id. This method is typically called when a record is {@link #create}d
- * and {@link #Record no id has been specified}. The returned id takes the form:
- * <tt>{PREFIX}-{AUTO_ID}</tt>.<div class="mdetail-params"><ul>
- * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.PREFIX</tt>
- * (defaults to <tt>'ext-record'</tt>)</p></li>
- * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.AUTO_ID</tt>
- * (defaults to <tt>1</tt> initially)</p></li>
- * </ul></div>
- * @param {Record} rec The record being created. The record does not exist, it's a {@link #phantom}.
- * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
- */
-Ext.data.Model.id = function(rec) {
- rec.phantom = true;
- return [Ext.data.Model.PREFIX, '-', Ext.data.Model.AUTO_ID++].join('');
-};
-
-
-//[deprecated 5.0]
-Ext.ns('Ext.data.Record');
-
-//Backwards compat
-Ext.data.Record.id = Ext.data.Model.id;
-//[end]
-
-Ext.data.Model.PREFIX = 'ext-record';
-Ext.data.Model.AUTO_ID = 1;
-Ext.data.Model.EDIT = 'edit';
-Ext.data.Model.REJECT = 'reject';
-Ext.data.Model.COMMIT = 'commit';
-
-/**
- * @author Ed Spencer
- * @class Ext.data.Association
- * @extends Object
- *
- * <p>Associations enable you to express relationships between different {@link Ext.data.Model Models}. Let's say we're
- * writing an ecommerce system where Users can make Orders - there's a relationship between these Models that we can
- * express like this:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: ['id', 'name', 'email'],
-
- hasMany: {model: 'Order', name: 'orders'}
-});
-
-Ext.regModel('Order', {
- fields: ['id', 'user_id', 'status', 'price'],
-
- belongsTo: 'User'
-});
-</code></pre>
- *
- * <p>We've set up two models - User and Order - and told them about each other. You can set up as many associations on
- * each Model as you need using the two default types - {@link Ext.data.HasManyAssociation hasMany} and
- * {@link Ext.data.BelongsToAssociation belongsTo}. There's much more detail on the usage of each of those inside their
- * documentation pages. If you're not familiar with Models already, {@link Ext.data.Model there is plenty on those too}.</p>
- *
- * <p><u>Further Reading</u></p>
- *
- * <ul style="list-style-type: disc; padding-left: 20px;">
- * <li>{@link Ext.data.HasManyAssociation hasMany associations}
- * <li>{@link Ext.data.BelongsToAssociation belongsTo associations}
- * <li>{@link Ext.data.Model using Models}
- * </ul>
- *
- * @constructor
- * @param {Object} config Optional config object
- */
-Ext.data.Association = Ext.extend(Object, {
- /**
- * @cfg {String} ownerModel The string name of the model that owns the association. Required
- */
-
- /**
- * @cfg {String} associatedModel The string name of the model that is being associated with. Required
- */
-
- /**
- * @cfg {String} primaryKey The name of the primary key on the associated model. Defaults to 'id'
- */
- primaryKey: 'id',
-
- constructor: function(config) {
- Ext.apply(this, config);
-
- var types = Ext.ModelMgr.types,
- ownerName = config.ownerModel,
- associatedName = config.associatedModel,
- ownerModel = types[ownerName],
- associatedModel = types[associatedName],
- ownerProto;
-
- if (ownerModel == undefined) {
- throw new Error("The configured ownerModel was not valid (you tried " + ownerName + ")");
- }
-
- if (associatedModel == undefined) {
- throw new Error("The configured associatedModel was not valid (you tried " + associatedName + ")");
- }
-
- this.ownerModel = ownerModel;
- this.associatedModel = associatedModel;
-
- /**
- * The name of the model that 'owns' the association
- * @property ownerName
- * @type String
- */
-
- /**
- * The name of the model is on the other end of the association (e.g. if a User model hasMany Orders, this is 'Order')
- * @property associatedName
- * @type String
- */
-
- Ext.applyIf(this, {
- ownerName : ownerName,
- associatedName: associatedName
- });
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.HasManyAssociation
- * @extends Ext.data.Association
- *
- * <p>Represents a one-to-many relationship between two models. Usually created indirectly via a model definition:</p>
- *
-<pre><code>
-Ext.regModel('Product', {
- fields: [
- {name: 'id', type: 'int'},
- {name: 'user_id', type: 'int'},
- {name: 'name', type: 'string'}
- ]
-});
-
-Ext.regModel('User', {
- fields: [
- {name: 'id', type: 'int'},
- {name: 'name', type: 'string'}
- ],
-
- hasMany: {model: 'Product', name: 'products'}
-});
-</pre></code>
-*
- * <p>Above we created Product and User models, and linked them by saying that a User hasMany Products. This gives
- * us a new function on every User instance, in this case the function is called 'products' because that is the name
- * we specified in the association configuration above.</p>
- *
- * <p>This new function returns a specialized {@link Ext.data.Store Store} which is automatically filtered to load
- * only Products for the given model instance:</p>
- *
-<pre><code>
-//first, we load up a User with id of 1
-var user = Ext.ModelMgr.create({id: 1, name: 'Ed'}, 'User');
-
-//the user.products function was created automatically by the association and returns a {@link Ext.data.Store Store}
-//the created store is automatically scoped to the set of Products for the User with id of 1
-var products = user.products();
-
-//we still have all of the usual Store functions, for example it's easy to add a Product for this User
-products.add({
- name: 'Another Product'
-});
-
-//saves the changes to the store - this automatically sets the new Product's user_id to 1 before saving
-products.sync();
-</code></pre>
- *
- * <p>The new Store is only instantiated the first time you call products() to conserve memory and processing time,
- * though calling products() a second time returns the same store instance.</p>
- *
- * <p><u>Custom filtering</u></p>
- *
- * <p>The Store is automatically furnished with a filter - by default this filter tells the store to only return
- * records where the associated model's foreign key matches the owner model's primary key. For example, if a User
- * with ID = 100 hasMany Products, the filter loads only Products with user_id == 100.</p>
- *
- * <p>Sometimes we want to filter by another field - for example in the case of a Twitter search application we may
- * have models for Search and Tweet:</p>
- *
-<pre><code>
-var Search = Ext.regModel('Search', {
- fields: [
- 'id', 'query'
- ],
-
- hasMany: {
- model: 'Tweet',
- name : 'tweets',
- filterProperty: 'query'
- }
-});
-
-Ext.regModel('Tweet', {
- fields: [
- 'id', 'text', 'from_user'
- ]
-});
-
-//returns a Store filtered by the filterProperty
-var store = new Search({query: 'Sencha Touch'}).tweets();
-</code></pre>
- *
- * <p>The tweets association above is filtered by the query property by setting the {@link #filterProperty}, and is
- * equivalent to this:</p>
- *
-<pre><code>
-var store = new Ext.data.Store({
- model: 'Tweet',
- filters: [
- {
- property: 'query',
- value : 'Sencha Touch'
- }
- ]
-});
-</code></pre>
- */
-Ext.data.HasManyAssociation = Ext.extend(Ext.data.Association, {
- /**
- * @cfg {String} foreignKey The name of the foreign key on the associated model that links it to the owner
- * model. Defaults to the lowercased name of the owner model plus "_id", e.g. an association with a where a
- * model called Group hasMany Users would create 'group_id' as the foreign key.
- */
-
- /**
- * @cfg {String} name The name of the function to create on the owner model. Required
- */
-
- /**
- * @cfg {Object} storeConfig Optional configuration object that will be passed to the generated Store. Defaults to
- * undefined.
- */
-
- /**
- * @cfg {String} filterProperty Optionally overrides the default filter that is set up on the associated Store. If
- * this is not set, a filter is automatically created which filters the association based on the configured
- * {@link #foreignKey}. See intro docs for more details. Defaults to undefined
- */
-
- constructor: function(config) {
- Ext.data.HasManyAssociation.superclass.constructor.apply(this, arguments);
-
- var ownerProto = this.ownerModel.prototype,
- name = this.name;
-
- Ext.applyIf(this, {
- storeName : name + "Store",
- foreignKey: this.ownerName.toLowerCase() + "_id"
- });
-
- ownerProto[name] = this.createStore();
- },
-
- /**
- * @private
- * Creates a function that returns an Ext.data.Store which is configured to load a set of data filtered
- * by the owner model's primary key - e.g. in a hasMany association where Group hasMany Users, this function
- * returns a Store configured to return the filtered set of a single Group's Users.
- * @return {Function} The store-generating function
- */
- createStore: function() {
- var associatedModel = this.associatedModel,
- storeName = this.storeName,
- foreignKey = this.foreignKey,
- primaryKey = this.primaryKey,
- filterProperty = this.filterProperty,
- storeConfig = this.storeConfig || {};
-
- return function() {
- var me = this,
- config, filter,
- modelDefaults = {};
-
- if (me[storeName] == undefined) {
- if (filterProperty) {
- filter = {
- property : filterProperty,
- value : me.get(filterProperty),
- exactMatch: true
- };
- } else {
- filter = {
- property : foreignKey,
- value : me.get(primaryKey),
- exactMatch: true
- };
- }
-
- modelDefaults[foreignKey] = me.get(primaryKey);
-
- config = Ext.apply({}, storeConfig, {
- model : associatedModel,
- filters : [filter],
- remoteFilter : false,
- modelDefaults: modelDefaults
- });
-
- me[storeName] = new Ext.data.Store(config);
- }
-
- return me[storeName];
- };
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.BelongsToAssociation
- * @extends Ext.data.Association
- *
- * <p>Represents a many to one association with another model. The owner model is expected to have
- * a foreign key which references the primary key of the associated model:</p>
- *
-<pre><code>
-var Category = Ext.regModel('Category', {
- fields: [
- {name: 'id', type: 'int'},
- {name: 'name', type: 'string'}
- ]
-});
-
-var Product = Ext.regModel('Product', {
- fields: [
- {name: 'id', type: 'int'},
- {name: 'category_id', type: 'int'},
- {name: 'name', type: 'string'}
- ],
-
- associations: [
- {type: 'belongsTo', model: 'Category'}
- ]
-});
-</code></pre>
- * <p>In the example above we have created models for Products and Categories, and linked them together
- * by saying that each Product belongs to a Category. This automatically links each Product to a Category
- * based on the Product's category_id, and provides new functions on the Product model:</p>
- *
- * <p><u>Generated getter function</u></p>
- *
- * <p>The first function that is added to the owner model is a getter function:</p>
- *
-<pre><code>
-var product = new Product({
- id: 100,
- category_id: 20,
- name: 'Sneakers'
-});
-
-product.getCategory(function(category, operation) {
- //do something with the category object
- alert(category.get('id')); //alerts 20
-}, this);
-</code></pre>
-*
- * <p>The getCategory function was created on the Product model when we defined the association. This uses the
- * Category's configured {@link Ext.data.Proxy proxy} to load the Category asynchronously, calling the provided
- * callback when it has loaded.</p>
- *
- * <p>The new getCategory function will also accept an object containing success, failure and callback properties
- * - callback will always be called, success will only be called if the associated model was loaded successfully
- * and failure will only be called if the associatied model could not be loaded:</p>
- *
-<pre><code>
-product.getCategory({
- callback: function(category, operation) {}, //a function that will always be called
- success : function(category, operation) {}, //a function that will only be called if the load succeeded
- failure : function(category, operation) {}, //a function that will only be called if the load did not succeed
- scope : this //optionally pass in a scope object to execute the callbacks in
-});
-</code></pre>
- *
- * <p>In each case above the callbacks are called with two arguments - the associated model instance and the
- * {@link Ext.data.Operation operation} object that was executed to load that instance. The Operation object is
- * useful when the instance could not be loaded.</p>
- *
- * <p><u>Generated setter function</u></p>
- *
- * <p>The second generated function sets the associated model instance - if only a single argument is passed to
- * the setter then the following two calls are identical:</p>
- *
-<pre><code>
-//this call
-product.setCategory(10);
-
-//is equivalent to this call:
-product.set('category_id', 10);
-</code></pre>
- * <p>If we pass in a second argument, the model will be automatically saved and the second argument passed to
- * the owner model's {@link Ext.data.Model#save save} method:</p>
-<pre><code>
-product.setCategory(10, function(product, operation) {
- //the product has been saved
- alert(product.get('category_id')); //now alerts 10
-});
-
-//alternative syntax:
-product.setCategory(10, {
- callback: function(product, operation), //a function that will always be called
- success : function(product, operation), //a function that will only be called if the load succeeded
- failure : function(product, operation), //a function that will only be called if the load did not succeed
- scope : this //optionally pass in a scope object to execute the callbacks in
-})
-</code></pre>
-*
- * <p><u>Customisation</u></p>
- *
- * <p>Associations reflect on the models they are linking to automatically set up properties such as the
- * {@link #primaryKey} and {@link #foreignKey}. These can alternatively be specified:</p>
- *
-<pre><code>
-var Product = Ext.regModel('Product', {
- fields: [...],
-
- associations: [
- {type: 'belongsTo', model: 'Category', primaryKey: 'unique_id', foreignKey: 'cat_id'}
- ]
-});
- </code></pre>
- *
- * <p>Here we replaced the default primary key (defaults to 'id') and foreign key (calculated as 'category_id')
- * with our own settings. Usually this will not be needed.</p>
- */
-Ext.data.BelongsToAssociation = Ext.extend(Ext.data.Association, {
- /**
- * @cfg {String} foreignKey The name of the foreign key on the owner model that links it to the associated
- * model. Defaults to the lowercased name of the associated model plus "_id", e.g. an association with a
- * model called Product would set up a product_id foreign key.
- */
-
- /**
- * @cfg {String} getterName The name of the getter function that will be added to the local model's prototype.
- * Defaults to 'get' + the name of the foreign model, e.g. getCategory
- */
-
- /**
- * @cfg {String} setterName The name of the setter function that will be added to the local model's prototype.
- * Defaults to 'set' + the name of the foreign model, e.g. setCategory
- */
-
- constructor: function(config) {
- Ext.data.BelongsToAssociation.superclass.constructor.apply(this, arguments);
-
- var me = this,
- ownerProto = me.ownerModel.prototype,
- associatedName = me.associatedName,
- getterName = me.getterName || 'get' + associatedName,
- setterName = me.setterName || 'set' + associatedName;
-
- Ext.applyIf(me, {
- name : associatedName,
- foreignKey : associatedName.toLowerCase() + "_id",
- instanceName: associatedName + 'BelongsToInstance'
- });
-
- ownerProto[getterName] = me.createGetter();
- ownerProto[setterName] = me.createSetter();
- },
-
- /**
- * @private
- * Returns a setter function to be placed on the owner model's prototype
- * @return {Function} The setter function
- */
- createSetter: function() {
- var me = this,
- ownerModel = me.ownerModel,
- associatedModel = me.associatedModel,
- foreignKey = me.foreignKey,
- primaryKey = me.primaryKey;
-
- //'this' refers to the Model instance inside this function
- return function(value, options, scope) {
- this.set(foreignKey, value);
-
- if (typeof options == 'function') {
- options = {
- callback: options,
- scope: scope || this
- };
- }
-
- if (Ext.isObject(options)) {
- return this.save(options);
- }
- };
- },
-
- /**
- * @private
- * Returns a getter function to be placed on the owner model's prototype. We cache the loaded instance
- * the first time it is loaded so that subsequent calls to the getter always receive the same reference.
- * @return {Function} The getter function
- */
- createGetter: function() {
- var me = this,
- ownerModel = me.ownerModel,
- associatedName = me.associatedName,
- associatedModel = me.associatedModel,
- foreignKey = me.foreignKey,
- primaryKey = me.primaryKey,
- instanceName = me.instanceName;
-
- //'this' refers to the Model instance inside this function
- return function(options, scope) {
- options = options || {};
-
- var foreignKeyId = this.get(foreignKey),
- instance, callbackFn;
-
- if (this[instanceName] == undefined) {
- instance = Ext.ModelMgr.create({}, associatedName);
- instance.set(primaryKey, foreignKeyId);
-
- if (typeof options == 'function') {
- options = {
- callback: options,
- scope: scope || this
- };
- }
-
- associatedModel.load(foreignKeyId, options);
- } else {
- instance = this[instanceName];
-
- //TODO: We're duplicating the callback invokation code that the instance.load() call above
- //makes here - ought to be able to normalize this - perhaps by caching at the Model.load layer
- //instead of the association layer.
- if (typeof options == 'function') {
- options.call(scope || this, instance);
- }
-
- if (options.success) {
- options.success.call(scope || this, instance);
- }
-
- if (options.callback) {
- options.callback.call(scope || this, instance);
- }
-
- return instance;
- }
- };
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.PolymorphicAssociation
- * @extends Ext.data.Association
- * @ignore
- */
-Ext.data.PolymorphicAssociation = Ext.extend(Ext.data.Association, {
-
- constructor: function(config) {
- Ext.data.PolymorphicAssociation.superclass.constructor.call(this, config);
-
- var ownerProto = this.ownerModel.prototype,
- name = this.name;
-
- Ext.applyIf(this, {
- associationIdField: this.ownerName.toLowerCase() + "_id"
- });
-
- ownerProto[name] = this.createStore();
- },
-
- /**
- * @private
- * Creates the association function that will be injected on the ownerModel. Most of what this is doing
- * is filtering the dataset down to the appropriate model/id combination, and adding modelDefaults to
- * any model instances that are created/inserted into the generated store.
- * @return {Function} The store-generating function
- */
- createStore: function() {
- var association = this,
- ownerName = this.ownerName,
- storeName = this.name + "Store",
- associatedModel = this.associatedModel,
- primaryKey = this.primaryKey,
- associationIdField = 'associated_id',
- associationModelField = 'associated_model';
-
- return function() {
- var me = this,
- modelDefaults = {},
- config, filters;
-
- if (me[storeName] == undefined) {
- filters = [
- {
- property : associationIdField,
- value : me.get(primaryKey),
- exactMatch: true
- },
- {
- property : associationModelField,
- value : ownerName,
- exactMatch: true
- }
- ];
-
- modelDefaults[associationIdField] = me.get(primaryKey);
- modelDefaults[associationModelField] = ownerName;
-
- config = Ext.apply({}, association.storeConfig || {}, {
- model : associatedModel,
- filters : filters,
- remoteFilter : false,
- modelDefaults: modelDefaults
- });
-
- me[storeName] = new Ext.data.Store(config);
- }
-
- return me[storeName];
- };
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.validations
- * @extends Object
- *
- * <p>This singleton contains a set of validation functions that can be used to validate any type
- * of data. They are most often used in {@link Ext.data.Model Models}, where they are automatically
- * set up and executed.</p>
- */
-Ext.data.validations = {
-
- /**
- * The default error message used when a presence validation fails
- * @property presenceMessage
- * @type String
- */
- presenceMessage: 'must be present',
-
- /**
- * The default error message used when a length validation fails
- * @property lengthMessage
- * @type String
- */
- lengthMessage: 'is the wrong length',
-
- /**
- * The default error message used when a format validation fails
- * @property formatMessage
- * @type Boolean
- */
- formatMessage: 'is the wrong format',
-
- /**
- * The default error message used when an inclusion validation fails
- * @property inclusionMessage
- * @type String
- */
- inclusionMessage: 'is not included in the list of acceptable values',
-
- /**
- * The default error message used when an exclusion validation fails
- * @property exclusionMessage
- * @type String
- */
- exclusionMessage: 'is not an acceptable value',
-
- /**
- * Validates that the given value is present
- * @param {Object} config Optional config object
- * @param {Mixed} value The value to validate
- * @return {Boolean} True if validation passed
- */
- presence: function(config, value) {
- if (value == undefined) {
- value = config;
- }
-
- return !!value;
- },
-
- /**
- * Returns true if the given value is between the configured min and max values
- * @param {Object} config Optional config object
- * @param {String} value The value to validate
- * @return {Boolean} True if the value passes validation
- */
- length: function(config, value) {
- if (value == undefined) {
- return false;
- }
-
- var length = value.length,
- min = config.min,
- max = config.max;
-
- if ((min && length < min) || (max && length > max)) {
- return false;
- } else {
- return true;
- }
- },
-
- /**
- * Returns true if the given value passes validation against the configured {@link #matcher} regex
- * @param {Object} config Optional config object
- * @param {String} value The value to validate
- * @return {Boolean} True if the value passes the format validation
- */
- format: function(config, value) {
- return !!(config.matcher && config.matcher.test(value));
- },
-
- /**
- * Validates that the given value is present in the configured {@link #list}
- * @param {String} value The value to validate
- * @return {Boolean} True if the value is present in the list
- */
- inclusion: function(config, value) {
- return config.list && config.list.indexOf(value) != -1;
- },
-
- /**
- * Validates that the given value is present in the configured {@link #list}
- * @param {Object} config Optional config object
- * @param {String} value The value to validate
- * @return {Boolean} True if the value is not present in the list
- */
- exclusion: function(config, value) {
- return config.list && config.list.indexOf(value) == -1;
- }
-};
-/**
- * @author Ed Spencer
- * @class Ext.data.Errors
- * @extends Ext.util.MixedCollection
- *
- * <p>Wraps a collection of validation error responses and provides convenient functions for
- * accessing and errors for specific fields.</p>
- *
- * <p>Usually this class does not need to be instantiated directly - instances are instead created
- * automatically when {@link Ext.data.Model#validate validate} on a model instance:</p>
- *
-<pre><code>
-//validate some existing model instance - in this case it returned 2 failures messages
-var errors = myModel.validate();
-
-errors.isValid(); //false
-
-errors.length; //2
-errors.getByField('name'); // [{field: 'name', error: 'must be present'}]
-errors.getByField('title'); // [{field: 'title', error: 'is too short'}]
-</code></pre>
- */
-Ext.data.Errors = Ext.extend(Ext.util.MixedCollection, {
- /**
- * Returns true if there are no errors in the collection
- * @return {Boolean}
- */
- isValid: function() {
- return this.length == 0;
- },
-
- /**
- * Returns all of the errors for the given field
- * @param {String} fieldName The field to get errors for
- * @return {Array} All errors for the given field
- */
- getByField: function(fieldName) {
- var errors = [],
- error, field, i;
-
- for (i = 0; i < this.length; i++) {
- error = this.items[i];
-
- if (error.field == fieldName) {
- errors.push(error);
- }
- }
-
- return errors;
- }
-});
-
-/**
- * @author Ed Spencer
- * @class Ext.data.Field
- * @extends Object
- *
- * <p>Fields are used to define what a Model is. They aren't instantiated directly - instead, {@link Ext#regModel}
- * creates a Field instance for each field configured in a {@link Ext.data.Model Model}. For example, we might set up a
- * model like this:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: [
- 'name', 'email',
- {name: 'age', type: 'int'},
- {name: 'gender', type: 'string', defaultValue: 'Unknown'}
- ]
-});
-</code></pre>
- *
- * <p>Four fields will have been created for the User Model - name, email, age and gender. Note that we specified a
- * couple of different formats here; if we only pass in the string name of the field (as with name and email), the
- * field is set up with the 'auto' type. It's as if we'd done this instead:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: [
- {name: 'name', type: 'auto'},
- {name: 'email', type: 'auto'},
- {name: 'age', type: 'int'},
- {name: 'gender', type: 'string', defaultValue: 'Unknown'}
- ]
-});
-</code></pre>
- *
- * <p><u>Types and conversion</u></p>
- *
- * <p>The {@link #type} is important - it's used to automatically convert data passed to the field into the correct
- * format. In our example above, the name and email fields used the 'auto' type and will just accept anything that is
- * passed into them. The 'age' field had an 'int' type however, so if we passed 25.4 this would be rounded to 25.</p>
- *
- * <p>Sometimes a simple type isn't enough, or we want to perform some processing when we load a Field's data. We can
- * do this using a {@link #convert} function. Here, we're going to create a new field based on another:</p>
- *
-<code><pre>
-Ext.regModel('User', {
- fields: [
- 'name', 'email',
- {name: 'age', type: 'int'},
- {name: 'gender', type: 'string', defaultValue: 'Unknown'},
-
- {
- name: 'firstName',
- convert: function(value, record) {
- var fullName = record.get('name'),
- splits = fullName.split(" "),
- firstName = splits[0];
-
- return firstName;
- }
- }
- ]
-});
-</code></pre>
- *
- * <p>Now when we create a new User, the firstName is populated automatically based on the name:</p>
- *
-<code><pre>
-var ed = Ext.ModelMgr.create({name: 'Ed Spencer'}, 'User');
-
-console.log(ed.get('firstName')); //logs 'Ed', based on our convert function
-</code></pre>
- *
- * <p>In fact, if we log out all of the data inside ed, we'll see this:</p>
- *
-<code><pre>
-console.log(ed.data);
-
-//outputs this:
-{
- age: 0,
- email: "",
- firstName: "Ed",
- gender: "Unknown",
- name: "Ed Spencer"
-}
-</code></pre>
- *
- * <p>The age field has been given a default of zero because we made it an int type. As an auto field, email has
- * defaulted to an empty string. When we registered the User model we set gender's {@link #defaultValue} to 'Unknown'
- * so we see that now. Let's correct that and satisfy ourselves that the types work as we expect:</p>
- *
-<code><pre>
-ed.set('gender', 'Male');
-ed.get('gender'); //returns 'Male'
-
-ed.set('age', 25.4);
-ed.get('age'); //returns 25 - we wanted an int, not a float, so no decimal places allowed
-</code></pre>
- *
- */
-Ext.data.Field = Ext.extend(Object, {
-
- constructor : function(config) {
- if (Ext.isString(config)) {
- config = {name: config};
- }
- Ext.apply(this, config);
-
- var types = Ext.data.Types,
- st = this.sortType,
- t;
-
- if (this.type) {
- if (Ext.isString(this.type)) {
- this.type = types[this.type.toUpperCase()] || types.AUTO;
- }
- } else {
- this.type = types.AUTO;
- }
-
- // named sortTypes are supported, here we look them up
- if (Ext.isString(st)) {
- this.sortType = Ext.data.SortTypes[st];
- } else if(Ext.isEmpty(st)) {
- this.sortType = this.type.sortType;
- }
-
- if (!this.convert) {
- this.convert = this.type.convert;
- }
- },
-
- /**
- * @cfg {String} name
- * The name by which the field is referenced within the Model. This is referenced by, for example,
- * the <code>dataIndex</code> property in column definition objects passed to {@link Ext.grid.ColumnModel}.
- * <p>Note: In the simplest case, if no properties other than <code>name</code> are required, a field
- * definition may consist of just a String for the field name.</p>
- */
-
- /**
- * @cfg {Mixed} type
- * (Optional) The data type for automatic conversion from received data to the <i>stored</i> value if <code>{@link Ext.data.Field#convert convert}</code>
- * has not been specified. This may be specified as a string value. Possible values are
- * <div class="mdetail-params"><ul>
- * <li>auto (Default, implies no conversion)</li>
- * <li>string</li>
- * <li>int</li>
- * <li>float</li>
- * <li>boolean</li>
- * <li>date</li></ul></div>
- * <p>This may also be specified by referencing a member of the {@link Ext.data.Types} class.</p>
- * <p>Developers may create their own application-specific data types by defining new members of the
- * {@link Ext.data.Types} class.</p>
- */
-
- /**
- * @cfg {Function} convert
- * (Optional) A function which converts the value provided by the Reader into an object that will be stored
- * in the Model. It is passed the following parameters:<div class="mdetail-params"><ul>
- * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
- * the configured <code>{@link Ext.data.Field#defaultValue defaultValue}</code>.</div></li>
- * <li><b>rec</b> : Ext.data.Model<div class="sub-desc">The data object containing the Model as read so far by the
- * Reader. Note that the Model may not be fully populated at this point as the fields are read in the order that
- * they are defined in your {@link #fields} array.</div></li>
- * </ul></div>
- * <pre><code>
-// example of convert function
-function fullName(v, record){
- return record.name.last + ', ' + record.name.first;
-}
-
-function location(v, record){
- return !record.city ? '' : (record.city + ', ' + record.state);
-}
-
-var Dude = Ext.regModel({
- fields: [
- {name: 'fullname', convert: fullName},
- {name: 'firstname', mapping: 'name.first'},
- {name: 'lastname', mapping: 'name.last'},
- {name: 'city', defaultValue: 'homeless'},
- 'state',
- {name: 'location', convert: location}
- ]
-});
-
-// create the data store
-var store = new Ext.data.Store({
- reader: {
- type: 'json',
- model: 'Dude',
- idProperty: 'key',
- root: 'daRoot',
- totalProperty: 'total'
- }
-});
-
-var myData = [
- { key: 1,
- name: { first: 'Fat', last: 'Albert' }
- // notice no city, state provided in data object
- },
- { key: 2,
- name: { first: 'Barney', last: 'Rubble' },
- city: 'Bedrock', state: 'Stoneridge'
- },
- { key: 3,
- name: { first: 'Cliff', last: 'Claven' },
- city: 'Boston', state: 'MA'
- }
-];
- * </code></pre>
- */
- /**
- * @cfg {String} dateFormat
- * <p>(Optional) Used when converting received data into a Date when the {@link #type} is specified as <code>"date"</code>.</p>
- * <p>A format string for the {@link Date#parseDate Date.parseDate} function, or "timestamp" if the
- * value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a
- * javascript millisecond timestamp. See {@link Date}</p>
- */
- dateFormat: null,
-
- /**
- * @cfg {Boolean} useNull
- * <p>(Optional) Use when converting received data into a Number type (either int or float). If the value cannot be parsed,
- * null will be used if useNull is true, otherwise the value will be 0. Defaults to <tt>false</tt>
- */
- useNull: false,
-
- /**
- * @cfg {Mixed} defaultValue
- * (Optional) The default value used <b>when a Model is being created by a {@link Ext.data.Reader Reader}</b>
- * when the item referenced by the <code>{@link Ext.data.Field#mapping mapping}</code> does not exist in the data
- * object (i.e. undefined). (defaults to "")
- */
- defaultValue: "",
- /**
- * @cfg {String/Number} mapping
- * <p>(Optional) A path expression for use by the {@link Ext.data.DataReader} implementation
- * that is creating the {@link Ext.data.Model Model} to extract the Field value from the data object.
- * If the path expression is the same as the field name, the mapping may be omitted.</p>
- * <p>The form of the mapping expression depends on the Reader being used.</p>
- * <div class="mdetail-params"><ul>
- * <li>{@link Ext.data.JsonReader}<div class="sub-desc">The mapping is a string containing the javascript
- * expression to reference the data from an element of the data item's {@link Ext.data.JsonReader#root root} Array. Defaults to the field name.</div></li>
- * <li>{@link Ext.data.XmlReader}<div class="sub-desc">The mapping is an {@link Ext.DomQuery} path to the data
- * item relative to the DOM element that represents the {@link Ext.data.XmlReader#record record}. Defaults to the field name.</div></li>
- * <li>{@link Ext.data.ArrayReader}<div class="sub-desc">The mapping is a number indicating the Array index
- * of the field's value. Defaults to the field specification's Array position.</div></li>
- * </ul></div>
- * <p>If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
- * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
- * return the desired data.</p>
- */
- mapping: null,
- /**
- * @cfg {Function} sortType
- * (Optional) A function which converts a Field's value to a comparable value in order to ensure
- * correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}. A custom
- * sort example:<pre><code>
-// current sort after sort we want
-// +-+------+ +-+------+
-// |1|First | |1|First |
-// |2|Last | |3|Second|
-// |3|Second| |2|Last |
-// +-+------+ +-+------+
-
-sortType: function(value) {
- switch (value.toLowerCase()) // native toLowerCase():
- {
- case 'first': return 1;
- case 'second': return 2;
- default: return 3;
- }
-}
- * </code></pre>
- */
- sortType : null,
- /**
- * @cfg {String} sortDir
- * (Optional) Initial direction to sort (<code>"ASC"</code> or <code>"DESC"</code>). Defaults to
- * <code>"ASC"</code>.
- */
- sortDir : "ASC",
- /**
- * @cfg {Boolean} allowBlank
- * @private
- * (Optional) Used for validating a {@link Ext.data.Model model}, defaults to <code>true</code>.
- * An empty value here will cause {@link Ext.data.Model}.{@link Ext.data.Model#isValid isValid}
- * to evaluate to <code>false</code>.
- */
- allowBlank : true
-});
-
-/**
- * @class Ext.data.SortTypes
- * @ignore
- * @singleton
- * Defines the default sorting (casting?) comparison functions used when sorting data.
- */
-Ext.data.SortTypes = {
- /**
- * Default sort that does nothing
- * @param {Mixed} s The value being converted
- * @return {Mixed} The comparison value
- */
- none : function(s) {
- return s;
- },
-
- /**
- * The regular expression used to strip tags
- * @type {RegExp}
- * @property
- */
- stripTagsRE : /<\/?[^>]+>/gi,
-
- /**
- * Strips all HTML tags to sort on text only
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asText : function(s) {
- return String(s).replace(this.stripTagsRE, "");
- },
-
- /**
- * Strips all HTML tags to sort on text only - Case insensitive
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asUCText : function(s) {
- return String(s).toUpperCase().replace(this.stripTagsRE, "");
- },
-
- /**
- * Case insensitive string
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asUCString : function(s) {
- return String(s).toUpperCase();
- },
-
- /**
- * Date sorting
- * @param {Mixed} s The value being converted
- * @return {Number} The comparison value
- */
- asDate : function(s) {
- if(!s){
- return 0;
- }
- if(Ext.isDate(s)){
- return s.getTime();
- }
- return Date.parse(String(s));
- },
-
- /**
- * Float sorting
- * @param {Mixed} s The value being converted
- * @return {Float} The comparison value
- */
- asFloat : function(s) {
- var val = parseFloat(String(s).replace(/,/g, ""));
- return isNaN(val) ? 0 : val;
- },
-
- /**
- * Integer sorting
- * @param {Mixed} s The value being converted
- * @return {Number} The comparison value
- */
- asInt : function(s) {
- var val = parseInt(String(s).replace(/,/g, ""), 10);
- return isNaN(val) ? 0 : val;
- }
-};
-/**
- * @class Ext.data.Types
- * @ignore
- * <p>This is s static class containing the system-supplied data types which may be given to a {@link Ext.data.Field Field}.<p/>
- * <p>The properties in this class are used as type indicators in the {@link Ext.data.Field Field} class, so to
- * test whether a Field is of a certain type, compare the {@link Ext.data.Field#type type} property against properties
- * of this class.</p>
- * <p>Developers may add their own application-specific data types to this class. Definition names must be UPPERCASE.
- * each type definition must contain three properties:</p>
- * <div class="mdetail-params"><ul>
- * <li><code>convert</code> : <i>Function</i><div class="sub-desc">A function to convert raw data values from a data block into the data
- * to be stored in the Field. The function is passed the collowing parameters:
- * <div class="mdetail-params"><ul>
- * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
- * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
- * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
- * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
- * ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
- * </ul></div></div></li>
- * <li><code>sortType</code> : <i>Function</i> <div class="sub-desc">A function to convert the stored data into comparable form, as defined by {@link Ext.data.SortTypes}.</div></li>
- * <li><code>type</code> : <i>String</i> <div class="sub-desc">A textual data type name.</div></li>
- * </ul></div>
- * <p>For example, to create a VELatLong field (See the Microsoft Bing Mapping API) containing the latitude/longitude value of a datapoint on a map from a JsonReader data block
- * which contained the properties <code>lat</code> and <code>long</code>, you would define a new data type like this:</p>
- *<pre><code>
-// Add a new Field data type which stores a VELatLong object in the Record.
-Ext.data.Types.VELATLONG = {
- convert: function(v, data) {
- return new VELatLong(data.lat, data.long);
- },
- sortType: function(v) {
- return v.Latitude; // When sorting, order by latitude
- },
- type: 'VELatLong'
-};
-</code></pre>
- * <p>Then, when declaring a Record, use <pre><code>
-var types = Ext.data.Types; // allow shorthand type access
-UnitRecord = Ext.data.Record.create([
- { name: 'unitName', mapping: 'UnitName' },
- { name: 'curSpeed', mapping: 'CurSpeed', type: types.INT },
- { name: 'latitude', mapping: 'lat', type: types.FLOAT },
- { name: 'latitude', mapping: 'lat', type: types.FLOAT },
- { name: 'position', type: types.VELATLONG }
-]);
-</code></pre>
- * @singleton
- */
-Ext.data.Types = new function() {
- var st = Ext.data.SortTypes;
-
- Ext.apply(this, {
- /**
- * @type Regexp
- * @property stripRe
- * A regular expression for stripping non-numeric characters from a numeric value. Defaults to <tt>/[\$,%]/g</tt>.
- * This should be overridden for localization.
- */
- stripRe: /[\$,%]/g,
-
- /**
- * @type Object.
- * @property AUTO
- * This data type means that no conversion is applied to the raw data before it is placed into a Record.
- */
- AUTO: {
- convert: function(v) {
- return v;
- },
- sortType: st.none,
- type: 'auto'
- },
-
- /**
- * @type Object.
- * @property STRING
- * This data type means that the raw data is converted into a String before it is placed into a Record.
- */
- STRING: {
- convert: function(v) {
- return (v === undefined || v === null) ? '' : String(v);
- },
- sortType: st.asUCString,
- type: 'string'
- },
-
- /**
- * @type Object.
- * @property INT
- * This data type means that the raw data is converted into an integer before it is placed into a Record.
- * <p>The synonym <code>INTEGER</code> is equivalent.</p>
- */
- INT: {
- convert: function(v) {
- return v !== undefined && v !== null && v !== '' ?
- parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
- },
- sortType: st.none,
- type: 'int'
- },
-
- /**
- * @type Object.
- * @property FLOAT
- * This data type means that the raw data is converted into a number before it is placed into a Record.
- * <p>The synonym <code>NUMBER</code> is equivalent.</p>
- */
- FLOAT: {
- convert: function(v) {
- return v !== undefined && v !== null && v !== '' ?
- parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
- },
- sortType: st.none,
- type: 'float'
- },
-
- /**
- * @type Object.
- * @property BOOL
- * <p>This data type means that the raw data is converted into a boolean before it is placed into
- * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
- * <p>The synonym <code>BOOLEAN</code> is equivalent.</p>
- */
- BOOL: {
- convert: function(v) {
- return v === true || v === 'true' || v == 1;
- },
- sortType: st.none,
- type: 'bool'
- },
-
- /**
- * @type Object.
- * @property DATE
- * This data type means that the raw data is converted into a Date before it is placed into a Record.
- * The date format is specified in the constructor of the {@link Ext.data.Field} to which this type is
- * being applied.
- */
- DATE: {
- convert: function(v) {
- var df = this.dateFormat;
- if (!v) {
- return null;
- }
- if (Ext.isDate(v)) {
- return v;
- }
- if (df) {
- if (df == 'timestamp') {
- return new Date(v*1000);
- }
- if (df == 'time') {
- return new Date(parseInt(v, 10));
- }
- return Date.parseDate(v, df);
- }
-
- var parsed = Date.parse(v);
- return parsed ? new Date(parsed) : null;
- },
- sortType: st.asDate,
- type: 'date'
- }
- });
-
- Ext.apply(this, {
- /**
- * @type Object.
- * @property BOOLEAN
- * <p>This data type means that the raw data is converted into a boolean before it is placed into
- * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
- * <p>The synonym <code>BOOL</code> is equivalent.</p>
- */
- BOOLEAN: this.BOOL,
-
- /**
- * @type Object.
- * @property INTEGER
- * This data type means that the raw data is converted into an integer before it is placed into a Record.
- * <p>The synonym <code>INT</code> is equivalent.</p>
- */
- INTEGER: this.INT,
-
- /**
- * @type Object.
- * @property NUMBER
- * This data type means that the raw data is converted into a number before it is placed into a Record.
- * <p>The synonym <code>FLOAT</code> is equivalent.</p>
- */
- NUMBER: this.FLOAT
- });
-};
-/**
- * @author Ed Spencer
- * @class Ext.ModelMgr
- * @extends Ext.AbstractManager
- * @singleton
- *
- * <p>Creates and manages the current set of models</p>
- */
-Ext.ModelMgr = new Ext.AbstractManager({
- typeName: 'mtype',
-
- /**
- * The string type of the default Model Proxy. Defaults to 'ajax'
- * @property defaultProxyType
- * @type String
- */
- defaultProxyType: 'ajax',
-
- /**
- * @property associationStack
- * @type Array
- * Private stack of associations that must be created once their associated model has been defined
- */
- associationStack: [],
-
- /**
- * Registers a model definition. All model plugins marked with isDefault: true are bootstrapped
- * immediately, as are any addition plugins defined in the model config.
- */
- registerType: function(name, config) {
- /*
- * This function does a lot. In order, it normalizes the configured associations (see the belongsTo/hasMany if blocks)
- * then looks to see if we are extending another model, in which case it copies all of the fields, validations and
- * associations from the superclass model. Once we have collected all of these configurations, the actual creation
- * is delegated to createFields and createAssociations. Finally we just link up a few convenience functions on the new model.
- */
-
- var PluginMgr = Ext.PluginMgr,
- plugins = PluginMgr.findByType('model', true),
- fields = config.fields || [],
- associations = config.associations || [],
- belongsTo = config.belongsTo,
- hasMany = config.hasMany,
- extendName = config.extend,
- modelPlugins = config.plugins || [],
- association, model, length, i,
- extendModel, extendModelProto, extendValidations, proxy;
-
- //associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
- //we support that here
- if (belongsTo) {
- if (!Ext.isArray(belongsTo)) {
- belongsTo = [belongsTo];
- }
-
- for (i = 0; i < belongsTo.length; i++) {
- association = belongsTo[i];
-
- if (!Ext.isObject(association)) {
- association = {model: association};
- }
- Ext.apply(association, {type: 'belongsTo'});
-
- associations.push(association);
- }
-
- delete config.belongsTo;
- }
-
- if (hasMany) {
- if (!Ext.isArray(hasMany)) {
- hasMany = [hasMany];
- }
-
- for (i = 0; i < hasMany.length; i++) {
- association = hasMany[i];
-
- if (!Ext.isObject(association)) {
- association = {model: association};
- }
-
- Ext.apply(association, {type: 'hasMany'});
-
- associations.push(association);
- }
-
- delete config.hasMany;
- }
-
- //if we're extending another model, inject its fields, associations and validations
- if (extendName) {
- extendModel = this.types[extendName];
- extendModelProto = extendModel.prototype;
- extendValidations = extendModelProto.validations;
-
- proxy = extendModel.proxy;
- fields = extendModelProto.fields.items.concat(fields);
- associations = extendModelProto.associations.items.concat(associations);
- config.validations = extendValidations ? extendValidations.concat(config.validations) : config.validations;
- } else {
- extendModel = Ext.data.Model;
- proxy = config.proxy;
- }
-
- model = Ext.extend(extendModel, config);
-
- for (i = 0, length = modelPlugins.length; i < length; i++) {
- plugins.push(PluginMgr.create(modelPlugins[i]));
- }
-
- this.types[name] = model;
-
- Ext.override(model, {
- plugins : plugins,
- fields : this.createFields(fields),
- associations: this.createAssociations(associations, name)
- });
-
- model.modelName = name;
- Ext.data.Model.setProxy.call(model, proxy || this.defaultProxyType);
- model.getProxy = model.prototype.getProxy;
-
- model.load = function() {
- Ext.data.Model.load.apply(this, arguments);
- };
-
- for (i = 0, length = plugins.length; i < length; i++) {
- plugins[i].bootstrap(model, config);
- }
-
- model.defined = true;
- this.onModelDefined(model);
-
- return model;
- },
-
- /**
- * @private
- * Private callback called whenever a model has just been defined. This sets up any associations
- * that were waiting for the given model to be defined
- * @param {Function} model The model that was just created
- */
- onModelDefined: function(model) {
- var stack = this.associationStack,
- length = stack.length,
- create = [],
- association, i;
-
- for (i = 0; i < length; i++) {
- association = stack[i];
-
- if (association.associatedModel == model.modelName) {
- create.push(association);
- }
- }
-
- length = create.length;
- for (i = 0; i < length; i++) {
- this.addAssociation(create[i], this.types[create[i].ownerModel].prototype.associations);
- stack.remove(create[i]);
- }
- },
-
- /**
- * @private
- * Creates and returns a MixedCollection representing the associations on a model
- * @param {Array} associations The array of Association configs
- * @param {String} name The string name of the owner model
- * @return {Ext.util.MixedCollection} The Mixed Collection
- */
- createAssociations: function(associations, name) {
- var length = associations.length,
- i, associationsMC, association;
-
- associationsMC = new Ext.util.MixedCollection(false, function(association) {
- return association.name;
- });
-
- for (i = 0; i < length; i++) {
- association = associations[i];
- Ext.apply(association, {
- ownerModel: name,
- associatedModel: association.model
- });
-
- if (this.types[association.model] == undefined) {
- this.associationStack.push(association);
- } else {
- this.addAssociation(association, associationsMC);
- }
- }
-
- return associationsMC;
- },
-
- /**
- * @private
- * Creates an Association based on config and the supplied MixedCollection. TODO: this will
- * probably need to be refactored into a more elegant solution - it was initially pulled out
- * to support deferred Association creation when the associated model has not been defined yet.
- */
- addAssociation: function(association, associationsMC) {
- var type = association.type;
-
- if (type == 'belongsTo') {
- associationsMC.add(new Ext.data.BelongsToAssociation(association));
- }
-
- if (type == 'hasMany') {
- associationsMC.add(new Ext.data.HasManyAssociation(association));
- }
-
- if (type == 'polymorphic') {
- associationsMC.add(new Ext.data.PolymorphicAssociation(association));
- }
- },
-
- /**
- * @private
- * Creates and returns a MixedCollection representing the fields in a model
- * @param {Array} fields The array of field configurations
- * @return {Ext.util.MixedCollection} The Mixed Collection
- */
- createFields: function(fields) {
- var length = fields.length,
- i, fieldsMC;
-
- fieldsMC = new Ext.util.MixedCollection(false, function(field) {
- return field.name;
- });
-
- for (i = 0; i < length; i++) {
- fieldsMC.add(new Ext.data.Field(fields[i]));
- }
-
- return fieldsMC;
- },
-
- /**
- * Returns the {@link Ext.data.Model} for a given model name
- * @param {String/Object} id The id of the model or the model instance.
- */
- getModel: function(id) {
- var model = id;
- if (typeof model == 'string') {
- model = this.types[model];
- }
- return model;
- },
-
- /**
- * Creates a new instance of a Model using the given data.
- * @param {Object} data Data to initialize the Model's fields with
- * @param {String} name The name of the model to create
- * @param {Number} id Optional unique id of the Model instance (see {@link Ext.data.Model})
- */
- create: function(config, name, id) {
- var con = typeof name == 'function' ? name : this.types[name || config.name];
-
- return new con(config, id);
- }
-});
-
-/**
- * Shorthand for {@link Ext.ModelMgr#registerType}
- * Creates a new Model class from the specified config object. See {@link Ext.data.Model} for full examples.
- *
- * @param {Object} config A configuration object for the Model you wish to create.
- * @return {Ext.data.Model} The newly registered Model
- * @member Ext
- * @method regModel
- */
-Ext.regModel = function() {
- return Ext.ModelMgr.registerType.apply(Ext.ModelMgr, arguments);
-};
-/**
- * @author Ed Spencer
- * @class Ext.data.Operation
- * @extends Object
- *
- * <p>Represents a single read or write operation performed by a {@link Ext.data.Proxy Proxy}.
- * Operation objects are used to enable communication between Stores and Proxies. Application
- * developers should rarely need to interact with Operation objects directly.</p>
- *
- * <p>Several Operations can be batched together in a {@link Ext.data.Batch batch}.</p>
- *
- * @constructor
- * @param {Object} config Optional config object
- */
-Ext.data.Operation = Ext.extend(Object, {
- /**
- * @cfg {Boolean} synchronous True if this Operation is to be executed synchronously (defaults to true). This
- * property is inspected by a {@link Ext.data.Batch Batch} to see if a series of Operations can be executed in
- * parallel or not.
- */
- synchronous: true,
-
- /**
- * @cfg {String} action The action being performed by this Operation. Should be one of 'create', 'read', 'update' or 'destroy'
- */
- action: undefined,
-
- /**
- * @cfg {Array} filters Optional array of filter objects. Only applies to 'read' actions.
- */
- filters: undefined,
-
- /**
- * @cfg {Array} sorters Optional array of sorter objects. Only applies to 'read' actions.
- */
- sorters: undefined,
-
- /**
- * @cfg {Object} group Optional grouping configuration. Only applies to 'read' actions where grouping is desired.
- */
- group: undefined,
-
- /**
- * @cfg {Number} start The start index (offset), used in paging when running a 'read' action.
- */
- start: undefined,
-
- /**
- * @cfg {Number} limit The number of records to load. Used on 'read' actions when paging is being used.
- */
- limit: undefined,
-
- /**
- * @cfg {Ext.data.Batch} batch The batch that this Operation is a part of (optional)
- */
- batch: undefined,
-
- /**
- * Read-only property tracking the start status of this Operation. Use {@link #isStarted}.
- * @property started
- * @type Boolean
- * @private
- */
- started: false,
-
- /**
- * Read-only property tracking the run status of this Operation. Use {@link #isRunning}.
- * @property running
- * @type Boolean
- * @private
- */
- running: false,
-
- /**
- * Read-only property tracking the completion status of this Operation. Use {@link #isComplete}.
- * @property complete
- * @type Boolean
- * @private
- */
- complete: false,
-
- /**
- * Read-only property tracking whether the Operation was successful or not. This starts as undefined and is set to true
- * or false by the Proxy that is executing the Operation. It is also set to false by {@link #setException}. Use
- * {@link #wasSuccessful} to query success status.
- * @property success
- * @type Boolean
- * @private
- */
- success: undefined,
-
- /**
- * Read-only property tracking the exception status of this Operation. Use {@link #hasException} and see {@link #getError}.
- * @property exception
- * @type Boolean
- * @private
- */
- exception: false,
-
- /**
- * The error object passed when {@link #setException} was called. This could be any object or primitive.
- * @property error
- * @type Mixed
- * @private
- */
- error: undefined,
-
- constructor: function(config) {
- Ext.apply(this, config || {});
- },
-
- /**
- * Marks the Operation as started
- */
- setStarted: function() {
- this.started = true;
- this.running = true;
- },
-
- /**
- * Marks the Operation as completed
- */
- setCompleted: function() {
- this.complete = true;
- this.running = false;
- },
-
- /**
- * Marks the Operation as successful
- */
- setSuccessful: function() {
- this.success = true;
- },
-
- /**
- * Marks the Operation as having experienced an exception. Can be supplied with an option error message/object.
- * @param {Mixed} error Optional error string/object
- */
- setException: function(error) {
- this.exception = true;
- this.success = false;
- this.running = false;
- this.error = error;
- },
-
- /**
- * @private
- */
- markStarted: function() {
- console.warn("Operation: markStarted has been deprecated. Please use setStarted");
- return this.setStarted();
- },
-
- /**
- * @private
- */
- markCompleted: function() {
- console.warn("Operation: markCompleted has been deprecated. Please use setCompleted");
- return this.setCompleted();
- },
-
- /**
- * @private
- */
- markSuccessful: function() {
- console.warn("Operation: markSuccessful has been deprecated. Please use setSuccessful");
- return this.setSuccessful();
- },
-
- /**
- * @private
- */
- markException: function() {
- console.warn("Operation: markException has been deprecated. Please use setException");
- return this.setException();
- },
-
- /**
- * Returns true if this Operation encountered an exception (see also {@link #getError})
- * @return {Boolean} True if there was an exception
- */
- hasException: function() {
- return this.exception === true;
- },
-
- /**
- * Returns the error string or object that was set using {@link #setException}
- * @return {Mixed} The error object
- */
- getError: function() {
- return this.error;
- },
-
- /**
- * Returns an array of Ext.data.Model instances as set by the Proxy.
- * @return {Array} Any loaded Records
- */
- getRecords: function() {
- var resultSet = this.getResultSet();
-
- return (resultSet == undefined ? this.records : resultSet.records);
- },
-
- /**
- * Returns the ResultSet object (if set by the Proxy). This object will contain the {@link Ext.data.Model model} instances
- * as well as meta data such as number of instances fetched, number available etc
- * @return {Ext.data.ResultSet} The ResultSet object
- */
- getResultSet: function() {
- return this.resultSet;
- },
-
- /**
- * Returns true if the Operation has been started. Note that the Operation may have started AND completed,
- * see {@link #isRunning} to test if the Operation is currently running.
- * @return {Boolean} True if the Operation has started
- */
- isStarted: function() {
- return this.started === true;
- },
-
- /**
- * Returns true if the Operation has been started but has not yet completed.
- * @return {Boolean} True if the Operation is currently running
- */
- isRunning: function() {
- return this.running === true;
- },
-
- /**
- * Returns true if the Operation has been completed
- * @return {Boolean} True if the Operation is complete
- */
- isComplete: function() {
- return this.complete === true;
- },
-
- /**
- * Returns true if the Operation has completed and was successful
- * @return {Boolean} True if successful
- */
- wasSuccessful: function() {
- return this.isComplete() && this.success === true;
- },
-
- /**
- * @private
- * Associates this Operation with a Batch
- * @param {Ext.data.Batch} batch The batch
- */
- setBatch: function(batch) {
- this.batch = batch;
- },
-
- /**
- * Checks whether this operation should cause writing to occur.
- * @return {Boolean} Whether the operation should cause a write to occur.
- */
- allowWrite: function() {
- return this.action != 'read';
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.ProxyMgr
- * @extends Ext.AbstractManager
- * @singleton
- * @ignore
- */
-Ext.data.ProxyMgr = new Ext.AbstractManager({
- create: function(config) {
- if (config == undefined || typeof config == 'string') {
- config = {
- type: config
- };
- }
-
- if (!(config instanceof Ext.data.Proxy)) {
- Ext.applyIf(config, {
- type : this.defaultProxyType,
- model: this.model
- });
-
- var type = config[this.typeName] || config.type,
- Constructor = this.types[type];
-
- if (Constructor == undefined) {
- throw new Error(Ext.util.Format.format("The '{0}' type has not been registered with this manager", type));
- }
-
- return new Constructor(config);
- } else {
- return config;
- }
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.ReaderMgr
- * @extends Ext.AbstractManager
- * @singleton
- * @ignore
- *
- * <p>Maintains the set of all registered {@link Ext.data.Reader Reader} types.</p>
- */
-Ext.data.ReaderMgr = new Ext.AbstractManager({
- typeName: 'rtype'
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.Request
- * @extends Object
- *
- * <p>Simple class that represents a Request that will be made by any {@link Ext.data.ServerProxy} subclass.
- * All this class does is standardize the representation of a Request as used by any ServerProxy subclass,
- * it does not contain any actual logic or perform the request itself.</p>
- *
- * @constructor
- * @param {Object} config Optional config object
- */
-Ext.data.Request = Ext.extend(Object, {
- /**
- * @cfg {String} action The name of the action this Request represents. Usually one of 'create', 'read', 'update' or 'destroy'
- */
- action: undefined,
-
- /**
- * @cfg {Object} params HTTP request params. The Proxy and its Writer have access to and can modify this object.
- */
- params: undefined,
-
- /**
- * @cfg {String} method The HTTP method to use on this Request (defaults to 'GET'). Should be one of 'GET', 'POST', 'PUT' or 'DELETE'
- */
- method: 'GET',
-
- /**
- * @cfg {String} url The url to access on this Request
- */
- url: undefined,
-
- constructor: function(config) {
- Ext.apply(this, config);
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.ResultSet
- * @extends Object
- *
- * <p>Simple wrapper class that represents a set of records returned by a Proxy.</p>
- *
- * @constructor
- * Creates the new ResultSet
- */
-Ext.data.ResultSet = Ext.extend(Object, {
- /**
- * @cfg {Boolean} loaded
- * True if the records have already been loaded. This is only meaningful when dealing with
- * SQL-backed proxies
- */
- loaded: true,
-
- /**
- * @cfg {Number} count
- * The number of records in this ResultSet. Note that total may differ from this number
- */
- count: 0,
-
- /**
- * @cfg {Number} total
- * The total number of records reported by the data source. This ResultSet may form a subset of
- * those records (see count)
- */
- total: 0,
-
- /**
- * @cfg {Boolean} success
- * True if the ResultSet loaded successfully, false if any errors were encountered
- */
- success: false,
-
- /**
- * @cfg {Array} records The array of record instances. Required
- */
-
- constructor: function(config) {
- Ext.apply(this, config);
-
- /**
- * DEPRECATED - will be removed in Ext JS 5.0. This is just a copy of this.total - use that instead
- * @property totalRecords
- * @type Mixed
- */
- this.totalRecords = this.total;
-
- if (config.count == undefined) {
- this.count = this.records.length;
- }
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.AbstractStore
- * @extends Ext.util.Observable
- *
- * <p>AbstractStore is a superclass of {@link Ext.data.Store} and {@link Ext.data.TreeStore}. It's never used directly,
- * but offers a set of methods used by both of those subclasses.</p>
- *
- * <p>We've left it here in the docs for reference purposes, but unless you need to make a whole new type of Store, what
- * you're probably looking for is {@link Ext.data.Store}. If you're still interested, here's a brief description of what
- * AbstractStore is and is not.</p>
- *
- * <p>AbstractStore provides the basic configuration for anything that can be considered a Store. It expects to be
- * given a {@link Ext.data.Model Model} that represents the type of data in the Store. It also expects to be given a
- * {@link Ext.data.Proxy Proxy} that handles the loading of data into the Store.</p>
- *
- * <p>AbstractStore provides a few helpful methods such as {@link #load} and {@link #sync}, which load and save data
- * respectively, passing the requests through the configured {@link #proxy}. Both built-in Store subclasses add extra
- * behavior to each of these functions. Note also that each AbstractStore subclass has its own way of storing data -
- * in {@link Ext.data.Store} the data is saved as a flat {@link Ext.data.MixedCollection MixedCollection}, whereas in
- * {@link Ext.data.TreeStore TreeStore} we use a {@link Ext.data.Tree} to maintain the data's hierarchy.</p>
- *
- * <p>Finally, AbstractStore provides an API for sorting and filtering data via its {@link #sorters} and {@link #filters}
- * {@link Ext.data.MixedCollection MixedCollections}. Although this functionality is provided by AbstractStore, there's a
- * good description of how to use it in the introduction of {@link Ext.data.Store}.
- *
- */
-Ext.data.AbstractStore = Ext.extend(Ext.util.Observable, {
- remoteSort : false,
- remoteFilter: false,
-
- /**
- * @cfg {String/Ext.data.Proxy/Object} proxy The Proxy to use for this Store. This can be either a string, a config
- * object or a Proxy instance - see {@link #setProxy} for details.
- */
-
- /**
- * @cfg {Boolean/Object} autoLoad If data is not specified, and if autoLoad is true or an Object, this store's load method
- * is automatically called after creation. If the value of autoLoad is an Object, this Object will be passed to the store's
- * load method. Defaults to false.
- */
- autoLoad: false,
-
- /**
- * @cfg {Boolean} autoSave True to automatically sync the Store with its Proxy after every edit to one of its Records.
- * Defaults to false.
- */
- autoSave: false,
-
- /**
- * Sets the updating behavior based on batch synchronization. 'operation' (the default) will update the Store's
- * internal representation of the data after each operation of the batch has completed, 'complete' will wait until
- * the entire batch has been completed before updating the Store's data. 'complete' is a good choice for local
- * storage proxies, 'operation' is better for remote proxies, where there is a comparatively high latency.
- * @property batchUpdateMode
- * @type String
- */
- batchUpdateMode: 'operation',
-
- /**
- * If true, any filters attached to this Store will be run after loading data, before the datachanged event is fired.
- * Defaults to true, ignored if {@link #remoteFilter} is true
- * @property filterOnLoad
- * @type Boolean
- */
- filterOnLoad: true,
-
- /**
- * If true, any sorters attached to this Store will be run after loading data, before the datachanged event is fired.
- * Defaults to true, igored if {@link #remoteSort} is true
- * @property sortOnLoad
- * @type Boolean
- */
- sortOnLoad: true,
-
- /**
- * The default sort direction to use if one is not specified (defaults to "ASC")
- * @property defaultSortDirection
- * @type String
- */
- defaultSortDirection: "ASC",
-
- /**
- * True if a model was created implicitly for this Store. This happens if a fields array is passed to the Store's constructor
- * instead of a model constructor or name.
- * @property implicitModel
- * @type Boolean
- * @private
- */
- implicitModel: false,
-
- /**
- * The string type of the Proxy to create if none is specified. This defaults to creating a {@link Ext.data.MemoryProxy memory proxy}.
- * @property defaultProxyType
- * @type String
- */
- defaultProxyType: 'memory',
-
- /**
- * True if the Store has already been destroyed via {@link #destroyStore}. If this is true, the reference to Store should be deleted
- * as it will not function correctly any more.
- * @property isDestroyed
- * @type Boolean
- */
- isDestroyed: false,
-
- isStore: true,
-
- /**
- * @cfg {String} storeId Optional unique identifier for this store. If present, this Store will be registered with
- * the {@link Ext.StoreMgr}, making it easy to reuse elsewhere. Defaults to undefined.
- */
-
- //documented above
- constructor: function(config) {
- this.addEvents(
- /**
- * @event add
- * Fired when a Model instance has been added to this Store
- * @param {Ext.data.Store} store The store
- * @param {Array} records The Model instances that were added
- * @param {Number} index The index at which the instances were inserted
- */
- 'add',
-
- /**
- * @event remove
- * Fired when a Model instance has been removed from this Store
- * @param {Ext.data.Store} store The Store object
- * @param {Ext.data.Model} record The record that was removed
- * @param {Number} index The index of the record that was removed
- */
- 'remove',
-
- /**
- * @event update
- * Fires when a Record has been updated
- * @param {Store} this
- * @param {Ext.data.Model} record The Model instance that was updated
- * @param {String} operation The update operation being performed. Value may be one of:
- * <pre><code>
- Ext.data.Model.EDIT
- Ext.data.Model.REJECT
- Ext.data.Model.COMMIT
- * </code></pre>
- */
- 'update',
-
- /**
- * @event datachanged
- * Fires whenever the records in the Store have changed in some way - this could include adding or removing records,
- * or updating the data in existing records
- * @param {Ext.data.Store} this The data store
- */
- 'datachanged',
-
- /**
- * @event beforeload
- * Event description
- * @param {Ext.data.Store} store This Store
- * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to load the Store
- */
- 'beforeload',
-
- /**
- * @event load
- * Fires whenever the store reads data from a remote data source.
- * @param {Ext.data.store} this
- * @param {Array} records An array of records
- * @param {Boolean} successful True if the operation was successful.
- */
- 'load',
-
- /**
- * @event beforesync
- * Called before a call to {@link #sync} is executed. Return false from any listener to cancel the synv
- * @param {Object} options Hash of all records to be synchronized, broken down into create, update and destroy
- */
- 'beforesync'
- );
-
- Ext.apply(this, config);
-
- /**
- * Temporary cache in which removed model instances are kept until successfully synchronised with a Proxy,
- * at which point this is cleared.
- * @private
- * @property removed
- * @type Array
- */
- this.removed = [];
-
- /**
- * Stores the current sort direction ('ASC' or 'DESC') for each field. Used internally to manage the toggling
- * of sort direction per field. Read only
- * @property sortToggle
- * @type Object
- */
- this.sortToggle = {};
-
- Ext.data.AbstractStore.superclass.constructor.apply(this, arguments);
-
- this.model = Ext.ModelMgr.getModel(config.model);
-
- /**
- * @property modelDefaults
- * @type Object
- * @private
- * A set of default values to be applied to every model instance added via {@link #insert} or created via {@link #create}.
- * This is used internally by associations to set foreign keys and other fields. See the Association classes source code
- * for examples. This should not need to be used by application developers.
- */
- Ext.applyIf(this, {
- modelDefaults: {}
- });
-
- //Supports the 3.x style of simply passing an array of fields to the store, implicitly creating a model
- if (!this.model && config.fields) {
- this.model = Ext.regModel('ImplicitModel-' + this.storeId || Ext.id(), {
- fields: config.fields
- });
-
- delete this.fields;
-
- this.implicitModel = true;
- }
-
- //ensures that the Proxy is instantiated correctly
- this.setProxy(config.proxy || this.model.proxy);
-
- if (this.id && !this.storeId) {
- this.storeId = this.id;
- delete this.id;
- }
-
- if (this.storeId) {
- Ext.StoreMgr.register(this);
- }
-
- /**
- * The collection of {@link Ext.util.Sorter Sorters} currently applied to this Store.
- * @property sorters
- * @type Ext.util.MixedCollection
- */
- this.sorters = new Ext.util.MixedCollection();
- this.sorters.addAll(this.decodeSorters(config.sorters));
-
- /**
- * The collection of {@link Ext.util.Filter Filters} currently applied to this Store
- * @property filters
- * @type Ext.util.MixedCollection
- */
- this.filters = new Ext.util.MixedCollection();
- this.filters.addAll(this.decodeFilters(config.filters));
- },
-
-
- /**
- * Sets the Store's Proxy by string, config object or Proxy instance
- * @param {String|Object|Ext.data.Proxy} proxy The new Proxy, which can be either a type string, a configuration object
- * or an Ext.data.Proxy instance
- * @return {Ext.data.Proxy} The attached Proxy object
- */
- setProxy: function(proxy) {
- if (proxy instanceof Ext.data.Proxy) {
- proxy.setModel(this.model);
- } else {
- Ext.applyIf(proxy, {
- model: this.model
- });
-
- proxy = Ext.data.ProxyMgr.create(proxy);
- }
-
- this.proxy = proxy;
-
- return this.proxy;
- },
-
- /**
- * Returns the proxy currently attached to this proxy instance
- * @return {Ext.data.Proxy} The Proxy instance
- */
- getProxy: function() {
- return this.proxy;
- },
-
- //saves any phantom records
- create: function(data, options) {
- var instance = Ext.ModelMgr.create(Ext.applyIf(data, this.modelDefaults), this.model.modelName),
- operation;
-
- options = options || {};
-
- Ext.applyIf(options, {
- action : 'create',
- records: [instance]
- });
-
- operation = new Ext.data.Operation(options);
-
- this.proxy.create(operation, this.onProxyWrite, this);
-
- return instance;
- },
-
- read: function() {
- return this.load.apply(this, arguments);
- },
-
- onProxyRead: Ext.emptyFn,
-
- update: function(options) {
- options = options || {};
-
- Ext.applyIf(options, {
- action : 'update',
- records: this.getUpdatedRecords()
- });
-
- var operation = new Ext.data.Operation(options);
-
- return this.proxy.update(operation, this.onProxyWrite, this);
- },
-
- onProxyWrite: Ext.emptyFn,
-
-
- //tells the attached proxy to destroy the given records
- destroy: function(options) {
- options = options || {};
-
- Ext.applyIf(options, {
- action : 'destroy',
- records: this.getRemovedRecords()
- });
-
- var operation = new Ext.data.Operation(options);
-
- return this.proxy.destroy(operation, this.onProxyWrite, this);
- },
-
- /**
- * @private
- * Attached as the 'operationcomplete' event listener to a proxy's Batch object. By default just calls through
- * to onProxyWrite.
- */
- onBatchOperationComplete: function(batch, operation) {
- return this.onProxyWrite(operation);
- },
-
- /**
- * @private
- * Attached as the 'complete' event listener to a proxy's Batch object. Iterates over the batch operations
- * and updates the Store's internal data MixedCollection.
- */
- onBatchComplete: function(batch, operation) {
- var operations = batch.operations,
- length = operations.length,
- i;
-
- this.suspendEvents();
-
- for (i = 0; i < length; i++) {
- this.onProxyWrite(operations[i]);
- }
-
- this.resumeEvents();
-
- this.fireEvent('datachanged', this);
- },
-
- onBatchException: function(batch, operation) {
- // //decide what to do... could continue with the next operation
- // batch.start();
- //
- // //or retry the last operation
- // batch.retry();
- },
-
- /**
- * @private
- * Filter function for new records.
- */
- filterNew: function(item) {
- return item.phantom == true || item.needsAdd == true;
- },
-
- /**
- * Returns all Model instances that are either currently a phantom (e.g. have no id), or have an ID but have not
- * yet been saved on this Store (this happens when adding a non-phantom record from another Store into this one)
- * @return {Array} The Model instances
- */
- getNewRecords: function() {
- return [];
- },
-
- /**
- * Returns all Model instances that have been updated in the Store but not yet synchronized with the Proxy
- * @return {Array} The updated Model instances
- */
- getUpdatedRecords: function() {
- return [];
- },
-
- /**
- * @private
- * Filter function for dirty records.
- */
- filterDirty: function(item) {
- return item.dirty == true;
- },
-
- //returns any records that have been removed from the store but not yet destroyed on the proxy
- getRemovedRecords: function() {
- return this.removed;
- },
-
-
- sort: function(sorters, direction) {
-
- },
-
- /**
- * @private
- * Normalizes an array of sorter objects, ensuring that they are all Ext.util.Sorter instances
- * @param {Array} sorters The sorters array
- * @return {Array} Array of Ext.util.Sorter objects
- */
- decodeSorters: function(sorters) {
- if (!Ext.isArray(sorters)) {
- if (sorters == undefined) {
- sorters = [];
- } else {
- sorters = [sorters];
- }
- }
-
- var length = sorters.length,
- Sorter = Ext.util.Sorter,
- config, i;
-
- for (i = 0; i < length; i++) {
- config = sorters[i];
-
- if (!(config instanceof Sorter)) {
- if (Ext.isString(config)) {
- config = {
- property: config
- };
- }
-
- Ext.applyIf(config, {
- root : 'data',
- direction: "ASC"
- });
-
- //support for 3.x style sorters where a function can be defined as 'fn'
- if (config.fn) {
- config.sorterFn = config.fn;
- }
-
- //support a function to be passed as a sorter definition
- if (typeof config == 'function') {
- config = {
- sorterFn: config
- };
- }
-
- sorters[i] = new Sorter(config);
- }
- }
-
- return sorters;
- },
-
- filter: function(filters, value) {
-
- },
-
- /**
- * @private
- * Creates and returns a function which sorts an array by the given field and direction
- * @param {String} field The field to create the sorter for
- * @param {String} direction The direction to sort by (defaults to "ASC")
- * @return {Function} A function which sorts by the field/direction combination provided
- */
- createSortFunction: function(field, direction) {
- direction = direction || "ASC";
- var directionModifier = direction.toUpperCase() == "DESC" ? -1 : 1;
-
- var fields = this.model.prototype.fields,
- sortType = fields.get(field).sortType;
-
- //create a comparison function. Takes 2 records, returns 1 if record 1 is greater,
- //-1 if record 2 is greater or 0 if they are equal
- return function(r1, r2) {
- var v1 = sortType(r1.data[field]),
- v2 = sortType(r2.data[field]);
-
- return directionModifier * (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
- };
- },
-
- /**
- * @private
- * Normalizes an array of filter objects, ensuring that they are all Ext.util.Filter instances
- * @param {Array} filters The filters array
- * @return {Array} Array of Ext.util.Filter objects
- */
- decodeFilters: function(filters) {
- if (!Ext.isArray(filters)) {
- if (filters == undefined) {
- filters = [];
- } else {
- filters = [filters];
- }
- }
-
- var length = filters.length,
- Filter = Ext.util.Filter,
- config, i;
-
- for (i = 0; i < length; i++) {
- config = filters[i];
-
- if (!(config instanceof Filter)) {
- Ext.apply(config, {
- root: 'data'
- });
-
- //support for 3.x style filters where a function can be defined as 'fn'
- if (config.fn) {
- config.filterFn = config.fn;
- }
-
- //support a function to be passed as a filter definition
- if (typeof config == 'function') {
- config = {
- filterFn: config
- };
- }
-
- filters[i] = new Filter(config);
- }
- }
-
- return filters;
- },
-
- clearFilter: function(supressEvent) {
-
- },
-
- isFiltered: function() {
-
- },
-
- filterBy: function(fn, scope) {
-
- },
-
-
- /**
- * Synchronizes the Store with its Proxy. This asks the Proxy to batch together any new, updated
- * and deleted records in the store, updating the Store's internal representation of the records
- * as each operation completes.
- */
- sync: function() {
- var me = this,
- options = {},
- toCreate = me.getNewRecords(),
- toUpdate = me.getUpdatedRecords(),
- toDestroy = me.getRemovedRecords(),
- needsSync = false;
-
- if (toCreate.length > 0) {
- options.create = toCreate;
- needsSync = true;
- }
-
- if (toUpdate.length > 0) {
- options.update = toUpdate;
- needsSync = true;
- }
-
- if (toDestroy.length > 0) {
- options.destroy = toDestroy;
- needsSync = true;
- }
-
- if (needsSync && me.fireEvent('beforesync', options) !== false) {
- me.proxy.batch(options, me.getBatchListeners());
- }
- },
-
-
- /**
- * @private
- * Returns an object which is passed in as the listeners argument to proxy.batch inside this.sync.
- * This is broken out into a separate function to allow for customisation of the listeners
- * @return {Object} The listeners object
- */
- getBatchListeners: function() {
- var listeners = {
- scope: this,
- exception: this.onBatchException
- };
-
- if (this.batchUpdateMode == 'operation') {
- listeners['operationcomplete'] = this.onBatchOperationComplete;
- } else {
- listeners['complete'] = this.onBatchComplete;
- }
-
- return listeners;
- },
-
- //deprecated, will be removed in 5.0
- save: function() {
- return this.sync.apply(this, arguments);
- },
-
- /**
- * Loads the Store using its configured {@link #proxy}.
- * @param {Object} options Optional config object. This is passed into the {@link Ext.data.Operation Operation}
- * object that is created and then sent to the proxy's {@link Ext.data.Proxy#read} function
- */
- load: function(options) {
- var me = this,
- operation;
-
- options = options || {};
-
- Ext.applyIf(options, {
- action : 'read',
- filters: me.filters.items,
- sorters: me.sorters.items
- });
-
- operation = new Ext.data.Operation(options);
-
- if (me.fireEvent('beforeload', me, operation) !== false) {
- me.loading = true;
- me.proxy.read(operation, me.onProxyLoad, me);
- }
-
- return me;
- },
-
- /**
- * @private
- * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
- * @param {Ext.data.Model} record The model instance that was edited
- */
- afterEdit : function(record) {
- this.fireEvent('update', this, record, Ext.data.Model.EDIT);
- },
-
- /**
- * @private
- * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to..
- * @param {Ext.data.Model} record The model instance that was edited
- */
- afterReject : function(record) {
- this.fireEvent('update', this, record, Ext.data.Model.REJECT);
- },
-
- /**
- * @private
- * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
- * @param {Ext.data.Model} record The model instance that was edited
- */
- afterCommit : function(record) {
- if (this.autoSave) {
- this.sync();
- }
-
- this.fireEvent('update', this, record, Ext.data.Model.COMMIT);
- },
-
- clearData: Ext.emptyFn,
-
- destroyStore: function() {
- if (!this.isDestroyed) {
- if (this.storeId) {
- Ext.StoreMgr.unregister(this);
- }
- this.clearData();
- this.data = null;
- this.tree = null;
- // Ext.destroy(this.proxy);
- this.reader = this.writer = null;
- this.clearListeners();
- this.isDestroyed = true;
-
- if (this.implicitModel) {
- Ext.destroy(this.model);
- }
- }
- },
-
- /**
- * Returns an object describing the current sort state of this Store.
- * @return {Object} The sort state of the Store. An object with two properties:<ul>
- * <li><b>field : String<p class="sub-desc">The name of the field by which the Records are sorted.</p></li>
- * <li><b>direction : String<p class="sub-desc">The sort order, 'ASC' or 'DESC' (case-sensitive).</p></li>
- * </ul>
- * See <tt>{@link #sortInfo}</tt> for additional details.
- */
- getSortState : function() {
- return this.sortInfo;
- },
-
- getCount: function() {
-
- },
-
- getById: function(id) {
-
- },
-
- // individual substores should implement a "fast" remove
- // and fire a clear event afterwards
- removeAll: function() {
-
- }
-});
-
-/**
- * @author Ed Spencer
- * @class Ext.data.Store
- * @extends Ext.data.AbstractStore
- *
- * <p>The Store class encapsulates a client side cache of {@link Ext.data.Model Model} objects. Stores load
- * data via a {@link Ext.data.Proxy Proxy}, and also provide functions for {@link #sort sorting},
- * {@link #filter filtering} and querying the {@link Ext.data.Model model} instances contained within it.</p>
- *
- * <p>Creating a Store is easy - we just tell it the Model and the Proxy to use to load and save its data:</p>
- *
-<pre><code>
-// Set up a {@link Ext.data.Model model} to use in our Store
-Ext.regModel('User', {
- fields: [
- {name: 'firstName', type: 'string'},
- {name: 'lastName', type: 'string'},
- {name: 'age', type: 'int'},
- {name: 'eyeColor', type: 'string'}
- ]
-});
-
-var myStore = new Ext.data.Store({
- model: 'User',
- proxy: {
- type: 'ajax',
- url : '/users.json',
- reader: {
- type: 'json',
- root: 'users'
- }
- },
- autoLoad: true
-});
-</code></pre>
-
- * <p>In the example above we configured an AJAX proxy to load data from the url '/users.json'. We told our Proxy
- * to use a {@link Ext.data.JsonReader JsonReader} to parse the response from the server into Model object -
- * {@link Ext.data.JsonReader see the docs on JsonReader} for details.</p>
- *
- * <p><u>Inline data</u></p>
- *
- * <p>Stores can also load data inline. Internally, Store converts each of the objects we pass in as {@link #data}
- * into Model instances:</p>
- *
-<pre><code>
-new Ext.data.Store({
- model: 'User',
- data : [
- {firstName: 'Ed', lastName: 'Spencer'},
- {firstName: 'Tommy', lastName: 'Maintz'},
- {firstName: 'Aaron', lastName: 'Conran'},
- {firstName: 'Jamie', lastName: 'Avins'}
- ]
-});
-</code></pre>
- *
- * <p>Loading inline data using the method above is great if the data is in the correct format already (e.g. it doesn't need
- * to be processed by a {@link Ext.data.Reader reader}). If your inline data requires processing to decode the data structure,
- * use a {@link Ext.data.MemoryProxy MemoryProxy} instead (see the {@link Ext.data.MemoryProxy MemoryProxy} docs for an example).</p>
- *
- * <p>Additional data can also be loaded locally using {@link #add}.</p>
- *
- * <p><u>Loading Nested Data</u></p>
- *
- * <p>Applications often need to load sets of associated data - for example a CRM system might load a User and her Orders.
- * Instead of issuing an AJAX request for the User and a series of additional AJAX requests for each Order, we can load a nested dataset
- * and allow the Reader to automatically populate the associated models. Below is a brief example, see the {@link Ext.data.Reader} intro
- * docs for a full explanation:</p>
- *
-<pre><code>
-var store = new Ext.data.Store({
- autoLoad: true,
- model: "User",
- proxy: {
- type: 'ajax',
- url : 'users.json',
- reader: {
- type: 'json',
- root: 'users'
- }
- }
-});
-</code></pre>
- *
- * <p>Which would consume a response like this:</p>
- *
-<pre><code>
-{
- "users": [
- {
- "id": 1,
- "name": "Ed",
- "orders": [
- {
- "id": 10,
- "total": 10.76,
- "status": "invoiced"
- },
- {
- "id": 11,
- "total": 13.45,
- "status": "shipped"
- }
- ]
- }
- ]
-}
-</code></pre>
- *
- * <p>See the {@link Ext.data.Reader} intro docs for a full explanation.</p>
- *
- * <p><u>Filtering and Sorting</u></p>
- *
- * <p>Stores can be sorted and filtered - in both cases either remotely or locally. The {@link #sorters} and {@link #filters} are
- * held inside {@link Ext.util.MixedCollection MixedCollection} instances to make them easy to manage. Usually it is sufficient to
- * either just specify sorters and filters in the Store configuration or call {@link #sort} or {@link #filter}:
- *
-<pre><code>
-var store = new Ext.data.Store({
- model: 'User',
- sorters: [
- {
- property : 'age',
- direction: 'DESC'
- },
- {
- property : 'firstName',
- direction: 'ASC'
- }
- ],
-
- filters: [
- {
- property: 'firstName',
- value : /Ed/
- }
- ]
-});
-</code></pre>
- *
- * <p>The new Store will keep the configured sorters and filters in the MixedCollection instances mentioned above. By default, sorting
- * and filtering are both performed locally by the Store - see {@link #remoteSort} and {@link #remoteFilter} to allow the server to
- * perform these operations instead.</p>
- *
- * <p>Filtering and sorting after the Store has been instantiated is also easy. Calling {@link #filter} adds another filter to the Store
- * and automatically filters the dataset (calling {@link #filter} with no arguments simply re-applies all existing filters). Note that by
- * default {@link #sortOnFilter} is set to true, which means that your sorters are automatically reapplied if using local sorting.</p>
- *
-<pre><code>
-store.filter('eyeColor', 'Brown');
-</code></pre>
- *
- * <p>Change the sorting at any time by calling {@link #sort}:</p>
- *
-<pre><code>
-store.sort('height', 'ASC');
-</code></pre>
- *
- * <p>Note that all existing sorters will be removed in favor of the new sorter data (if {@link #sort} is called with no arguments,
- * the existing sorters are just reapplied instead of being removed). To keep existing sorters and add new ones, just add them
- * to the MixedCollection:</p>
- *
-<pre><code>
-store.sorters.add(new Ext.util.Sorter({
- property : 'shoeSize',
- direction: 'ASC'
-}));
-
-store.sort();
-</code></pre>
- *
- * <p><u>Registering with StoreMgr</u></p>
- *
- * <p>Any Store that is instantiated with a {@link #storeId} will automatically be registed with the {@link Ext.StoreMgr StoreMgr}.
- * This makes it easy to reuse the same store in multiple views:</p>
- *
- <pre><code>
-//this store can be used several times
-new Ext.data.Store({
- model: 'User',
- storeId: 'usersStore'
-});
-
-new Ext.List({
- store: 'usersStore',
-
- //other config goes here
-});
-
-new Ext.DataView({
- store: 'usersStore',
-
- //other config goes here
-});
-</code></pre>
- *
- * <p><u>Further Reading</u></p>
- *
- * <p>Stores are backed up by an ecosystem of classes that enables their operation. To gain a full understanding of these
- * pieces and how they fit together, see:</p>
- *
- * <ul style="list-style-type: disc; padding-left: 25px">
- * <li>{@link Ext.data.Proxy Proxy} - overview of what Proxies are and how they are used</li>
- * <li>{@link Ext.data.Model Model} - the core class in the data package</li>
- * <li>{@link Ext.data.Reader Reader} - used by any subclass of {@link Ext.data.ServerProxy ServerProxy} to read a response</li>
- * </ul>
- *
- * @constructor
- * @param {Object} config Optional config object
- */
-Ext.data.Store = Ext.extend(Ext.data.AbstractStore, {
- /**
- * @cfg {Boolean} remoteSort
- * True to defer any sorting operation to the server. If false, sorting is done locally on the client. Defaults to <tt>false</tt>.
- */
- remoteSort: false,
-
- /**
- * @cfg {Boolean} remoteFilter
- * True to defer any filtering operation to the server. If false, filtering is done locally on the client. Defaults to <tt>false</tt>.
- */
- remoteFilter: false,
-
- /**
- * @cfg {String/Ext.data.Proxy/Object} proxy The Proxy to use for this Store. This can be either a string, a config
- * object or a Proxy instance - see {@link #setProxy} for details.
- */
-
- /**
- * @cfg {Array} data Optional array of Model instances or data objects to load locally. See "Inline data" above for details.
- */
-
- /**
- * The (optional) field by which to group data in the store. Internally, grouping is very similar to sorting - the
- * groupField and {@link #groupDir} are injected as the first sorter (see {@link #sort}). Stores support a single
- * level of grouping, and groups can be fetched via the {@link #getGroups} method.
- * @property groupField
- * @type String
- */
- groupField: undefined,
-
- /**
- * The direction in which sorting should be applied when grouping. Defaults to "ASC" - the other supported value is "DESC"
- * @property groupDir
- * @type String
- */
- groupDir: "ASC",
-
- /**
- * The number of records considered to form a 'page'. This is used to power the built-in
- * paging using the nextPage and previousPage functions. Defaults to 25.
- * @property pageSize
- * @type Number
- */
- pageSize: 25,
-
- /**
- * The page that the Store has most recently loaded (see {@link #loadPage})
- * @property currentPage
- * @type Number
- */
- currentPage: 1,
-
- /**
- * @cfg {Boolean} clearOnPageLoad True to empty the store when loading another page via {@link #loadPage},
- * {@link #nextPage} or {@link #previousPage} (defaults to true). Setting to false keeps existing records, allowing
- * large data sets to be loaded one page at a time but rendered all together.
- */
- clearOnPageLoad: true,
-
- /**
- * True if a model was created implicitly for this Store. This happens if a fields array is passed to the Store's constructor
- * instead of a model constructor or name.
- * @property implicitModel
- * @type Boolean
- * @private
- */
- implicitModel: false,
-
- /**
- * True if the Store is currently loading via its Proxy
- * @property loading
- * @type Boolean
- * @private
- */
- loading: false,
-
- /**
- * @cfg {Boolean} sortOnFilter For local filtering only, causes {@link #sort} to be called whenever {@link #filter} is called,
- * causing the sorters to be reapplied after filtering. Defaults to true
- */
- sortOnFilter: true,
-
- isStore: true,
-
- //documented above
- constructor: function(config) {
- config = config || {};
-
- /**
- * The MixedCollection that holds this store's local cache of records
- * @property data
- * @type Ext.util.MixedCollection
- */
- this.data = new Ext.util.MixedCollection(false, function(record) {
- return record.internalId;
- });
-
- if (config.data) {
- this.inlineData = config.data;
- delete config.data;
- }
-
- Ext.data.Store.superclass.constructor.call(this, config);
-
- var proxy = this.proxy,
- data = this.inlineData;
-
- if (data) {
- if (proxy instanceof Ext.data.MemoryProxy) {
- proxy.data = data;
- this.read();
- } else {
- this.add.apply(this, data);
- }
-
- this.sort();
- delete this.inlineData;
- } else if (this.autoLoad) {
- Ext.defer(this.load, 10, this, [typeof this.autoLoad == 'object' ? this.autoLoad : undefined]);
- // Remove the defer call, we may need reinstate this at some point, but currently it's not obvious why it's here.
- // this.load(typeof this.autoLoad == 'object' ? this.autoLoad : undefined);
- }
- },
-
- /**
- * Returns an object containing the result of applying grouping to the records in this store. See {@link #groupField},
- * {@link #groupDir} and {@link #getGroupString}. Example for a store containing records with a color field:
-<pre><code>
-var myStore = new Ext.data.Store({
- groupField: 'color',
- groupDir : 'DESC'
-});
-
-myStore.getGroups(); //returns:
-[
- {
- name: 'yellow',
- children: [
- //all records where the color field is 'yellow'
- ]
- },
- {
- name: 'red',
- children: [
- //all records where the color field is 'red'
- ]
- }
-]
-</code></pre>
- * @return {Array} The grouped data
- */
- getGroups: function() {
- var records = this.data.items,
- length = records.length,
- groups = [],
- pointers = {},
- record, groupStr, group, i;
-
- for (i = 0; i < length; i++) {
- record = records[i];
- groupStr = this.getGroupString(record);
- group = pointers[groupStr];
-
- if (group == undefined) {
- group = {
- name: groupStr,
- children: []
- };
-
- groups.push(group);
- pointers[groupStr] = group;
- }
-
- group.children.push(record);
- }
-
- return groups;
- },
-
- /**
- * Returns the string to group on for a given model instance. The default implementation of this method returns the model's
- * {@link #groupField}, but this can be overridden to group by an arbitrary string. For example, to group by the first letter
- * of a model's 'name' field, use the following code:
-<pre><code>
-new Ext.data.Store({
- groupDir: 'ASC',
- getGroupString: function(instance) {
- return instance.get('name')[0];
- }
-});
-</code></pre>
- * @param {Ext.data.Model} instance The model instance
- * @return {String} The string to compare when forming groups
- */
- getGroupString: function(instance) {
- return instance.get(this.groupField);
- },
-
- /**
- * Convenience function for getting the first model instance in the store
- * @return {Ext.data.Model/undefined} The first model instance in the store, or undefined
- */
- first: function() {
- return this.data.first();
- },
-
- /**
- * Convenience function for getting the last model instance in the store
- * @return {Ext.data.Model/undefined} The last model instance in the store, or undefined
- */
- last: function() {
- return this.data.last();
- },
-
- /**
- * Inserts Model instances into the Store at the given index and fires the {@link #add} event.
- * See also <code>{@link #add}</code>.
- * @param {Number} index The start index at which to insert the passed Records.
- * @param {Ext.data.Model[]} records An Array of Ext.data.Model objects to add to the cache.
- */
- insert : function(index, records) {
- var i, record, len;
-
- records = [].concat(records);
- for (i = 0, len = records.length; i < len; i++) {
- record = this.createModel(records[i]);
- record.set(this.modelDefaults);
-
- this.data.insert(index + i, record);
- record.join(this);
- }
-
- if (this.snapshot) {
- this.snapshot.addAll(records);
- }
-
- this.fireEvent('add', this, records, index);
- this.fireEvent('datachanged', this);
- },
-
- /**
- * Adds Model instances to the Store by instantiating them based on a JavaScript object. When adding already-
- * instantiated Models, use {@link #insert} instead. The instances will be added at the end of the existing collection.
- * This method accepts either a single argument array of Model instances or any number of model instance arguments.
- * Sample usage:
- *
-<pre><code>
-myStore.add({some: 'data'}, {some: 'other data'});
-</code></pre>
- *
- * @param {Object} data The data for each model
- * @return {Array} The array of newly created model instances
- */
- add: function(records) {
- //accept both a single-argument array of records, or any number of record arguments
- if (!Ext.isArray(records)) {
- records = Array.prototype.slice.apply(arguments);
- }
-
- var length = records.length,
- record, i;
-
- for (i = 0; i < length; i++) {
- record = this.createModel(records[i]);
-
- if (record.phantom == false) {
- record.needsAdd = true;
- }
-
- records[i] = record;
- }
-
- this.insert(this.data.length, records);
-
- return records;
- },
-
- /**
- * Converts a literal to a model, if it's not a model already
- * @private
- * @param record {Ext.data.Model/Object} The record to create
- * @return {Ext.data.Model}
- */
- createModel: function(record) {
- if (!(record instanceof Ext.data.Model)) {
- record = Ext.ModelMgr.create(record, this.model);
- }
-
- return record;
- },
-
- /**
- * Calls the specified function for each of the {@link Ext.data.Record Records} in the cache.
- * @param {Function} fn The function to call. The {@link Ext.data.Record Record} is passed as the first parameter.
- * Returning <tt>false</tt> aborts and exits the iteration.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed.
- * Defaults to the current {@link Ext.data.Record Record} in the iteration.
- */
- each : function(fn, scope) {
- this.data.each(fn, scope);
- },
-
- /**
- * Removes the given record from the Store, firing the 'remove' event for each instance that is removed, plus a single
- * 'datachanged' event after removal.
- * @param {Ext.data.Model/Array} records The Ext.data.Model instance or array of instances to remove
- */
- remove: function(records) {
- if (!Ext.isArray(records)) {
- records = [records];
- }
-
- var length = records.length,
- i, index, record;
-
- for (i = 0; i < length; i++) {
- record = records[i];
- index = this.data.indexOf(record);
-
- if (index > -1) {
- this.removed.push(record);
-
- if (this.snapshot) {
- this.snapshot.remove(record);
- }
-
- record.unjoin(this);
- this.data.remove(record);
-
- this.fireEvent('remove', this, record, index);
- }
- }
-
- this.fireEvent('datachanged', this);
- },
-
- /**
- * Removes the model instance at the given index
- * @param {Number} index The record index
- */
- removeAt: function(index) {
- var record = this.getAt(index);
-
- if (record) {
- this.remove(record);
- }
- },
-
- /**
- * <p>Loads data into the Store via the configured {@link #proxy}. This uses the Proxy to make an
- * asynchronous call to whatever storage backend the Proxy uses, automatically adding the retrieved
- * instances into the Store and calling an optional callback if required. Example usage:</p>
- *
-<pre><code>
-store.load({
- scope : this,
- callback: function(records, operation, success) {
- //the {@link Ext.data.Operation operation} object contains all of the details of the load operation
- console.log(records);
- }
-});
-</code></pre>
- *
- * <p>If the callback scope does not need to be set, a function can simply be passed:</p>
- *
-<pre><code>
-store.load(function(records, operation, success) {
- console.log('loaded records');
-});
-</code></pre>
- *
- * @param {Object/Function} options Optional config object, passed into the Ext.data.Operation object before loading.
- */
- load: function(options) {
- options = options || {};
-
- if (Ext.isFunction(options)) {
- options = {
- callback: options
- };
- }
-
- Ext.applyIf(options, {
- group : {field: this.groupField, direction: this.groupDir},
- start : 0,
- limit : this.pageSize,
- addRecords: false
- });
-
- return Ext.data.Store.superclass.load.call(this, options);
- },
-
- /**
- * Returns true if the Store is currently performing a load operation
- * @return {Boolean} True if the Store is currently loading
- */
- isLoading: function() {
- return this.loading;
- },
-
- /**
- * @private
- * Called internally when a Proxy has completed a load request
- */
- onProxyLoad: function(operation) {
- var records = operation.getRecords();
-
- this.loadRecords(records, operation.addRecords);
- this.loading = false;
- this.fireEvent('load', this, records, operation.wasSuccessful());
-
- //TODO: deprecate this event, it should always have been 'load' instead. 'load' is now documented, 'read' is not.
- //People are definitely using this so can't deprecate safely until 2.x
- this.fireEvent('read', this, records, operation.wasSuccessful());
-
- //this is a callback that would have been passed to the 'read' function and is optional
- var callback = operation.callback;
-
- if (typeof callback == 'function') {
- callback.call(operation.scope || this, records, operation, operation.wasSuccessful());
- }
- },
-
- /**
- * @private
- * Callback for any write Operation over the Proxy. Updates the Store's MixedCollection to reflect
- * the updates provided by the Proxy
- */
- onProxyWrite: function(operation) {
- var data = this.data,
- action = operation.action,
- records = operation.getRecords(),
- length = records.length,
- callback = operation.callback,
- record, i;
-
- if (operation.wasSuccessful()) {
- if (action == 'create' || action == 'update') {
- for (i = 0; i < length; i++) {
- record = records[i];
-
- record.phantom = false;
- record.join(this);
- data.replace(record);
- }
- }
-
- else if (action == 'destroy') {
- for (i = 0; i < length; i++) {
- record = records[i];
-
- record.unjoin(this);
- data.remove(record);
- }
-
- this.removed = [];
- }
-
- this.fireEvent('datachanged');
- }
-
- //this is a callback that would have been passed to the 'create', 'update' or 'destroy' function and is optional
- if (typeof callback == 'function') {
- callback.call(operation.scope || this, records, operation, operation.wasSuccessful());
- }
- },
-
- //inherit docs
- getNewRecords: function() {
- return this.data.filterBy(this.filterNew).items;
- },
-
- //inherit docs
- getUpdatedRecords: function() {
- return this.data.filterBy(this.filterDirty).items;
- },
-
- /**
- * <p>Sorts the data in the Store by one or more of its properties. Example usage:</p>
-<pre><code>
-//sort by a single field
-myStore.sort('myField', 'DESC');
-
-//sorting by multiple fields
-myStore.sort([
- {
- property : 'age',
- direction: 'ASC'
- },
- {
- property : 'name',
- direction: 'DESC'
- }
-]);
-</code></pre>
- * <p>Internally, Store converts the passed arguments into an array of {@link Ext.util.Sorter} instances, and delegates the actual
- * sorting to its internal {@link Ext.util.MixedCollection}.</p>
- * <p>When passing a single string argument to sort, Store maintains a ASC/DESC toggler per field, so this code:</p>
-<pre><code>
-store.sort('myField');
-store.sort('myField');
- </code></pre>
- * <p>Is equivalent to this code, because Store handles the toggling automatically:</p>
-<pre><code>
-store.sort('myField', 'ASC');
-store.sort('myField', 'DESC');
-</code></pre>
- * @param {String|Array} sorters Either a string name of one of the fields in this Store's configured {@link Ext.data.Model Model},
- * or an Array of sorter configurations.
- * @param {String} direction The overall direction to sort the data by. Defaults to "ASC".
- */
- sort: function(sorters, direction) {
- if (Ext.isString(sorters)) {
- var property = sorters,
- sortToggle = this.sortToggle,
- toggle = Ext.util.Format.toggle;
-
- if (direction == undefined) {
- sortToggle[property] = toggle(sortToggle[property] || "", "ASC", "DESC");
- direction = sortToggle[property];
- }
-
- sorters = {
- property : property,
- direction: direction
- };
- }
-
- if (arguments.length != 0) {
- this.sorters.clear();
- }
-
- this.sorters.addAll(this.decodeSorters(sorters));
-
- if (this.remoteSort) {
- //the load function will pick up the new sorters and request the sorted data from the proxy
- this.load();
- } else {
- this.data.sort(this.sorters.items);
-
- this.fireEvent('datachanged', this);
- }
- },
-
-
- /**
- * Filters the loaded set of records by a given set of filters.
- * @param {Mixed} filters The set of filters to apply to the data. These are stored internally on the store,
- * but the filtering itself is done on the Store's {@link Ext.util.MixedCollection MixedCollection}. See
- * MixedCollection's {@link Ext.util.MixedCollection#filter filter} method for filter syntax. Alternatively,
- * pass in a property string
- * @param {String} value Optional value to filter by (only if using a property string as the first argument)
- */
- filter: function(filters, value) {
- if (Ext.isString(filters)) {
- filters = {
- property: filters,
- value : value
- };
- }
-
- this.filters.addAll(this.decodeFilters(filters));
-
- if (this.remoteFilter) {
- //the load function will pick up the new filters and request the filtered data from the proxy
- this.load();
- } else {
- /**
- * A pristine (unfiltered) collection of the records in this store. This is used to reinstate
- * records when a filter is removed or changed
- * @property snapshot
- * @type Ext.util.MixedCollection
- */
- this.snapshot = this.snapshot || this.data.clone();
-
- this.data = this.data.filter(this.filters.items);
-
- if (this.sortOnFilter && !this.remoteSort) {
- this.sort();
- } else {
- this.fireEvent('datachanged', this);
- }
- }
- },
-
- /**
- * Revert to a view of the Record cache with no filtering applied.
- * @param {Boolean} suppressEvent If <tt>true</tt> the filter is cleared silently without firing the
- * {@link #datachanged} event.
- */
- clearFilter : function(suppressEvent) {
- this.filters.clear();
-
- if (this.isFiltered()) {
- this.data = this.snapshot.clone();
- delete this.snapshot;
-
- if (suppressEvent !== true) {
- this.fireEvent('datachanged', this);
- }
- }
- },
-
- /**
- * Returns true if this store is currently filtered
- * @return {Boolean}
- */
- isFiltered : function() {
- return !!this.snapshot && this.snapshot != this.data;
- },
-
- /**
- * Filter by a function. The specified function will be called for each
- * Record in this Store. If the function returns <tt>true</tt> the Record is included,
- * otherwise it is filtered out.
- * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
- * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
- * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
- * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
- * </ul>
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
- */
- filterBy : function(fn, scope) {
- this.snapshot = this.snapshot || this.data.clone();
- this.data = this.queryBy(fn, scope || this);
- this.fireEvent('datachanged', this);
- },
-
- /**
- * Query the cached records in this Store using a filtering function. The specified function
- * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
- * included in the results.
- * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
- * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
- * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
- * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
- * </ul>
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
- * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
- **/
- queryBy : function(fn, scope) {
- var data = this.snapshot || this.data;
- return data.filterBy(fn, scope||this);
- },
-
- /**
- * Loads an array of data straight into the Store
- * @param {Array} data Array of data to load. Any non-model instances will be cast into model instances first
- * @param {Boolean} append True to add the records to the existing records in the store, false to remove the old ones first
- */
- loadData: function(data, append) {
- var model = this.model,
- length = data.length,
- i, record;
-
- //make sure each data element is an Ext.data.Model instance
- for (i = 0; i < length; i++) {
- record = data[i];
-
- if (!(record instanceof Ext.data.Model)) {
- data[i] = Ext.ModelMgr.create(record, model);
- }
- }
-
- this.loadRecords(data, append);
- },
-
- /**
- * Loads an array of {@Ext.data.Model model} instances into the store, fires the datachanged event. This should only usually
- * be called internally when loading from the {@link Ext.data.Proxy Proxy}, when adding records manually use {@link #add} instead
- * @param {Array} records The array of records to load
- * @param {Boolean} add True to add these records to the existing records, false to remove the Store's existing records first
- */
- loadRecords: function(records, add) {
- if (!add) {
- this.data.clear();
- }
-
- this.data.addAll(records);
-
- //FIXME: this is not a good solution. Ed Spencer is totally responsible for this and should be forced to fix it immediately.
- for (var i = 0, length = records.length; i < length; i++) {
- records[i].needsAdd = false;
- records[i].join(this);
- }
-
- /*
- * this rather inelegant suspension and resumption of events is required because both the filter and sort functions
- * fire an additional datachanged event, which is not wanted. Ideally we would do this a different way. The first
- * datachanged event is fired by the call to this.add, above.
- */
- this.suspendEvents();
-
- if (this.filterOnLoad && !this.remoteFilter) {
- this.filter();
- }
-
- if (this.sortOnLoad && !this.remoteSort) {
- this.sort();
- }
-
- this.resumeEvents();
- this.fireEvent('datachanged', this, records);
- },
-
- // PAGING METHODS
-
- /**
- * Loads a given 'page' of data by setting the start and limit values appropriately. Internally this just causes a normal
- * load operation, passing in calculated 'start' and 'limit' params
- * @param {Number} page The number of the page to load
- */
- loadPage: function(page) {
- this.currentPage = page;
-
- this.read({
- page : page,
- start: (page - 1) * this.pageSize,
- limit: this.pageSize,
- addRecords: !this.clearOnPageLoad
- });
- },
-
- /**
- * Loads the next 'page' in the current data set
- */
- nextPage: function() {
- this.loadPage(this.currentPage + 1);
- },
-
- /**
- * Loads the previous 'page' in the current data set
- */
- previousPage: function() {
- this.loadPage(this.currentPage - 1);
- },
-
- // private
- clearData: function(){
- this.data.each(function(record) {
- record.unjoin();
- });
-
- this.data.clear();
- },
-
- /**
- * Finds the index of the first matching Record in this store by a specific field value.
- * @param {String} fieldName The name of the Record field to test.
- * @param {String/RegExp} value Either a string that the field value
- * should begin with, or a RegExp to test against the field.
- * @param {Number} startIndex (optional) The index to start searching at
- * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
- * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
- * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
- * @return {Number} The matched index or -1
- */
- find : function(property, value, start, anyMatch, caseSensitive, exactMatch) {
- var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
- return fn ? this.data.findIndexBy(fn, null, start) : -1;
- },
-
- /**
- * Finds the first matching Record in this store by a specific field value.
- * @param {String} fieldName The name of the Record field to test.
- * @param {String/RegExp} value Either a string that the field value
- * should begin with, or a RegExp to test against the field.
- * @param {Number} startIndex (optional) The index to start searching at
- * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
- * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
- * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
- * @return {Ext.data.Record} The matched record or null
- */
- findRecord : function() {
- var index = this.find.apply(this, arguments);
- return index != -1 ? this.getAt(index) : null;
- },
-
- /**
- * @private
- * Returns a filter function used to test a the given property's value. Defers most of the work to
- * Ext.util.MixedCollection's createValueMatcher function
- * @param {String} property The property to create the filter function for
- * @param {String/RegExp} value The string/regex to compare the property value to
- * @param {Boolean} anyMatch True if we don't care if the filter value is not the full value (defaults to false)
- * @param {Boolean} caseSensitive True to create a case-sensitive regex (defaults to false)
- * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
- * Ignored if anyMatch is true.
- */
- createFilterFn : function(property, value, anyMatch, caseSensitive, exactMatch) {
- if(Ext.isEmpty(value)){
- return false;
- }
- value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
- return function(r) {
- return value.test(r.data[property]);
- };
- },
-
- /**
- * Finds the index of the first matching Record in this store by a specific field value.
- * @param {String} fieldName The name of the Record field to test.
- * @param {Mixed} value The value to match the field against.
- * @param {Number} startIndex (optional) The index to start searching at
- * @return {Number} The matched index or -1
- */
- findExact: function(property, value, start) {
- return this.data.findIndexBy(function(rec){
- return rec.get(property) === value;
- }, this, start);
- },
-
- /**
- * Find the index of the first matching Record in this Store by a function.
- * If the function returns <tt>true</tt> it is considered a match.
- * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
- * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
- * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
- * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
- * </ul>
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
- * @param {Number} startIndex (optional) The index to start searching at
- * @return {Number} The matched index or -1
- */
- findBy : function(fn, scope, start) {
- return this.data.findIndexBy(fn, scope, start);
- },
-
- /**
- * Collects unique values for a particular dataIndex from this store.
- * @param {String} dataIndex The property to collect
- * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
- * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
- * @return {Array} An array of the unique values
- **/
- collect : function(dataIndex, allowNull, bypassFilter) {
- var values = [],
- uniques = {},
- length, value, strValue, data, i;
-
- if (bypassFilter === true && this.snapshot) {
- data = this.snapshot.items;
- } else {
- data = this.data.items;
- }
-
- length = data.length;
-
- for (i = 0; i < length; i++) {
- value = data[i].data[dataIndex];
- strValue = String(value);
-
- if ((allowNull || !Ext.isEmpty(value)) && !uniques[strValue]) {
- uniques[strValue] = true;
- values[values.length] = value;
- }
- }
-
- return values;
- },
-
- /**
- * Sums the value of <tt>property</tt> for each {@link Ext.data.Record record} between <tt>start</tt>
- * and <tt>end</tt> and returns the result.
- * @param {String} property A field in each record
- * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
- * @param {Number} end (optional) The last record index to include (defaults to length - 1)
- * @return {Number} The sum
- */
- sum : function(property, start, end) {
- var records = this.data.items,
- value = 0,
- i;
-
- start = start || 0;
- end = (end || end === 0) ? end : records.length - 1;
-
- for (i = start; i <= end; i++) {
- value += (records[i].data[property] || 0);
- }
-
- return value;
- },
-
- /**
- * Gets the number of cached records.
- * <p>If using paging, this may not be the total size of the dataset. If the data object
- * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
- * the dataset size. <b>Note</b>: see the Important note in {@link #load}.</p>
- * @return {Number} The number of Records in the Store's cache.
- */
- getCount : function() {
- return this.data.length || 0;
- },
-
- /**
- * Get the Record at the specified index.
- * @param {Number} index The index of the Record to find.
- * @return {Ext.data.Model} The Record at the passed index. Returns undefined if not found.
- */
- getAt : function(index) {
- return this.data.getAt(index);
- },
-
- /**
- * Returns a range of Records between specified indices.
- * @param {Number} startIndex (optional) The starting index (defaults to 0)
- * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
- * @return {Ext.data.Model[]} An array of Records
- */
- getRange : function(start, end) {
- return this.data.getRange(start, end);
- },
-
- /**
- * Get the Record with the specified id.
- * @param {String} id The id of the Record to find.
- * @return {Ext.data.Record} The Record with the passed id. Returns undefined if not found.
- */
- getById : function(id) {
- return (this.snapshot || this.data).findBy(function(record) {
- return record.getId() === id;
- });
- },
-
- /**
- * Get the index within the cache of the passed Record.
- * @param {Ext.data.Model} record The Ext.data.Model object to find.
- * @return {Number} The index of the passed Record. Returns -1 if not found.
- */
- indexOf : function(record) {
- return this.data.indexOf(record);
- },
-
- /**
- * Get the index within the cache of the Record with the passed id.
- * @param {String} id The id of the Record to find.
- * @return {Number} The index of the Record. Returns -1 if not found.
- */
- indexOfId : function(id) {
- return this.data.indexOfKey(id);
- },
-
- removeAll: function(silent) {
- var items = [];
- this.each(function(rec){
- items.push(rec);
- });
- this.clearData();
- if(this.snapshot){
- this.snapshot.clear();
- }
- //if(this.pruneModifiedRecords){
- // this.modified = [];
- //}
- if (silent !== true) {
- this.fireEvent('clear', this, items);
- }
- }
-});
-
-/**
- * @author Aaron Conran
- * @class Ext.data.TreeStore
- * @extends Ext.data.AbstractStore
- *
- * <p>A store class that allows the representation of hierarchical data.</p>
- * @constructor
- * @param {Object} config Optional config object
- */
-Ext.data.TreeStore = Ext.extend(Ext.data.AbstractStore, {
- /**
- * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
- * child nodes before loading.
- */
- clearOnLoad : true,
-
- /**
- * @cfg {String} nodeParam The name of the parameter sent to the server which contains
- * the identifier of the node. Defaults to <tt>'node'</tt>.
- */
- nodeParam: 'node',
-
- /**
- * @cfg {String} defaultRootId
- * The default root id. Defaults to 'root'
- */
- defaultRootId: 'root',
-
- constructor: function(config) {
- config = config || {};
- var rootCfg = config.root || {};
- rootCfg.id = rootCfg.id || this.defaultRootId;
-
- // create a default rootNode and create internal data struct.
- var rootNode = new Ext.data.RecordNode(rootCfg);
- this.tree = new Ext.data.Tree(rootNode);
- this.tree.treeStore = this;
-
- Ext.data.TreeStore.superclass.constructor.call(this, config);
-
-
- if (config.root) {
- this.read({
- node: rootNode,
- doPreload: true
- });
- }
- },
-
-
- /**
- * Returns the root node for this tree.
- * @return {Ext.data.RecordNode}
- */
- getRootNode: function() {
- return this.tree.getRootNode();
- },
-
- /**
- * Returns the record node by id
- * @return {Ext.data.RecordNode}
- */
- getNodeById: function(id) {
- return this.tree.getNodeById(id);
- },
-
-
- // new options are
- // * node - a node within the tree
- // * doPreload - private option used to preload existing childNodes
- load: function(options) {
- options = options || {};
- options.params = options.params || {};
-
- var node = options.node || this.tree.getRootNode(),
- records,
- record,
- reader = this.proxy.reader,
- root;
-
- if (this.clearOnLoad) {
- while (node.firstChild){
- node.removeChild(node.firstChild);
- }
- }
-
- if (!options.doPreload) {
- Ext.applyIf(options, {
- node: node
- });
- record = node.getRecord();
- options.params[this.nodeParam] = record ? record.getId() : 'root';
-
- return Ext.data.TreeStore.superclass.load.call(this, options);
- } else {
- root = reader.getRoot(node.isRoot ? node.attributes : node.getRecord().raw);
- records = reader.extractData(root, true);
- this.fillNode(node, records);
- return true;
- }
- },
-
- // @private
- // fills an Ext.data.RecordNode with records
- fillNode: function(node, records) {
- node.loaded = true;
- var ln = records.length,
- recordNode,
- i = 0,
- raw,
- subStore = node.subStore;
-
- for (; i < ln; i++) {
- raw = records[i].raw;
- records[i].data.leaf = raw.leaf;
- recordNode = new Ext.data.RecordNode({
- id: records[i].getId(),
- leaf: raw.leaf,
- record: records[i],
- expanded: raw.expanded
- });
- node.appendChild(recordNode);
- if (records[i].doPreload) {
- this.load({
- node: recordNode,
- doPreload: true
- });
- }
- }
-
- // maintain the subStore if its already been created
- if (subStore) {
- if (this.clearOnLoad) {
- subStore.removeAll();
- }
- subStore.add.apply(subStore, records);
- }
- },
-
-
- onProxyLoad: function(operation) {
- var records = operation.getRecords();
-
- this.fillNode(operation.node, records);
-
- this.fireEvent('read', this, operation.node, records, operation.wasSuccessful());
- //this is a callback that would have been passed to the 'read' function and is optional
- var callback = operation.callback;
- if (typeof callback == 'function') {
- callback.call(operation.scope || this, records, operation, operation.wasSuccessful());
- }
- },
-
-
- /**
- * Returns a flat Ext.data.Store with the correct type of model.
- * @param {Ext.data.RecordNode/Ext.data.Record} record
- * @returns Ext.data.Store
- */
- getSubStore: function(node) {
- // Remap Record to RecordNode
- if (node && node.node) {
- node = node.node;
- }
- return node.getSubStore();
- },
-
-
- removeAll: function() {
- var rootNode = this.getRootNode();
- rootNode.destroy();
- }
-});
-
-/**
- * @class Ext.StoreMgr
- * @extends Ext.util.MixedCollection
- * The default global group of stores.
- * @singleton
- * TODO: Make this an AbstractMgr
- */
-Ext.StoreMgr = Ext.apply(new Ext.util.MixedCollection(), {
- /**
- * @cfg {Object} listeners @hide
- */
-
- /**
- * Registers one or more Stores with the StoreMgr. You do not normally need to register stores
- * manually. Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered.
- * @param {Ext.data.Store} store1 A Store instance
- * @param {Ext.data.Store} store2 (optional)
- * @param {Ext.data.Store} etc... (optional)
- */
- register : function() {
- for (var i = 0, s; (s = arguments[i]); i++) {
- this.add(s);
- }
- },
-
- /**
- * Unregisters one or more Stores with the StoreMgr
- * @param {String/Object} id1 The id of the Store, or a Store instance
- * @param {String/Object} id2 (optional)
- * @param {String/Object} etc... (optional)
- */
- unregister : function() {
- for (var i = 0, s; (s = arguments[i]); i++) {
- this.remove(this.lookup(s));
- }
- },
-
- /**
- * Gets a registered Store by id
- * @param {String/Object} id The id of the Store, or a Store instance
- * @return {Ext.data.Store}
- */
- lookup : function(id) {
- if (Ext.isArray(id)) {
- var fields = ['field1'], expand = !Ext.isArray(id[0]);
- if(!expand){
- for(var i = 2, len = id[0].length; i <= len; ++i){
- fields.push('field' + i);
- }
- }
- return new Ext.data.ArrayStore({
- data : id,
- fields: fields,
- expandData : expand,
- autoDestroy: true,
- autoCreated: true
- });
- }
- return Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
- },
-
- // getKey implementation for MixedCollection
- getKey : function(o) {
- return o.storeId;
- }
-});
-
-/**
- * <p>Creates a new store for the given id and config, then registers it with the {@link Ext.StoreMgr Store Mananger}.
- * Sample usage:</p>
-<pre><code>
-Ext.regStore('AllUsers', {
- model: 'User'
-});
-
-//the store can now easily be used throughout the application
-new Ext.List({
- store: 'AllUsers',
- ... other config
-});
-</code></pre>
- * @param {String} id The id to set on the new store
- * @param {Object} config The store config
- * @param {Constructor} cls The new Component class.
- * @member Ext
- * @method regStore
- */
-Ext.regStore = function(name, config) {
- var store;
-
- if (Ext.isObject(name)) {
- config = name;
- } else {
- config.storeId = name;
- }
-
- if (config instanceof Ext.data.Store) {
- store = config;
- } else {
- store = new Ext.data.Store(config);
- }
-
- return Ext.StoreMgr.register(store);
-};
-
-/**
- * Gets a registered Store by id (shortcut to {@link #lookup})
- * @param {String/Object} id The id of the Store, or a Store instance
- * @return {Ext.data.Store}
- * @member Ext
- * @method getStore
- */
-Ext.getStore = function(name) {
- return Ext.StoreMgr.lookup(name);
-};
-
-/**
- * @author Ed Spencer
- * @class Ext.data.WriterMgr
- * @extends Ext.AbstractManager
- * @ignore
- *
- * <p>Keeps track of all of the registered {@link Ext.data.Writer Writers}</p>
- */
-Ext.data.WriterMgr = new Ext.AbstractManager({
-
-});
-/**
- * @class Ext.data.Tree
- * @extends Ext.util.Observable
- * Represents a tree data structure and bubbles all the events for its nodes. The nodes
- * in the tree have most standard DOM functionality.
- * @constructor
- * @param {Node} root (optional) The root node
- */
-Ext.data.Tree = Ext.extend(Ext.util.Observable, {
-
- constructor: function(root) {
- this.nodeHash = {};
-
- /**
- * The root node for this tree
- * @type Node
- */
- this.root = null;
-
- if (root) {
- this.setRootNode(root);
- }
-
- this.addEvents(
- /**
- * @event append
- * Fires when a new child node is appended to a node in this tree.
- * @param {Tree} tree The owner tree
- * @param {Node} parent The parent node
- * @param {Node} node The newly appended node
- * @param {Number} index The index of the newly appended node
- */
- "append",
-
- /**
- * @event remove
- * Fires when a child node is removed from a node in this tree.
- * @param {Tree} tree The owner tree
- * @param {Node} parent The parent node
- * @param {Node} node The child node removed
- */
- "remove",
-
- /**
- * @event move
- * Fires when a node is moved to a new location in the tree
- * @param {Tree} tree The owner tree
- * @param {Node} node The node moved
- * @param {Node} oldParent The old parent of this node
- * @param {Node} newParent The new parent of this node
- * @param {Number} index The index it was moved to
- */
- "move",
-
- /**
- * @event insert
- * Fires when a new child node is inserted in a node in this tree.
- * @param {Tree} tree The owner tree
- * @param {Node} parent The parent node
- * @param {Node} node The child node inserted
- * @param {Node} refNode The child node the node was inserted before
- */
- "insert",
-
- /**
- * @event beforeappend
- * Fires before a new child is appended to a node in this tree, return false to cancel the append.
- * @param {Tree} tree The owner tree
- * @param {Node} parent The parent node
- * @param {Node} node The child node to be appended
- */
- "beforeappend",
-
- /**
- * @event beforeremove
- * Fires before a child is removed from a node in this tree, return false to cancel the remove.
- * @param {Tree} tree The owner tree
- * @param {Node} parent The parent node
- * @param {Node} node The child node to be removed
- */
- "beforeremove",
-
- /**
- * @event beforemove
- * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
- * @param {Tree} tree The owner tree
- * @param {Node} node The node being moved
- * @param {Node} oldParent The parent of the node
- * @param {Node} newParent The new parent the node is moving to
- * @param {Number} index The index it is being moved to
- */
- "beforemove",
-
- /**
- * @event beforeinsert
- * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
- * @param {Tree} tree The owner tree
- * @param {Node} parent The parent node
- * @param {Node} node The child node to be inserted
- * @param {Node} refNode The child node the node is being inserted before
- */
- "beforeinsert"
- );
-
- Ext.data.Tree.superclass.constructor.call(this);
- },
-
- /**
- * @cfg {String} pathSeparator
- * The token used to separate paths in node ids (defaults to '/').
- */
- pathSeparator: "/",
-
- // private
- proxyNodeEvent : function(){
- return this.fireEvent.apply(this, arguments);
- },
-
- /**
- * Returns the root node for this tree.
- * @return {Node}
- */
- getRootNode : function() {
- return this.root;
- },
-
- /**
- * Sets the root node for this tree.
- * @param {Node} node
- * @return {Node}
- */
- setRootNode : function(node) {
- this.root = node;
- node.ownerTree = this;
- node.isRoot = true;
- this.registerNode(node);
- return node;
- },
-
- /**
- * Gets a node in this tree by its id.
- * @param {String} id
- * @return {Node}
- */
- getNodeById : function(id) {
- return this.nodeHash[id];
- },
-
- // private
- registerNode : function(node) {
- this.nodeHash[node.id] = node;
- },
-
- // private
- unregisterNode : function(node) {
- delete this.nodeHash[node.id];
- },
-
- toString : function() {
- return "[Tree"+(this.id?" "+this.id:"")+"]";
- }
-});
-
-/**
- * @class Ext.data.Node
- * @extends Ext.util.Observable
- * @cfg {Boolean} leaf true if this node is a leaf and does not have children
- * @cfg {String} id The id for this node. If one is not specified, one is generated.
- * @constructor
- * @param {Object} attributes The attributes/config for the node
- */
-Ext.data.Node = Ext.extend(Ext.util.Observable, {
-
- constructor: function(attributes) {
- /**
- * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
- * @type {Object}
- */
- this.attributes = attributes || {};
-
- this.leaf = !!this.attributes.leaf;
-
- /**
- * The node id. @type String
- */
- this.id = this.attributes.id;
-
- if (!this.id) {
- this.id = Ext.id(null, "xnode-");
- this.attributes.id = this.id;
- }
- /**
- * All child nodes of this node. @type Array
- */
- this.childNodes = [];
-
- /**
- * The parent node for this node. @type Node
- */
- this.parentNode = null;
-
- /**
- * The first direct child node of this node, or null if this node has no child nodes. @type Node
- */
- this.firstChild = null;
-
- /**
- * The last direct child node of this node, or null if this node has no child nodes. @type Node
- */
- this.lastChild = null;
-
- /**
- * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
- */
- this.previousSibling = null;
-
- /**
- * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
- */
- this.nextSibling = null;
-
- this.addEvents({
- /**
- * @event append
- * Fires when a new child node is appended
- * @param {Tree} tree The owner tree
- * @param {Node} this This node
- * @param {Node} node The newly appended node
- * @param {Number} index The index of the newly appended node
- */
- "append" : true,
-
- /**
- * @event remove
- * Fires when a child node is removed
- * @param {Tree} tree The owner tree
- * @param {Node} this This node
- * @param {Node} node The removed node
- */
- "remove" : true,
-
- /**
- * @event move
- * Fires when this node is moved to a new location in the tree
- * @param {Tree} tree The owner tree
- * @param {Node} this This node
- * @param {Node} oldParent The old parent of this node
- * @param {Node} newParent The new parent of this node
- * @param {Number} index The index it was moved to
- */
- "move" : true,
-
- /**
- * @event insert
- * Fires when a new child node is inserted.
- * @param {Tree} tree The owner tree
- * @param {Node} this This node
- * @param {Node} node The child node inserted
- * @param {Node} refNode The child node the node was inserted before
- */
- "insert" : true,
-
- /**
- * @event beforeappend
- * Fires before a new child is appended, return false to cancel the append.
- * @param {Tree} tree The owner tree
- * @param {Node} this This node
- * @param {Node} node The child node to be appended
- */
- "beforeappend" : true,
-
- /**
- * @event beforeremove
- * Fires before a child is removed, return false to cancel the remove.
- * @param {Tree} tree The owner tree
- * @param {Node} this This node
- * @param {Node} node The child node to be removed
- */
- "beforeremove" : true,
-
- /**
- * @event beforemove
- * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
- * @param {Tree} tree The owner tree
- * @param {Node} this This node
- * @param {Node} oldParent The parent of this node
- * @param {Node} newParent The new parent this node is moving to
- * @param {Number} index The index it is being moved to
- */
- "beforemove" : true,
-
- /**
- * @event beforeinsert
- * Fires before a new child is inserted, return false to cancel the insert.
- * @param {Tree} tree The owner tree
- * @param {Node} this This node
- * @param {Node} node The child node to be inserted
- * @param {Node} refNode The child node the node is being inserted before
- */
- "beforeinsert" : true
- });
-
- this.listeners = this.attributes.listeners;
- Ext.data.Node.superclass.constructor.call(this);
- },
-
- // private
- fireEvent : function(evtName) {
- // first do standard event for this node
- if (Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false) {
- return false;
- }
-
- // then bubble it up to the tree if the event wasn't cancelled
- var ot = this.getOwnerTree();
- if (ot) {
- if (ot.proxyNodeEvent.apply(ot, arguments) === false) {
- return false;
- }
- }
- return true;
- },
-
- /**
- * Returns true if this node is a leaf
- * @return {Boolean}
- */
- isLeaf : function() {
- return this.leaf === true;
- },
-
- // private
- setFirstChild : function(node) {
- this.firstChild = node;
- },
-
- //private
- setLastChild : function(node) {
- this.lastChild = node;
- },
-
-
- /**
- * Returns true if this node is the last child of its parent
- * @return {Boolean}
- */
- isLast : function() {
- return (!this.parentNode ? true : this.parentNode.lastChild == this);
- },
-
- /**
- * Returns true if this node is the first child of its parent
- * @return {Boolean}
- */
- isFirst : function() {
- return (!this.parentNode ? true : this.parentNode.firstChild == this);
- },
-
- /**
- * Returns true if this node has one or more child nodes, else false.
- * @return {Boolean}
- */
- hasChildNodes : function() {
- return !this.isLeaf() && this.childNodes.length > 0;
- },
-
- /**
- * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
- * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
- * @return {Boolean}
- */
- isExpandable : function() {
- return this.attributes.expandable || this.hasChildNodes();
- },
-
- /**
- * Insert node(s) as the last child node of this node.
- * @param {Node/Array} node The node or Array of nodes to append
- * @return {Node} The appended node if single append, or null if an array was passed
- */
- appendChild : function(node) {
- var multi = false,
- i, len;
-
- if (Ext.isArray(node)) {
- multi = node;
- } else if (arguments.length > 1) {
- multi = arguments;
- }
-
- // if passed an array or multiple args do them one by one
- if (multi) {
- len = multi.length;
-
- for (i = 0; i < len; i++) {
- this.appendChild(multi[i]);
- }
- } else {
- if (this.fireEvent("beforeappend", this.ownerTree, this, node) === false) {
- return false;
- }
-
- var index = this.childNodes.length;
- var oldParent = node.parentNode;
-
- // it's a move, make sure we move it cleanly
- if (oldParent) {
- if (node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false) {
- return false;
- }
- oldParent.removeChild(node);
- }
-
- index = this.childNodes.length;
- if (index === 0) {
- this.setFirstChild(node);
- }
-
- this.childNodes.push(node);
- node.parentNode = this;
- var ps = this.childNodes[index-1];
- if (ps) {
- node.previousSibling = ps;
- ps.nextSibling = node;
- } else {
- node.previousSibling = null;
- }
-
- node.nextSibling = null;
- this.setLastChild(node);
- node.setOwnerTree(this.getOwnerTree());
- this.fireEvent("append", this.ownerTree, this, node, index);
-
- if (oldParent) {
- node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
- }
-
- return node;
- }
- },
-
- /**
- * Removes a child node from this node.
- * @param {Node} node The node to remove
- * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
- * @return {Node} The removed node
- */
- removeChild : function(node, destroy) {
- var index = this.indexOf(node);
-
- if (index == -1) {
- return false;
- }
- if (this.fireEvent("beforeremove", this.ownerTree, this, node) === false) {
- return false;
- }
-
- // remove it from childNodes collection
- this.childNodes.splice(index, 1);
-
- // update siblings
- if (node.previousSibling) {
- node.previousSibling.nextSibling = node.nextSibling;
- }
- if (node.nextSibling) {
- node.nextSibling.previousSibling = node.previousSibling;
- }
-
- // update child refs
- if (this.firstChild == node) {
- this.setFirstChild(node.nextSibling);
- }
- if (this.lastChild == node) {
- this.setLastChild(node.previousSibling);
- }
-
- this.fireEvent("remove", this.ownerTree, this, node);
- if (destroy) {
- node.destroy(true);
- } else {
- node.clear();
- }
-
- return node;
- },
-
- // private
- clear : function(destroy) {
- // clear any references from the node
- this.setOwnerTree(null, destroy);
- this.parentNode = this.previousSibling = this.nextSibling = null;
- if (destroy) {
- this.firstChild = this.lastChild = null;
- }
- },
-
- /**
- * Destroys the node.
- */
- destroy : function(silent) {
- /*
- * Silent is to be used in a number of cases
- * 1) When setRootNode is called.
- * 2) When destroy on the tree is called
- * 3) For destroying child nodes on a node
- */
- if (silent === true) {
- this.clearListeners();
- this.clear(true);
- Ext.each(this.childNodes, function(n) {
- n.destroy(true);
- });
- this.childNodes = null;
- } else {
- this.remove(true);
- }
- },
-
- /**
- * Inserts the first node before the second node in this nodes childNodes collection.
- * @param {Node} node The node to insert
- * @param {Node} refNode The node to insert before (if null the node is appended)
- * @return {Node} The inserted node
- */
- insertBefore : function(node, refNode) {
- if (!refNode) { // like standard Dom, refNode can be null for append
- return this.appendChild(node);
- }
- // nothing to do
- if (node == refNode) {
- return false;
- }
-
- if (this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false) {
- return false;
- }
-
- var index = this.indexOf(refNode),
- oldParent = node.parentNode,
- refIndex = index;
-
- // when moving internally, indexes will change after remove
- if (oldParent == this && this.indexOf(node) < index) {
- refIndex--;
- }
-
- // it's a move, make sure we move it cleanly
- if (oldParent) {
- if (node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false) {
- return false;
- }
- oldParent.removeChild(node);
- }
-
- if (refIndex === 0) {
- this.setFirstChild(node);
- }
-
- this.childNodes.splice(refIndex, 0, node);
- node.parentNode = this;
-
- var ps = this.childNodes[refIndex-1];
-
- if (ps) {
- node.previousSibling = ps;
- ps.nextSibling = node;
- } else {
- node.previousSibling = null;
- }
-
- node.nextSibling = refNode;
- refNode.previousSibling = node;
- node.setOwnerTree(this.getOwnerTree());
- this.fireEvent("insert", this.ownerTree, this, node, refNode);
-
- if (oldParent) {
- node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
- }
- return node;
- },
-
- /**
- * Removes this node from its parent
- * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
- * @return {Node} this
- */
- remove : function(destroy) {
- var parentNode = this.parentNode;
-
- if (parentNode) {
- parentNode.removeChild(this, destroy);
- }
- return this;
- },
-
- /**
- * Removes all child nodes from this node.
- * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
- * @return {Node} this
- */
- removeAll : function(destroy) {
- var cn = this.childNodes,
- n;
-
- while ((n = cn[0])) {
- this.removeChild(n, destroy);
- }
- return this;
- },
-
- /**
- * Returns the child node at the specified index.
- * @param {Number} index
- * @return {Node}
- */
- getChildAt : function(index) {
- return this.childNodes[index];
- },
-
- /**
- * Replaces one child node in this node with another.
- * @param {Node} newChild The replacement node
- * @param {Node} oldChild The node to replace
- * @return {Node} The replaced node
- */
- replaceChild : function(newChild, oldChild) {
- var s = oldChild ? oldChild.nextSibling : null;
-
- this.removeChild(oldChild);
- this.insertBefore(newChild, s);
- return oldChild;
- },
-
- /**
- * Returns the index of a child node
- * @param {Node} node
- * @return {Number} The index of the node or -1 if it was not found
- */
- indexOf : function(child) {
- return this.childNodes.indexOf(child);
- },
-
- /**
- * Returns the tree this node is in.
- * @return {Tree}
- */
- getOwnerTree : function() {
- // if it doesn't have one, look for one
- if (!this.ownerTree) {
- var p = this;
-
- while (p) {
- if (p.ownerTree) {
- this.ownerTree = p.ownerTree;
- break;
- }
- p = p.parentNode;
- }
- }
-
- return this.ownerTree;
- },
-
- /**
- * Returns depth of this node (the root node has a depth of 0)
- * @return {Number}
- */
- getDepth : function() {
- var depth = 0,
- p = this;
-
- while (p.parentNode) {
- ++depth;
- p = p.parentNode;
- }
-
- return depth;
- },
-
- // private
- setOwnerTree : function(tree, destroy) {
- // if it is a move, we need to update everyone
- if (tree != this.ownerTree) {
- if (this.ownerTree) {
- this.ownerTree.unregisterNode(this);
- }
- this.ownerTree = tree;
-
- // If we're destroying, we don't need to recurse since it will be called on each child node
- if (destroy !== true) {
- Ext.each(this.childNodes, function(n) {
- n.setOwnerTree(tree);
- });
- }
- if (tree) {
- tree.registerNode(this);
- }
- }
- },
-
- /**
- * Changes the id of this node.
- * @param {String} id The new id for the node.
- */
- setId: function(id) {
- if (id !== this.id) {
- var t = this.ownerTree;
- if (t) {
- t.unregisterNode(this);
- }
- this.id = this.attributes.id = id;
- if (t) {
- t.registerNode(this);
- }
- this.onIdChange(id);
- }
- },
-
- // private
- onIdChange: Ext.emptyFn,
-
- /**
- * Returns the path for this node. The path can be used to expand or select this node programmatically.
- * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
- * @return {String} The path
- */
- getPath : function(attr) {
- attr = attr || "id";
- var p = this.parentNode,
- b = [this.attributes[attr]];
-
- while (p) {
- b.unshift(p.attributes[attr]);
- p = p.parentNode;
- }
-
- var sep = this.getOwnerTree().pathSeparator;
- return sep + b.join(sep);
- },
-
- /**
- * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
- * will be the args provided or the current node. 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 (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
- * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
- */
- bubble : function(fn, scope, args) {
- var p = this;
- while (p) {
- if (fn.apply(scope || p, args || [p]) === false) {
- break;
- }
- p = p.parentNode;
- }
- },
-
-
- /**
- * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
- * will be the args provided or the current node. 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 (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
- * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
- */
- cascadeBy : function(fn, scope, args) {
- if (fn.apply(scope || this, args || [this]) !== false) {
- var childNodes = this.childNodes,
- length = childNodes.length,
- i;
-
- for (i = 0; i < length; i++) {
- childNodes[i].cascadeBy(fn, scope, args);
- }
- }
- },
-
- /**
- * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
- * will be the args provided or the current node. If the function returns false at any point,
- * the iteration stops.
- * @param {Function} fn The function to call
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.
- * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
- */
- eachChild : function(fn, scope, args) {
- var childNodes = this.childNodes,
- length = childNodes.length,
- i;
-
- for (i = 0; i < length; i++) {
- if (fn.apply(scope || this, args || [childNodes[i]]) === false) {
- break;
- }
- }
- },
-
- /**
- * Finds the first child that has the attribute with the specified value.
- * @param {String} attribute The attribute name
- * @param {Mixed} value The value to search for
- * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
- * @return {Node} The found child or null if none was found
- */
- findChild : function(attribute, value, deep) {
- return this.findChildBy(function(){
- return this.attributes[attribute] == value;
- }, null, deep);
- },
-
- /**
- * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
- * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.
- * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
- * @return {Node} The found child or null if none was found
- */
- findChildBy : function(fn, scope, deep) {
- var cs = this.childNodes,
- len = cs.length,
- i = 0,
- n,
- res;
-
- for(; i < len; i++){
- n = cs[i];
- if(fn.call(scope || n, n) === true){
- return n;
- }else if (deep){
- res = n.findChildBy(fn, scope, deep);
- if(res != null){
- return res;
- }
- }
-
- }
-
- return null;
- },
-
- /**
- * Sorts this nodes children using the supplied sort function.
- * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
- * @param {Object} scope (optional)The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
- */
- sort : function(fn, scope) {
- var cs = this.childNodes,
- len = cs.length,
- i, n;
-
- if (len > 0) {
- var sortFn = scope ? function(){return fn.apply(scope, arguments);} : fn;
- cs.sort(sortFn);
- for (i = 0; i < len; i++) {
- n = cs[i];
- n.previousSibling = cs[i-1];
- n.nextSibling = cs[i+1];
-
- if (i === 0){
- this.setFirstChild(n);
- }
- if (i == len - 1) {
- this.setLastChild(n);
- }
- }
- }
- },
-
- /**
- * Returns true if this node is an ancestor (at any point) of the passed node.
- * @param {Node} node
- * @return {Boolean}
- */
- contains : function(node) {
- return node.isAncestor(this);
- },
-
- /**
- * Returns true if the passed node is an ancestor (at any point) of this node.
- * @param {Node} node
- * @return {Boolean}
- */
- isAncestor : function(node) {
- var p = this.parentNode;
- while (p) {
- if (p == node) {
- return true;
- }
- p = p.parentNode;
- }
- return false;
- },
-
- toString : function() {
- return "[Node" + (this.id ? " " + this.id : "") + "]";
- }
-});
-
-
-Ext.data.RecordNode = Ext.extend(Ext.data.Node, {
- constructor: function(config) {
- config = config || {};
- if (config.record) {
- // provide back reference
- config.record.node = this;
- }
- Ext.data.RecordNode.superclass.constructor.call(this, config);
- },
-
- getChildRecords: function() {
- var cn = this.childNodes,
- ln = cn.length,
- i = 0,
- rs = [],
- r;
-
- for (; i < ln; i++) {
- r = cn[i].attributes.record;
- // Hack to inject leaf attribute into the
- // data portion of a record, this will be
- // removed once Record and Ext.data.Node have
- // been combined rather than aggregated.
- r.data.leaf = cn[i].leaf;
- rs.push(r);
- }
- return rs;
- },
-
- getRecord: function() {
- return this.attributes.record;
- },
-
-
- getSubStore: function() {
-
- // <debug>
- if (this.isLeaf()) {
- throw "Attempted to get a substore of a leaf node.";
- }
- // </debug>
-
- var treeStore = this.getOwnerTree().treeStore;
- if (!this.subStore) {
- this.subStore = new Ext.data.Store({
- model: treeStore.model
- });
- // if records have already been preLoaded, apply them
- // to the subStore, if not they will be loaded by the
- // read within the TreeStore itself.
- var children = this.getChildRecords();
- this.subStore.add.apply(this.subStore, children);
- }
-
- if (!this.loaded) {
- treeStore.load({
- node: this
- });
- }
- return this.subStore;
- },
-
- destroy : function(silent) {
- if (this.subStore) {
- this.subStore.destroyStore();
- }
- var attr = this.attributes;
- if (attr.record) {
- delete attr.record.node;
- delete attr.record;
- }
-
- return Ext.data.RecordNode.superclass.destroy.call(this, silent);
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.Proxy
- * @extends Ext.util.Observable
- *
- * <p>Proxies are used by {@link Ext.data.Store Stores} to handle the loading and saving of {@link Ext.data.Model Model} data.
- * Usually developers will not need to create or interact with proxies directly.</p>
- * <p><u>Types of Proxy</u></p>
- *
- * <p>There are two main types of Proxy - {@link Ext.data.ClientProxy Client} and {@link Ext.data.ServerProxy Server}. The Client proxies
- * save their data locally and include the following subclasses:</p>
- *
- * <ul style="list-style-type: disc; padding-left: 25px">
- * <li>{@link Ext.data.LocalStorageProxy LocalStorageProxy} - saves its data to localStorage if the browser supports it</li>
- * <li>{@link Ext.data.SessionStorageProxy SessionStorageProxy} - saves its data to sessionStorage if the browsers supports it</li>
- * <li>{@link Ext.data.MemoryProxy MemoryProxy} - holds data in memory only, any data is lost when the page is refreshed</li>
- * </ul>
- *
- * <p>The Server proxies save their data by sending requests to some remote server. These proxies include:</p>
- *
- * <ul style="list-style-type: disc; padding-left: 25px">
- * <li>{@link Ext.data.AjaxProxy AjaxProxy} - sends requests to a server on the same domain</li>
- * <li>{@link Ext.data.ScriptTagProxy ScriptTagProxy} - uses JSON-P to send requests to a server on a different domain</li>
- * </ul>
- *
- * <p>Proxies operate on the principle that all operations performed are either Create, Read, Update or Delete. These four operations
- * are mapped to the methods {@link #create}, {@link #read}, {@link #update} and {@link #destroy} respectively. Each Proxy subclass
- * implements these functions.</p>
- *
- * <p>The CRUD methods each expect an {@link Ext.data.Operation Operation} object as the sole argument. The Operation encapsulates
- * information about the action the Store wishes to perform, the {@link Ext.data.Model model} instances that are to be modified, etc.
- * See the {@link Ext.data.Operation Operation} documentation for more details. Each CRUD method also accepts a callback function to be
- * called asynchronously on completion.</p>
- *
- * <p>Proxies also support batching of Operations via a {@link Ext.data.Batch batch} object, invoked by the {@link #batch} method.</p>
- *
- * @constructor
- * Creates the Proxy
- * @param {Object} config Optional config object
- */
-Ext.data.Proxy = Ext.extend(Ext.util.Observable, {
- /**
- * @cfg {String} batchOrder
- * Comma-separated ordering 'create', 'update' and 'destroy' actions when batching. Override this
- * to set a different order for the batched CRUD actions to be executed in. Defaults to 'create,update,destroy'
- */
- batchOrder: 'create,update,destroy',
-
- /**
- * @cfg {String} defaultReaderType The default registered reader type. Defaults to 'json'
- * @private
- */
- defaultReaderType: 'json',
-
- /**
- * @cfg {String} defaultWriterType The default registered writer type. Defaults to 'json'
- * @private
- */
- defaultWriterType: 'json',
-
- /**
- * @cfg {String/Ext.data.Model} model The name of the Model to tie to this Proxy. Can be either the string name of
- * the Model, or a reference to the Model constructor. Required.
- */
-
- constructor: function(config) {
- config = config || {};
-
- if (config.model == undefined) {
- delete config.model;
- }
-
- Ext.data.Proxy.superclass.constructor.call(this, config);
-
- if (this.model != undefined && !(this.model instanceof Ext.data.Model)) {
- this.setModel(this.model);
- }
- },
-
- /**
- * Sets the model associated with this proxy. This will only usually be called by a Store
- * @param {String|Ext.data.Model} model The new model. Can be either the model name string,
- * or a reference to the model's constructor
- * @param {Boolean} setOnStore Sets the new model on the associated Store, if one is present
- */
- setModel: function(model, setOnStore) {
- this.model = Ext.ModelMgr.getModel(model);
-
- var reader = this.reader,
- writer = this.writer;
-
- this.setReader(reader);
- this.setWriter(writer);
-
- if (setOnStore && this.store) {
- this.store.setModel(this.model);
- }
- },
-
- /**
- * Returns the model attached to this Proxy
- * @return {Ext.data.Model} The model
- */
- getModel: function() {
- return this.model;
- },
-
- /**
- * Sets the Proxy's Reader by string, config object or Reader instance
- * @param {String|Object|Ext.data.Reader} reader The new Reader, which can be either a type string, a configuration object
- * or an Ext.data.Reader instance
- * @return {Ext.data.Reader} The attached Reader object
- */
- setReader: function(reader) {
- if (reader == undefined || typeof reader == 'string') {
- reader = {
- type: reader
- };
- }
-
- if (reader instanceof Ext.data.Reader) {
- reader.setModel(this.model);
- } else {
- Ext.applyIf(reader, {
- proxy: this,
- model: this.model,
- type : this.defaultReaderType
- });
-
- reader = Ext.data.ReaderMgr.create(reader);
- }
-
- this.reader = reader;
-
- return this.reader;
- },
-
- /**
- * Returns the reader currently attached to this proxy instance
- * @return {Ext.data.Reader} The Reader instance
- */
- getReader: function() {
- return this.reader;
- },
-
- /**
- * Sets the Proxy's Writer by string, config object or Writer instance
- * @param {String|Object|Ext.data.Writer} writer The new Writer, which can be either a type string, a configuration object
- * or an Ext.data.Writer instance
- * @return {Ext.data.Writer} The attached Writer object
- */
- setWriter: function(writer) {
- if (writer == undefined || typeof writer == 'string') {
- writer = {
- type: writer
- };
- }
-
- if (!(writer instanceof Ext.data.Writer)) {
- Ext.applyIf(writer, {
- model: this.model,
- type : this.defaultWriterType
- });
-
- writer = Ext.data.WriterMgr.create(writer);
- }
-
- this.writer = writer;
-
- return this.writer;
- },
-
- /**
- * Returns the writer currently attached to this proxy instance
- * @return {Ext.data.Writer} The Writer instance
- */
- getWriter: function() {
- return this.writer;
- },
-
- /**
- * Performs the given create operation.
- * @param {Ext.data.Operation} operation The Operation to perform
- * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
- * @param {Object} scope Scope to execute the callback function in
- */
- create: Ext.emptyFn,
-
- /**
- * Performs the given read operation.
- * @param {Ext.data.Operation} operation The Operation to perform
- * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
- * @param {Object} scope Scope to execute the callback function in
- */
- read: Ext.emptyFn,
-
- /**
- * Performs the given update operation.
- * @param {Ext.data.Operation} operation The Operation to perform
- * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
- * @param {Object} scope Scope to execute the callback function in
- */
- update: Ext.emptyFn,
-
- /**
- * Performs the given destroy operation.
- * @param {Ext.data.Operation} operation The Operation to perform
- * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
- * @param {Object} scope Scope to execute the callback function in
- */
- destroy: Ext.emptyFn,
-
- /**
- * Performs a batch of {@link Ext.data.Operation Operations}, in the order specified by {@link #batchOrder}. Used internally by
- * {@link Ext.data.Store}'s {@link Ext.data.Store#sync sync} method. Example usage:
- * <pre><code>
- * myProxy.batch({
- * create : [myModel1, myModel2],
- * update : [myModel3],
- * destroy: [myModel4, myModel5]
- * });
- * </code></pre>
- * Where the myModel* above are {@link Ext.data.Model Model} instances - in this case 1 and 2 are new instances and have not been
- * saved before, 3 has been saved previously but needs to be updated, and 4 and 5 have already been saved but should now be destroyed.
- * @param {Object} operations Object containing the Model instances to act upon, keyed by action name
- * @param {Object} listeners Optional listeners object passed straight through to the Batch - see {@link Ext.data.Batch}
- * @return {Ext.data.Batch} The newly created Ext.data.Batch object
- */
- batch: function(operations, listeners) {
- var batch = new Ext.data.Batch({
- proxy: this,
- listeners: listeners || {}
- });
-
- Ext.each(this.batchOrder.split(','), function(action) {
- if (operations[action]) {
- batch.add(new Ext.data.Operation({
- action : action,
- records: operations[action]
- }));
- }
- }, this);
-
- batch.start();
-
- return batch;
- }
-});
-
-//backwards compatibility
-Ext.data.DataProxy = Ext.data.Proxy;
-
-Ext.data.ProxyMgr.registerType('proxy', Ext.data.Proxy);
-/**
- * @author Ed Spencer
- * @class Ext.data.ServerProxy
- * @extends Ext.data.Proxy
- *
- * <p>ServerProxy is a superclass of {@link Ext.data.ScriptTagProxy ScriptTagProxy} and {@link Ext.data.AjaxProxy AjaxProxy},
- * and would not usually be used directly.</p>
- *
- * <p>ServerProxy should ideally be named HttpProxy as it is a superclass for all HTTP proxies - for Ext JS 4.x it has been
- * called ServerProxy to enable any 3.x applications that reference the HttpProxy to continue to work (HttpProxy is now an
- * alias of AjaxProxy).</p>
- */
-Ext.data.ServerProxy = Ext.extend(Ext.data.Proxy, {
- /**
- * @cfg {String} url The URL from which to request the data object.
- */
-
- /**
- * @cfg {Object/String/Ext.data.Reader} reader The Ext.data.Reader to use to decode the server's response. This can
- * either be a Reader instance, a config object or just a valid Reader type name (e.g. 'json', 'xml').
- */
-
- /**
- * @cfg {Object/String/Ext.data.Writer} writer The Ext.data.Writer to use to encode any request sent to the server.
- * This can either be a Writer instance, a config object or just a valid Writer type name (e.g. 'json', 'xml').
- */
-
- /**
- * @cfg {String} pageParam The name of the 'page' parameter to send in a request. Defaults to 'page'. Set this to
- * undefined if you don't want to send a page parameter
- */
- pageParam: 'page',
-
- /**
- * @cfg {String} startParam The name of the 'start' parameter to send in a request. Defaults to 'start'. Set this
- * to undefined if you don't want to send a start parameter
- */
- startParam: 'start',
-
- /**
- * @cfg {String} limitParam The name of the 'limit' parameter to send in a request. Defaults to 'limit'. Set this
- * to undefined if you don't want to send a limit parameter
- */
- limitParam: 'limit',
-
- /**
- * @cfg {String} groupParam The name of the 'group' parameter to send in a request. Defaults to 'group'. Set this
- * to undefined if you don't want to send a group parameter
- */
- groupParam: 'group',
-
- /**
- * @cfg {String} sortParam The name of the 'sort' parameter to send in a request. Defaults to 'sort'. Set this
- * to undefined if you don't want to send a sort parameter
- */
- sortParam: 'sort',
-
- /**
- * @cfg {String} filterParam The name of the 'filter' parameter to send in a request. Defaults to 'filter'. Set
- * this to undefined if you don't want to send a filter parameter
- */
- filterParam: 'filter',
-
- /**
- * @cfg {Boolean} noCache (optional) Defaults to true. Disable caching by adding a unique parameter
- * name to the request.
- */
- noCache : true,
-
- /**
- * @cfg {String} cacheString The name of the cache param added to the url when using noCache (defaults to "_dc")
- */
- cacheString: "_dc",
-
- /**
- * @cfg {Number} timeout (optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
- */
- timeout : 30000,
-
- /**
- * @ignore
- */
- constructor: function(config) {
- config = config || {};
-
- Ext.data.ServerProxy.superclass.constructor.call(this, config);
-
- /**
- * @cfg {Object} extraParams Extra parameters that will be included on every request. Individual requests with params
- * of the same name will override these params when they are in conflict.
- */
- this.extraParams = config.extraParams || {};
-
- //backwards compatibility, will be deprecated in 5.0
- this.nocache = this.noCache;
- },
-
- //in a ServerProxy all four CRUD operations are executed in the same manner, so we delegate to doRequest in each case
- create: function() {
- return this.doRequest.apply(this, arguments);
- },
-
- read: function() {
- return this.doRequest.apply(this, arguments);
- },
-
- update: function() {
- return this.doRequest.apply(this, arguments);
- },
-
- destroy: function() {
- return this.doRequest.apply(this, arguments);
- },
-
- /**
- * Creates and returns an Ext.data.Request object based on the options passed by the {@link Ext.data.Store Store}
- * that this Proxy is attached to.
- * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
- * @return {Ext.data.Request} The request object
- */
- buildRequest: function(operation) {
- var params = Ext.applyIf(operation.params || {}, this.extraParams || {});
-
- //copy any sorters, filters etc into the params so they can be sent over the wire
- params = Ext.applyIf(params, this.getParams(params, operation));
-
- var request = new Ext.data.Request({
- params : params,
- action : operation.action,
- records : operation.records,
- operation: operation
- });
-
- request.url = this.buildUrl(request);
-
- /*
- * Save the request on the Operation. Operations don't usually care about Request and Response data, but in the
- * ServerProxy and any of its subclasses we add both request and response as they may be useful for further processing
- */
- operation.request = request;
-
- return request;
- },
-
- /**
- * Encodes the array of {@link Ext.util.Sorter} objects into a string to be sent in the request url. By default,
- * this simply JSON-encodes the sorter data
- * @param {Array} sorters The array of {@link Ext.util.Sorter Sorter} objects
- * @return {String} The encoded sorters
- */
- encodeSorters: function(sorters) {
- var min = [],
- length = sorters.length,
- i;
-
- for (i = 0; i < length; i++) {
- min[i] = {
- property : sorters[i].property,
- direction: sorters[i].direction
- };
- }
-
- return Ext.encode(min);
- },
-
- /**
- * Encodes the array of {@link Ext.util.Filter} objects into a string to be sent in the request url. By default,
- * this simply JSON-encodes the filter data
- * @param {Array} sorters The array of {@link Ext.util.Filter Filter} objects
- * @return {String} The encoded filters
- */
- encodeFilters: function(filters) {
- var min = [],
- length = filters.length,
- i;
-
- for (i = 0; i < length; i++) {
- min[i] = {
- property: filters[i].property,
- value : filters[i].value
- };
- }
-
- return Ext.encode(min);
- },
-
- /**
- * Encodes the grouping object (field and direction) into a string to be sent in the request url. Be default, this
- * simply JSON-encodes the grouping data
- * @param {Object} group The group configuration (field and direction)
- * @return {String} The encoded group string
- */
- encodeGroupers: function(group) {
- return Ext.encode(group);
- },
-
- /**
- * @private
- * Copy any sorters, filters etc into the params so they can be sent over the wire
- */
- getParams: function(params, operation) {
- params = params || {};
-
- var group = operation.group,
- sorters = operation.sorters,
- filters = operation.filters,
- page = operation.page,
- start = operation.start,
- limit = operation.limit,
-
- pageParam = this.pageParam,
- startParam = this.startParam,
- limitParam = this.limitParam,
- groupParam = this.groupParam,
- sortParam = this.sortParam,
- filterParam = this.filterParam;
-
- if (pageParam && page) {
- params[pageParam] = page;
- }
-
- if (startParam && start) {
- params[startParam] = start;
- }
-
- if (limitParam && limit) {
- params[limitParam] = limit;
- }
-
- if (groupParam && group && group.field) {
- params[groupParam] = this.encodeGroupers(group);
- }
-
- if (sortParam && sorters && sorters.length > 0) {
- params[sortParam] = this.encodeSorters(sorters);
- }
-
- if (filterParam && filters && filters.length > 0) {
- params[filterParam] = this.encodeFilters(filters);
- }
-
- return params;
- },
-
- /**
- * Generates a url based on a given Ext.data.Request object. By default, ServerProxy's buildUrl will
- * add the cache-buster param to the end of the url. Subclasses may need to perform additional modifications
- * to the url.
- * @param {Ext.data.Request} request The request object
- * @return {String} The url
- */
- buildUrl: function(request) {
- var url = request.url || this.url;
-
- if (!url) {
- throw new Error("You are using a ServerProxy but have not supplied it with a url.");
- }
-
- if (this.noCache) {
- url = Ext.urlAppend(url, Ext.util.Format.format("{0}={1}", this.cacheString, (new Date().getTime())));
- }
-
- return url;
- },
-
- /**
- * In ServerProxy subclasses, the {@link #create}, {@link #read}, {@link #update} and {@link #destroy} methods all pass
- * through to doRequest. Each ServerProxy subclass must implement the doRequest method - see {@link Ext.data.ScriptTagProxy}
- * and {@link Ext.data.AjaxProxy} for examples. This method carries the same signature as each of the methods that delegate to it.
- * @param {Ext.data.Operation} operation The Ext.data.Operation object
- * @param {Function} callback The callback function to call when the Operation has completed
- * @param {Object} scope The scope in which to execute the callback
- */
- doRequest: function(operation, callback, scope) {
- throw new Error("The doRequest function has not been implemented on your Ext.data.ServerProxy subclass. See src/data/ServerProxy.js for details");
- },
-
- /**
- * Optional callback function which can be used to clean up after a request has been completed.
- * @param {Ext.data.Request} request The Request object
- * @param {Boolean} success True if the request was successful
- */
- afterRequest: Ext.emptyFn,
-
- onDestroy: function() {
- Ext.destroy(this.reader, this.writer);
-
- Ext.data.ServerProxy.superclass.destroy.apply(this, arguments);
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.AjaxProxy
- * @extends Ext.data.ServerProxy
- *
- * <p>AjaxProxy is one of the most widely-used ways of getting data into your application. It uses AJAX requests to
- * load data from the server, usually to be placed into a {@link Ext.data.Store Store}. Let's take a look at a typical
- * setup. Here we're going to set up a Store that has an AjaxProxy. To prepare, we'll also set up a
- * {@link Ext.data.Model Model}:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: ['id', 'name', 'email']
-});
-
-//The Store contains the AjaxProxy as an inline configuration
-var store = new Ext.data.Store({
- model: 'User',
- proxy: {
- type: 'ajax',
- url : 'users.json'
- }
-});
-
-store.load();
-</code></pre>
- *
- * <p>Our example is going to load user data into a Store, so we start off by defining a {@link Ext.data.Model Model}
- * with the fields that we expect the server to return. Next we set up the Store itself, along with a {@link #proxy}
- * configuration. This configuration was automatically turned into an Ext.data.AjaxProxy instance, with the url we
- * specified being passed into AjaxProxy's constructor. It's as if we'd done this:</p>
- *
-<pre><code>
-new Ext.data.AjaxProxy({
- url: 'users.json',
- model: 'User',
- reader: 'json'
-});
-</code></pre>
- *
- * <p>A couple of extra configurations appeared here - {@link #model} and {@link #reader}. These are set by default
- * when we create the proxy via the Store - the Store already knows about the Model, and Proxy's default
- * {@link Ext.data.Reader Reader} is {@link Ext.data.JsonReader JsonReader}.</p>
- *
- * <p>Now when we call store.load(), the AjaxProxy springs into action, making a request to the url we configured
- * ('users.json' in this case). As we're performing a read, it sends a GET request to that url (see {@link #actionMethods}
- * to customize this - by default any kind of read will be sent as a GET request and any kind of write will be sent as a
- * POST request).</p>
- *
- * <p><u>Limitations</u></p>
- *
- * <p>AjaxProxy cannot be used to retrieve data from other domains. If your application is running on http://domainA.com
- * it cannot load data from http://domainB.com because browsers have a built-in security policy that prohibits domains
- * talking to each other via AJAX.</p>
- *
- * <p>If you need to read data from another domain and can't set up a proxy server (some software that runs on your own
- * domain's web server and transparently forwards requests to http://domainB.com, making it look like they actually came
- * from http://domainA.com), you can use {@link Ext.data.ScriptTagProxy} and a technique known as JSON-P (JSON with
- * Padding), which can help you get around the problem so long as the server on http://domainB.com is set up to support
- * JSON-P responses. See {@link Ext.data.ScriptTagProxy ScriptTagProxy}'s introduction docs for more details.</p>
- *
- * <p><u>Readers and Writers</u></p>
- *
- * <p>AjaxProxy can be configured to use any type of {@link Ext.data.Reader Reader} to decode the server's response. If
- * no Reader is supplied, AjaxProxy will default to using a {@link Ext.data.JsonReader JsonReader}. Reader configuration
- * can be passed in as a simple object, which the Proxy automatically turns into a {@link Ext.data.Reader Reader}
- * instance:</p>
- *
-<pre><code>
-var proxy = new Ext.data.AjaxProxy({
- model: 'User',
- reader: {
- type: 'xml',
- root: 'users'
- }
-});
-
-proxy.getReader(); //returns an {@link Ext.data.XmlReader XmlReader} instance based on the config we supplied
-</code></pre>
- *
- * <p><u>Url generation</u></p>
- *
- * <p>AjaxProxy automatically inserts any sorting, filtering, paging and grouping options into the url it generates for
- * each request. These are controlled with the following configuration options:</p>
- *
- * <ul style="list-style-type: disc; padding-left: 20px;">
- * <li>{@link #pageParam} - controls how the page number is sent to the server
- * (see also {@link #startParam} and {@link #limitParam})</li>
- * <li>{@link #sortParam} - controls how sort information is sent to the server</li>
- * <li>{@link #groupParam} - controls how grouping information is sent to the server</li>
- * <li>{@link #filterParam} - controls how filter information is sent to the server</li>
- * </ul>
- *
- * <p>Each request sent by AjaxProxy is described by an {@link Ext.data.Operation Operation}. To see how we can
- * customize the generated urls, let's say we're loading the Proxy with the following Operation:</p>
- *
-<pre><code>
-var operation = new Ext.data.Operation({
- action: 'read',
- page : 2
-});
-</code></pre>
- *
- * <p>Now we'll issue the request for this Operation by calling {@link #read}:</p>
- *
-<pre><code>
-var proxy = new Ext.data.AjaxProxy({
- url: '/users'
-});
-
-proxy.read(operation); //GET /users?page=2
-</code></pre>
- *
- * <p>Easy enough - the Proxy just copied the page property from the Operation. We can customize how this page data is
- * sent to the server:</p>
- *
-<pre><code>
-var proxy = new Ext.data.AjaxProxy({
- url: '/users',
- pagePage: 'pageNumber'
-});
-
-proxy.read(operation); //GET /users?pageNumber=2
-</code></pre>
- *
- * <p>Alternatively, our Operation could have been configured to send start and limit parameters instead of page:</p>
- *
-<pre><code>
-var operation = new Ext.data.Operation({
- action: 'read',
- start : 50,
- limit : 25
-});
-
-var proxy = new Ext.data.AjaxProxy({
- url: '/users'
-});
-
-proxy.read(operation); //GET /users?start=50&limit=25
-</code></pre>
- *
- * <p>Again we can customize this url:</p>
- *
-<pre><code>
-var proxy = new Ext.data.AjaxProxy({
- url: '/users',
- startParam: 'startIndex',
- limitParam: 'limitIndex'
-});
-
-proxy.read(operation); //GET /users?startIndex=50&limitIndex=25
-</code></pre>
- *
- * <p>AjaxProxy will also send sort and filter information to the server. Let's take a look at how this looks with a
- * more expressive Operation object:</p>
- *
-<pre><code>
-var operation = new Ext.data.Operation({
- action: 'read',
- sorters: [
- new Ext.util.Sorter({
- property : 'name',
- direction: 'ASC'
- }),
- new Ext.util.Sorter({
- property : 'age',
- direction: 'DESC'
- })
- ],
- filters: [
- new Ext.util.Filter({
- property: 'eyeColor',
- value : 'brown'
- })
- ]
-});
-</code></pre>
- *
- * <p>This is the type of object that is generated internally when loading a {@link Ext.data.Store Store} with sorters
- * and filters defined. By default the AjaxProxy will JSON encode the sorters and filters, resulting in something like
- * this (note that the url is escaped before sending the request, but is left unescaped here for clarity):</p>
- *
-<pre><code>
-var proxy = new Ext.data.AjaxProxy({
- url: '/users'
-});
-
-proxy.read(operation); //GET /users?sort=[{"property":"name","direction":"ASC"},{"property":"age","direction":"DESC"}]&filter=[{"property":"eyeColor","value":"brown"}]
-</code></pre>
- *
- * <p>We can again customize how this is created by supplying a few configuration options. Let's say our server is set
- * up to receive sorting information is a format like "sortBy=name#ASC,age#DESC". We can configure AjaxProxy to provide
- * that format like this:</p>
- *
- <pre><code>
- var proxy = new Ext.data.AjaxProxy({
- url: '/users',
- sortParam: 'sortBy',
- filterParam: 'filterBy',
-
- //our custom implementation of sorter encoding - turns our sorters into "name#ASC,age#DESC"
- encodeSorters: function(sorters) {
- var length = sorters.length,
- sortStrs = [],
- sorter, i;
-
- for (i = 0; i < length; i++) {
- sorter = sorters[i];
-
- sortStrs[i] = sorter.property + '#' + sorter.direction
- }
-
- return sortStrs.join(",");
- }
- });
-
- proxy.read(operation); //GET /users?sortBy=name#ASC,age#DESC&filterBy=[{"property":"eyeColor","value":"brown"}]
- </code></pre>
- *
- * <p>We can also provide a custom {@link #encodeFilters} function to encode our filters.</p>
- *
- * @constructor
- *
- * <p>Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the
- * Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>
- * options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters,
- * or react to loading events. The Store's {@link Ext.data.Store#baseParams baseParams} may also be
- * used to pass parameters known at instantiation time.</p>
- *
- * <p>If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make
- * the request.</p>
- */
-Ext.data.AjaxProxy = Ext.extend(Ext.data.ServerProxy, {
- /**
- * @property actionMethods
- * Mapping of action name to HTTP request method. In the basic AjaxProxy these are set to 'GET' for 'read' actions and 'POST'
- * for 'create', 'update' and 'destroy' actions. The {@link Ext.data.RestProxy} maps these to the correct RESTful methods.
- */
- actionMethods: {
- create : 'POST',
- read : 'GET',
- update : 'POST',
- destroy: 'POST'
- },
-
- /**
- * @cfg {Object} headers Any headers to add to the Ajax request. Defaults to <tt>undefined</tt>.
- */
-
- constructor: function() {
- this.addEvents(
- /**
- * @event exception
- * Fires when the server returns an exception
- * @param {Ext.data.Proxy} this
- * @param {Object} response The response from the AJAX request
- * @param {Ext.data.Operation} operation The operation that triggered request
- */
- 'exception'
- );
-
- Ext.data.AjaxProxy.superclass.constructor.apply(this, arguments);
- },
-
- /**
- * @ignore
- */
- doRequest: function(operation, callback, scope) {
- var writer = this.getWriter(),
- request = this.buildRequest(operation, callback, scope);
-
- if (operation.allowWrite()) {
- request = writer.write(request);
- }
-
- Ext.apply(request, {
- headers : this.headers,
- timeout : this.timeout,
- scope : this,
- callback : this.createRequestCallback(request, operation, callback, scope),
- method : this.getMethod(request),
- disableCaching: false // explicitly set it to false, ServerProxy handles caching
- });
-
- Ext.Ajax.request(request);
-
- return request;
- },
-
- /**
- * Returns the HTTP method name for a given request. By default this returns based on a lookup on {@link #actionMethods}.
- * @param {Ext.data.Request} request The request object
- * @return {String} The HTTP method to use (should be one of 'GET', 'POST', 'PUT' or 'DELETE')
- */
- getMethod: function(request) {
- return this.actionMethods[request.action];
- },
-
- /**
- * @private
- * TODO: This is currently identical to the ScriptTagProxy version except for the return function's signature. There is a lot
- * of code duplication inside the returned function so we need to find a way to DRY this up.
- * @param {Ext.data.Request} request The Request object
- * @param {Ext.data.Operation} operation The Operation being executed
- * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
- * passed to doRequest
- * @param {Object} scope The scope in which to execute the callback function
- * @return {Function} The callback function
- */
- createRequestCallback: function(request, operation, callback, scope) {
- var me = this;
-
- return function(options, success, response) {
- if (success === true) {
- var reader = me.getReader(),
- result = reader.read(response);
-
- //see comment in buildRequest for why we include the response object here
- Ext.apply(operation, {
- response : response,
- resultSet: result
- });
-
- operation.setCompleted();
- operation.setSuccessful();
- } else {
- me.fireEvent('exception', this, response, operation);
-
- //TODO: extract error message from reader
- operation.setException();
- }
-
- //this callback is the one that was passed to the 'read' or 'write' function above
- if (typeof callback == 'function') {
- callback.call(scope || me, operation);
- }
-
- me.afterRequest(request, true);
- };
- }
-});
-
-Ext.data.ProxyMgr.registerType('ajax', Ext.data.AjaxProxy);
-
-//backwards compatibility, remove in Ext JS 5.0
-Ext.data.HttpProxy = Ext.data.AjaxProxy;
-/**
- * @author Ed Spencer
- * @class Ext.data.RestProxy
- * @extends Ext.data.AjaxProxy
- *
- * <p>RestProxy is a specialization of the {@link Ext.data.AjaxProxy AjaxProxy} which simply maps the four actions
- * (create, read, update and destroy) to RESTful HTTP verbs. For example, let's set up a {@link Ext.data.Model Model}
- * with an inline RestProxy</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: ['id', 'name', 'email'],
-
- proxy: {
- type: 'rest',
- url : '/users'
- }
-});
-</code></pre>
- *
- * <p>Now we can create a new User instance and save it via the RestProxy. Doing this will cause the Proxy to send a
- * POST request to '/users':
- *
-<pre><code>
-var user = Ext.ModelMgr.create({name: 'Ed Spencer', email: 'ed at sencha.com'}, 'User');
-
-user.save(); //POST /users
-</code></pre>
- *
- * <p>Let's expand this a little and provide a callback for the {@link Ext.data.Model#save} call to update the Model
- * once it has been created. We'll assume the creation went successfully and that the server gave this user an ID of
- * 123:</p>
- *
-<pre><code>
-user.save({
- success: function(user) {
- user.set('name', 'Khan Noonien Singh');
-
- user.save(); //PUT /users/123
- }
-});
-</code></pre>
- *
- * <p>Now that we're no longer creating a new Model instance, the request method is changed to an HTTP PUT, targeting
- * the relevant url for that user. Now let's delete this user, which will use the DELETE method:</p>
- *
-<pre><code>
- user.destroy(); //DELETE /users/123
-</code></pre>
- *
- * <p>Finally, when we perform a load of a Model or Store, RestProxy will use the GET method:</p>
- *
-<pre><code>
-//1. Load via Store
-
-//the Store automatically picks up the Proxy from the User model
-var store = new Ext.data.Store({
- model: 'User'
-});
-
-store.load(); //GET /users
-
-//2. Load directly from the Model
-
-//GET /users/123
-Ext.ModelMgr.getModel('User').load(123, {
- success: function(user) {
- console.log(user.getId()); //outputs 123
- }
-});
-</code></pre>
- *
- * <p><u>Url generation</u></p>
- *
- * <p>RestProxy is able to automatically generate the urls above based on two configuration options - {@link #appendId}
- * and {@link #format}. If appendId is true (it is by default) then RestProxy will automatically append the ID of the
- * Model instance in question to the configured url, resulting in the '/users/123' that we saw above.</p>
- *
- * <p>If the request is not for a specific Model instance (e.g. loading a Store), the url is not appended with an id.
- * RestProxy will automatically insert a '/' before the ID if one is not already present.</p>
- *
-<pre><code>
-new Ext.data.RestProxy({
- url: '/users',
- appendId: true //default
-});
-
-// Collection url: /users
-// Instance url : /users/123
-</code></pre>
- *
- * <p>RestProxy can also optionally append a format string to the end of any generated url:</p>
- *
-<pre><code>
-new Ext.data.RestProxy({
- url: '/users',
- format: 'json'
-});
-
-// Collection url: /users.json
-// Instance url : /users/123.json
-</code></pre>
- *
- * <p>If further customization is needed, simply implement the {@link #buildUrl} method and add your custom generated
- * url onto the {@link Ext.data.Request Request} object that is passed to buildUrl. See
- * <a href="source/RestProxy.html#method-Ext.data.RestProxy-buildUrl">RestProxy's implementation</a> for an example of
- * how to achieve this.</p>
- *
- * <p>Note that RestProxy inherits from {@link Ext.data.AjaxProxy AjaxProxy}, which already injects all of the sorter,
- * filter, group and paging options into the generated url. See the {@link Ext.data.AjaxProxy AjaxProxy docs} for more
- * details.</p>
- */
-Ext.data.RestProxy = Ext.extend(Ext.data.AjaxProxy, {
- /**
- * @cfg {Boolean} appendId True to automatically append the ID of a Model instance when performing a request based
- * on that single instance. See RestProxy intro docs for more details. Defaults to true.
- */
- appendId: true,
-
- /**
- * @cfg {String} format Optional data format to send to the server when making any request (e.g. 'json'). See the
- * RestProxy intro docs for full details. Defaults to undefined.
- */
-
- /**
- * Mapping of action name to HTTP request method. These default to RESTful conventions for the 'create', 'read',
- * 'update' and 'destroy' actions (which map to 'POST', 'GET', 'PUT' and 'DELETE' respectively). This object should
- * not be changed except globally via {@link Ext.override} - the {@link #getMethod} function can be overridden instead.
- * @property actionMethods
- * @type Object
- */
- actionMethods: {
- create : 'POST',
- read : 'GET',
- update : 'PUT',
- destroy: 'DELETE'
- },
-
- api: {
- create : 'create',
- read : 'read',
- update : 'update',
- destroy: 'destroy'
- },
-
- /**
- * Specialized version of buildUrl that incorporates the {@link #appendId} and {@link #format} options into the
- * generated url. Override this to provide further customizations, but remember to call the superclass buildUrl
- * so that additional parameters like the cache buster string are appended
- */
- buildUrl: function(request) {
- var records = request.operation.records || [],
- record = records[0],
- format = this.format,
- url = request.url || this.url;
-
- if (this.appendId && record) {
- if (!url.match(/\/$/)) {
- url += '/';
- }
-
- url += record.getId();
- }
-
- if (format) {
- if (!url.match(/\.$/)) {
- url += '.';
- }
-
- url += format;
- }
-
- request.url = url;
-
- return Ext.data.RestProxy.superclass.buildUrl.apply(this, arguments);
- }
-});
-
-Ext.data.ProxyMgr.registerType('rest', Ext.data.RestProxy);
-Ext.apply(Ext, {
- /**
- * Returns the current document body as an {@link Ext.Element}.
- * @ignore
- * @memberOf Ext
- * @return Ext.Element The document body
- */
- getHead : function() {
- var head;
-
- return function() {
- if (head == undefined) {
- head = Ext.get(document.getElementsByTagName("head")[0]);
- }
-
- return head;
- };
- }()
-});
-
-/**
- * @author Ed Spencer
- * @class Ext.data.ScriptTagProxy
- * @extends Ext.data.ServerProxy
- *
- * <p>ScriptTagProxy is useful when you need to load data from a domain other than the one your application is running
- * on. If your application is running on http://domainA.com it cannot use {@link Ext.data.AjaxProxy Ajax} to load its
- * data from http://domainB.com because cross-domain ajax requests are prohibited by the browser.</p>
- *
- * <p>We can get around this using a ScriptTagProxy. ScriptTagProxy injects a <script> tag into the DOM whenever
- * an AJAX request would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag
- * that would be injected might look like this:</p>
- *
-<pre><code>
-<script src="http://domainB.com/users?callback=someCallback"></script>
-</code></pre>
- *
- * <p>When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
- * other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we
- * want to be notified when the result comes in and that it should call our callback function with the data it sends
- * back. So long as the server formats the response to look like this, everything will work:</p>
- *
-<pre><code>
-someCallback({
- users: [
- {
- id: 1,
- name: "Ed Spencer",
- email: "ed at sencha.com"
- }
- ]
-});
-</code></pre>
- *
- * <p>As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the
- * JSON object that the server returned.</p>
- *
- * <p>ScriptTagProxy takes care of all of this automatically. It formats the url you pass, adding the callback
- * parameter automatically. It even creates a temporary callback function, waits for it to be called and then puts
- * the data into the Proxy making it look just like you loaded it through a normal {@link Ext.data.AjaxProxy AjaxProxy}.
- * Here's how we might set that up:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: ['id', 'name', 'email']
-});
-
-var store = new Ext.data.Store({
- model: 'User',
- proxy: {
- type: 'scripttag',
- url : 'http://domainB.com/users'
- }
-});
-
-store.load();
-</code></pre>
- *
- * <p>That's all we need to do - ScriptTagProxy takes care of the rest. In this case the Proxy will have injected a
- * script tag like this:
- *
-<pre><code>
-<script src="http://domainB.com/users?callback=stcCallback001" id="stcScript001"></script>
-</code></pre>
- *
- * <p><u>Customization</u></p>
- *
- * <p>Most parts of this script tag can be customized using the {@link #callbackParam}, {@link #callbackPrefix} and
- * {@link #scriptIdPrefix} configurations. For example:
- *
-<pre><code>
-var store = new Ext.data.Store({
- model: 'User',
- proxy: {
- type: 'scripttag',
- url : 'http://domainB.com/users',
- callbackParam: 'theCallbackFunction',
- callbackPrefix: 'ABC',
- scriptIdPrefix: 'injectedScript'
- }
-});
-
-store.load();
-</code></pre>
- *
- * <p>Would inject a script tag like this:</p>
- *
-<pre><code>
-<script src="http://domainB.com/users?theCallbackFunction=ABC001" id="injectedScript001"></script>
-</code></pre>
- *
- * <p><u>Implementing on the server side</u></p>
- *
- * <p>The remote server side needs to be configured to return data in this format. Here are suggestions for how you
- * might achieve this using Java, PHP and ASP.net:</p>
- *
- * <p>Java:</p>
- *
-<pre><code>
-boolean scriptTag = false;
-String cb = request.getParameter("callback");
-if (cb != null) {
- scriptTag = true;
- response.setContentType("text/javascript");
-} else {
- response.setContentType("application/x-json");
-}
-Writer out = response.getWriter();
-if (scriptTag) {
- out.write(cb + "(");
-}
-out.print(dataBlock.toJsonString());
-if (scriptTag) {
- out.write(");");
-}
-</code></pre>
- *
- * <p>PHP:</p>
- *
-<pre><code>
-$callback = $_REQUEST['callback'];
-
-// Create the output object.
-$output = array('a' => 'Apple', 'b' => 'Banana');
-
-//start output
-if ($callback) {
- header('Content-Type: text/javascript');
- echo $callback . '(' . json_encode($output) . ');';
-} else {
- header('Content-Type: application/x-json');
- echo json_encode($output);
-}
-</code></pre>
- *
- * <p>ASP.net:</p>
- *
-<pre><code>
-String jsonString = "{success: true}";
-String cb = Request.Params.Get("callback");
-String responseString = "";
-if (!String.IsNullOrEmpty(cb)) {
- responseString = cb + "(" + jsonString + ")";
-} else {
- responseString = jsonString;
-}
-Response.Write(responseString);
-</code></pre>
- *
- */
-Ext.data.ScriptTagProxy = Ext.extend(Ext.data.ServerProxy, {
- defaultWriterType: 'base',
-
- /**
- * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
- * the server the name of the callback function set up by the load call to process the returned data object.
- * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
- * javascript output which calls this named function passing the data object as its only parameter.
- */
- callbackParam : "callback",
-
- /**
- * @cfg {String} scriptIdPrefix
- * The prefix string that is used to create a unique ID for the injected script tag element (defaults to 'stcScript')
- */
- scriptIdPrefix: 'stcScript',
-
- /**
- * @cfg {String} callbackPrefix
- * The prefix string that is used to create a unique callback function name in the global scope. This can optionally
- * be modified to give control over how the callback string passed to the remote server is generated. Defaults to 'stcCallback'
- */
- callbackPrefix: 'stcCallback',
-
- /**
- * @cfg {String} recordParam
- * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString').
- * Defaults to 'records'
- */
- recordParam: 'records',
-
- /**
- * Reference to the most recent request made through this Proxy. Used internally to clean up when the Proxy is destroyed
- * @property lastRequest
- * @type Ext.data.Request
- */
- lastRequest: undefined,
-
- /**
- * @cfg {Boolean} autoAppendParams True to automatically append the request's params to the generated url. Defaults to true
- */
- autoAppendParams: true,
-
- constructor: function(){
- this.addEvents(
- /**
- * @event exception
- * Fires when the server returns an exception
- * @param {Ext.data.Proxy} this
- * @param {Ext.data.Request} request The request that was sent
- * @param {Ext.data.Operation} operation The operation that triggered the request
- */
- 'exception'
- );
-
- Ext.data.ScriptTagProxy.superclass.constructor.apply(this, arguments);
- },
-
- /**
- * @private
- * Performs the read request to the remote domain. ScriptTagProxy does not actually create an Ajax request,
- * instead we write out a <script> tag based on the configuration of the internal Ext.data.Request object
- * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
- * @param {Function} callback A callback function to execute when the Operation has been completed
- * @param {Object} scope The scope to execute the callback in
- */
- doRequest: function(operation, callback, scope) {
- //generate the unique IDs for this request
- var format = Ext.util.Format.format,
- transId = ++Ext.data.ScriptTagProxy.TRANS_ID,
- scriptId = format("{0}{1}", this.scriptIdPrefix, transId),
- stCallback = format("{0}{1}", this.callbackPrefix, transId);
-
- var writer = this.getWriter(),
- request = this.buildRequest(operation),
- //FIXME: ideally this would be in buildUrl, but we don't know the stCallback name at that point
- url = Ext.urlAppend(request.url, format("{0}={1}", this.callbackParam, stCallback));
-
- if (operation.allowWrite()) {
- request = writer.write(request);
- }
-
- //apply ScriptTagProxy-specific attributes to the Request
- Ext.apply(request, {
- url : url,
- transId : transId,
- scriptId : scriptId,
- stCallback: stCallback
- });
-
- //if the request takes too long this timeout function will cancel it
- request.timeoutId = Ext.defer(this.createTimeoutHandler, this.timeout, this, [request, operation]);
-
- //this is the callback that will be called when the request is completed
- window[stCallback] = this.createRequestCallback(request, operation, callback, scope);
-
- //create the script tag and inject it into the document
- var script = document.createElement("script");
- script.setAttribute("src", url);
- script.setAttribute("async", true);
- script.setAttribute("type", "text/javascript");
- script.setAttribute("id", scriptId);
-
- Ext.getHead().appendChild(script);
- operation.setStarted();
-
- this.lastRequest = request;
-
- return request;
- },
-
- /**
- * @private
- * Creates and returns the function that is called when the request has completed. The returned function
- * should accept a Response object, which contains the response to be read by the configured Reader.
- * The third argument is the callback that should be called after the request has been completed and the Reader has decoded
- * the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
- * theCallback refers to the callback argument received by this function.
- * See {@link #doRequest} for details.
- * @param {Ext.data.Request} request The Request object
- * @param {Ext.data.Operation} operation The Operation being executed
- * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
- * passed to doRequest
- * @param {Object} scope The scope in which to execute the callback function
- * @return {Function} The callback function
- */
- createRequestCallback: function(request, operation, callback, scope) {
- var me = this;
-
- return function(response) {
- var reader = me.getReader(),
- result = reader.read(response);
-
- //see comment in buildRequest for why we include the response object here
- Ext.apply(operation, {
- response : response,
- resultSet: result
- });
-
- operation.setCompleted();
- operation.setSuccessful();
-
- //this callback is the one that was passed to the 'read' or 'write' function above
- if (typeof callback == 'function') {
- callback.call(scope || me, operation);
- }
-
- me.afterRequest(request, true);
- };
- },
-
- /**
- * Cleans up after a completed request by removing the now unnecessary script tag from the DOM. Also removes the
- * global JSON-P callback function.
- * @param {Ext.data.Request} request The request object
- * @param {Boolean} isLoaded True if the request completed successfully
- */
- afterRequest: function() {
- var cleanup = function(functionName) {
- return function() {
- window[functionName] = undefined;
-
- try {
- delete window[functionName];
- } catch(e) {}
- };
- };
-
- return function(request, isLoaded) {
- Ext.get(request.scriptId).remove();
- clearTimeout(request.timeoutId);
-
- var callbackName = request.stCallback;
-
- if (isLoaded) {
- cleanup(callbackName)();
- this.lastRequest.completed = true;
- } else {
- // if we haven't loaded yet, the callback might still be called in the future so don't unset it immediately
- window[callbackName] = cleanup(callbackName);
- }
- };
- }(),
-
- /**
- * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
- * @param {Ext.data.Request} request The request object
- * @return {String} The url
- */
- buildUrl: function(request) {
- var url = Ext.data.ScriptTagProxy.superclass.buildUrl.call(this, request),
- params = Ext.apply({}, request.params),
- filters = params.filters,
- filter, i;
-
- delete params.filters;
-
- if (this.autoAppendParams) {
- url = Ext.urlAppend(url, Ext.urlEncode(params));
- }
-
- if (filters && filters.length) {
- for (i = 0; i < filters.length; i++) {
- filter = filters[i];
-
- if (filter.value) {
- url = Ext.urlAppend(url, filter.property + "=" + filter.value);
- }
- }
- }
-
- //if there are any records present, append them to the url also
- var records = request.records;
-
- if (Ext.isArray(records) && records.length > 0) {
- url = Ext.urlAppend(url, Ext.util.Format.format("{0}={1}", this.recordParam, this.encodeRecords(records)));
- }
-
- return url;
- },
-
- //inherit docs
- destroy: function() {
- this.abort();
-
- Ext.data.ScriptTagProxy.superclass.destroy.apply(this, arguments);
- },
-
- /**
- * @private
- * @return {Boolean} True if there is a current request that hasn't completed yet
- */
- isLoading : function(){
- var lastRequest = this.lastRequest;
-
- return (lastRequest != undefined && !lastRequest.completed);
- },
-
- /**
- * Aborts the current server request if one is currently running
- */
- abort: function() {
- if (this.isLoading()) {
- this.afterRequest(this.lastRequest);
- }
- },
-
- /**
- * Encodes an array of records into a string suitable to be appended to the script src url. This is broken
- * out into its own function so that it can be easily overridden.
- * @param {Array} records The records array
- * @return {String} The encoded records string
- */
- encodeRecords: function(records) {
- var encoded = "";
-
- for (var i = 0, length = records.length; i < length; i++) {
- encoded += Ext.urlEncode(records[i].data);
- }
-
- return encoded;
- },
-
- /**
- * @private
- * Starts a timer with the value of this.timeout - if this fires it means the request took too long so we
- * cancel the request. If the request was successful this timer is cancelled by this.afterRequest
- * @param {Ext.data.Request} request The Request to handle
- */
- createTimeoutHandler: function(request, operation) {
- this.afterRequest(request, false);
-
- this.fireEvent('exception', this, request, operation);
-
- if (typeof request.callback == 'function') {
- request.callback.call(request.scope || window, null, request.options, false);
- }
- }
-});
-
-Ext.data.ScriptTagProxy.TRANS_ID = 1000;
-
-Ext.data.ProxyMgr.registerType('scripttag', Ext.data.ScriptTagProxy);
-/**
- * @author Ed Spencer
- * @class Ext.data.ClientProxy
- * @extends Ext.data.Proxy
- *
- * <p>Base class for any client-side storage. Used as a superclass for {@link Ext.data.MemoryProxy Memory} and
- * {@link Ext.data.WebStorageProxy Web Storage} proxies. Do not use directly, use one of the subclasses instead.</p>
- */
-Ext.data.ClientProxy = Ext.extend(Ext.data.Proxy, {
- /**
- * Abstract function that must be implemented by each ClientProxy subclass. This should purge all record data
- * from the client side storage, as well as removing any supporting data (such as lists of record IDs)
- */
- clear: function() {
- throw new Error("The Ext.data.ClientProxy subclass that you are using has not defined a 'clear' function. See src/data/ClientProxy.js for details.");
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.MemoryProxy
- * @extends Ext.data.ClientProxy
- *
- * <p>In-memory proxy. This proxy simply uses a local variable for data storage/retrieval, so its contents are lost on
- * every page refresh.</p>
- *
- * <p>Usually this Proxy isn't used directly, serving instead as a helper to a {@link Ext.data.Store Store} where a
- * reader is required to load data. For example, say we have a Store for a User model and have some inline data we want
- * to load, but this data isn't in quite the right format: we can use a MemoryProxy with a JsonReader to read it into
- * our Store:</p>
- *
-<pre><code>
-//this is the model we will be using in the store
-Ext.regModel('User', {
- fields: [
- {name: 'id', type: 'int'},
- {name: 'name', type: 'string'},
- {name: 'phone', type: 'string', mapping: 'phoneNumber'}
- ]
-});
-
-//this data does not line up to our model fields - the phone field is called phoneNumber
-var data = {
- users: [
- {
- id: 1,
- name: 'Ed Spencer',
- phoneNumber: '555 1234'
- },
- {
- id: 2,
- name: 'Abe Elias',
- phoneNumber: '666 1234'
- }
- ]
-};
-
-//note how we set the 'root' in the reader to match the data structure above
-var store = new Ext.data.Store({
- autoLoad: true,
- model: 'User',
- data : data,
- proxy: {
- type: 'memory',
- reader: {
- type: 'json',
- root: 'users'
- }
- }
-});
-</code></pre>
- */
-Ext.data.MemoryProxy = Ext.extend(Ext.data.ClientProxy, {
- /**
- * @cfg {Array} data Optional array of Records to load into the Proxy
- */
-
- constructor: function(config) {
- Ext.data.MemoryProxy.superclass.constructor.call(this, config);
-
- //ensures that the reader has been instantiated properly
- this.setReader(this.reader);
- },
-
- /**
- * Reads data from the configured {@link #data} object. Uses the Proxy's {@link #reader}, if present
- * @param {Ext.data.Operation} operation The read Operation
- * @param {Function} callback The callback to call when reading has completed
- * @param {Object} scope The scope to call the callback function in
- */
- read: function(operation, callback, scope) {
- var reader = this.getReader(),
- result = reader.read(this.data);
-
- Ext.apply(operation, {
- resultSet: result
- });
-
- operation.setCompleted();
-
- if (typeof callback == 'function') {
- callback.call(scope || this, operation);
- }
- },
-
- clear: Ext.emptyFn
-});
-
-Ext.data.ProxyMgr.registerType('memory', Ext.data.MemoryProxy);
-/**
- * @author Ed Spencer
- * @class Ext.data.WebStorageProxy
- * @extends Ext.data.ClientProxy
- *
- * <p>WebStorageProxy is simply a superclass for the {@link Ext.data.LocalStorageProxy localStorage} and
- * {@link Ext.data.SessionStorageProxy sessionStorage} proxies. It uses the new HTML5 key/value client-side storage
- * objects to save {@link Ext.data.Model model instances} for offline use.</p>
- *
- * @constructor
- * Creates the proxy, throws an error if local storage is not supported in the current browser
- * @param {Object} config Optional config object
- */
-Ext.data.WebStorageProxy = Ext.extend(Ext.data.ClientProxy, {
- /**
- * @cfg {String} id The unique ID used as the key in which all record data are stored in the local storage object
- */
- id: undefined,
-
- /**
- * @ignore
- */
- constructor: function(config) {
- Ext.data.WebStorageProxy.superclass.constructor.call(this, config);
-
- /**
- * Cached map of records already retrieved by this Proxy - ensures that the same instance is always retrieved
- * @property cache
- * @type Object
- */
- this.cache = {};
-
- if (this.getStorageObject() == undefined) {
- throw "Local Storage is not supported in this browser, please use another type of data proxy";
- }
-
- //if an id is not given, try to use the store's id instead
- this.id = this.id || (this.store ? this.store.storeId : undefined);
-
- if (this.id == undefined) {
- throw "No unique id was provided to the local storage proxy. See Ext.data.LocalStorageProxy documentation for details";
- }
-
- this.initialize();
- },
-
- //inherit docs
- create: function(operation, callback, scope) {
- var records = operation.records,
- length = records.length,
- ids = this.getIds(),
- id, record, i;
-
- operation.setStarted();
-
- for (i = 0; i < length; i++) {
- record = records[i];
-
- if (record.phantom) {
- record.phantom = false;
- id = this.getNextId();
- } else {
- id = record.getId();
- }
-
- this.setRecord(record, id);
- ids.push(id);
- }
-
- this.setIds(ids);
-
- operation.setCompleted();
- operation.setSuccessful();
-
- if (typeof callback == 'function') {
- callback.call(scope || this, operation);
- }
- },
-
- //inherit docs
- read: function(operation, callback, scope) {
- //TODO: respect sorters, filters, start and limit options on the Operation
-
- var records = [],
- ids = this.getIds(),
- length = ids.length,
- i, recordData, record;
-
- //read a single record
- if (operation.id) {
- record = this.getRecord(operation.id);
-
- if (record) {
- records.push(record);
- operation.setSuccessful();
- }
- } else {
- for (i = 0; i < length; i++) {
- records.push(this.getRecord(ids[i]));
- }
- operation.setSuccessful();
- }
-
- operation.setCompleted();
-
- operation.resultSet = new Ext.data.ResultSet({
- records: records,
- total : records.length,
- loaded : true
- });
-
- if (typeof callback == 'function') {
- callback.call(scope || this, operation);
- }
- },
-
- //inherit docs
- update: function(operation, callback, scope) {
- var records = operation.records,
- length = records.length,
- ids = this.getIds(),
- record, id, i;
-
- operation.setStarted();
-
- for (i = 0; i < length; i++) {
- record = records[i];
- this.setRecord(record);
-
- //we need to update the set of ids here because it's possible that a non-phantom record was added
- //to this proxy - in which case the record's id would never have been added via the normal 'create' call
- id = record.getId();
- if (id !== undefined && ids.indexOf(id) == -1) {
- ids.push(id);
- }
- }
- this.setIds(ids);
-
- operation.setCompleted();
- operation.setSuccessful();
-
- if (typeof callback == 'function') {
- callback.call(scope || this, operation);
- }
- },
-
- //inherit
- destroy: function(operation, callback, scope) {
- var records = operation.records,
- length = records.length,
- ids = this.getIds(),
-
- //newIds is a copy of ids, from which we remove the destroyed records
- newIds = [].concat(ids),
- i;
-
- for (i = 0; i < length; i++) {
- newIds.remove(records[i].getId());
- this.removeRecord(records[i], false);
- }
-
- this.setIds(newIds);
-
- if (typeof callback == 'function') {
- callback.call(scope || this, operation);
- }
- },
-
- /**
- * @private
- * Fetches a model instance from the Proxy by ID. Runs each field's decode function (if present) to decode the data
- * @param {String} id The record's unique ID
- * @return {Ext.data.Model} The model instance
- */
- getRecord: function(id) {
- if (this.cache[id] == undefined) {
- var rawData = Ext.decode(this.getStorageObject().getItem(this.getRecordKey(id))),
- data = {},
- Model = this.model,
- fields = Model.prototype.fields.items,
- length = fields.length,
- i, field, name, record;
-
- for (i = 0; i < length; i++) {
- field = fields[i];
- name = field.name;
-
- if (typeof field.decode == 'function') {
- data[name] = field.decode(rawData[name]);
- } else {
- data[name] = rawData[name];
- }
- }
-
- record = new Model(data);
- record.phantom = false;
-
- this.cache[id] = record;
- }
-
- return this.cache[id];
- },
-
- /**
- * Saves the given record in the Proxy. Runs each field's encode function (if present) to encode the data
- * @param {Ext.data.Model} record The model instance
- * @param {String} id The id to save the record under (defaults to the value of the record's getId() function)
- */
- setRecord: function(record, id) {
- if (id) {
- record.setId(id);
- } else {
- id = record.getId();
- }
-
- var rawData = record.data,
- data = {},
- model = this.model,
- fields = model.prototype.fields.items,
- length = fields.length,
- i, field, name;
-
- for (i = 0; i < length; i++) {
- field = fields[i];
- name = field.name;
-
- if (typeof field.encode == 'function') {
- data[name] = field.encode(rawData[name], record);
- } else {
- data[name] = rawData[name];
- }
- }
-
- var obj = this.getStorageObject(),
- key = this.getRecordKey(id);
-
- //keep the cache up to date
- this.cache[id] = record;
-
- //iPad bug requires that we remove the item before setting it
- obj.removeItem(key);
- obj.setItem(key, Ext.encode(data));
- },
-
- /**
- * @private
- * Physically removes a given record from the local storage. Used internally by {@link #destroy}, which you should
- * use instead because it updates the list of currently-stored record ids
- * @param {String|Number|Ext.data.Model} id The id of the record to remove, or an Ext.data.Model instance
- */
- removeRecord: function(id, updateIds) {
- if (id instanceof Ext.data.Model) {
- id = id.getId();
- }
-
- if (updateIds !== false) {
- var ids = this.getIds();
- ids.remove(id);
- this.setIds(ids);
- }
-
- this.getStorageObject().removeItem(this.getRecordKey(id));
- },
-
- /**
- * @private
- * Given the id of a record, returns a unique string based on that id and the id of this proxy. This is used when
- * storing data in the local storage object and should prevent naming collisions.
- * @param {String|Number|Ext.data.Model} id The record id, or a Model instance
- * @return {String} The unique key for this record
- */
- getRecordKey: function(id) {
- if (id instanceof Ext.data.Model) {
- id = id.getId();
- }
-
- return Ext.util.Format.format("{0}-{1}", this.id, id);
- },
-
- /**
- * @private
- * Returns the unique key used to store the current record counter for this proxy. This is used internally when
- * realizing models (creating them when they used to be phantoms), in order to give each model instance a unique id.
- * @return {String} The counter key
- */
- getRecordCounterKey: function() {
- return Ext.util.Format.format("{0}-counter", this.id);
- },
-
- /**
- * @private
- * Returns the array of record IDs stored in this Proxy
- * @return {Array} The record IDs. Each is cast as a Number
- */
- getIds: function() {
- var ids = (this.getStorageObject().getItem(this.id) || "").split(","),
- length = ids.length,
- i;
-
- if (length == 1 && ids[0] == "") {
- ids = [];
- } else {
- for (i = 0; i < length; i++) {
- ids[i] = parseInt(ids[i], 10);
- }
- }
-
- return ids;
- },
-
- /**
- * @private
- * Saves the array of ids representing the set of all records in the Proxy
- * @param {Array} ids The ids to set
- */
- setIds: function(ids) {
- var obj = this.getStorageObject(),
- str = ids.join(",");
-
- obj.removeItem(this.id);
-
- if (!Ext.isEmpty(str)) {
- obj.setItem(this.id, str);
- }
- },
-
- /**
- * @private
- * Returns the next numerical ID that can be used when realizing a model instance (see getRecordCounterKey). Increments
- * the counter.
- * @return {Number} The id
- */
- getNextId: function() {
- var obj = this.getStorageObject(),
- key = this.getRecordCounterKey(),
- last = obj[key],
- ids, id;
-
- if (last == undefined) {
- ids = this.getIds();
- last = ids[ids.length - 1] || 0;
- }
-
- id = parseInt(last, 10) + 1;
- obj.setItem(key, id);
-
- return id;
- },
-
- /**
- * @private
- * Sets up the Proxy by claiming the key in the storage object that corresponds to the unique id of this Proxy. Called
- * automatically by the constructor, this should not need to be called again unless {@link #clear} has been called.
- */
- initialize: function() {
- var storageObject = this.getStorageObject();
- storageObject.setItem(this.id, storageObject.getItem(this.id) || "");
- },
-
- /**
- * Destroys all records stored in the proxy and removes all keys and values used to support the proxy from the storage object
- */
- clear: function() {
- var obj = this.getStorageObject(),
- ids = this.getIds(),
- len = ids.length,
- i;
-
- //remove all the records
- for (i = 0; i < len; i++) {
- this.removeRecord(ids[i]);
- }
-
- //remove the supporting objects
- obj.removeItem(this.getRecordCounterKey());
- obj.removeItem(this.id);
- },
-
- /**
- * @private
- * Abstract function which should return the storage object that data will be saved to. This must be implemented
- * in each subclass.
- * @return {Object} The storage object
- */
- getStorageObject: function() {
- throw new Error("The getStorageObject function has not been defined in your Ext.data.WebStorageProxy subclass");
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.LocalStorageProxy
- * @extends Ext.data.WebStorageProxy
- *
- * <p>The LocalStorageProxy uses the new HTML5 localStorage API to save {@link Ext.data.Model Model} data locally on
- * the client browser. HTML5 localStorage is a key-value store (e.g. cannot save complex objects like JSON), so
- * LocalStorageProxy automatically serializes and deserializes data when saving and retrieving it.</p>
- *
- * <p>localStorage is extremely useful for saving user-specific information without needing to build server-side
- * infrastructure to support it. Let's imagine we're writing a Twitter search application and want to save the user's
- * searches locally so they can easily perform a saved search again later. We'd start by creating a Search model:</p>
- *
-<pre><code>
-Ext.regModel('Search', {
- fields: ['id', 'query'],
-
- proxy: {
- type: 'localstorage',
- id : 'twitter-Searches'
- }
-});
-</code></pre>
- *
- * <p>Our Search model contains just two fields - id and query - plus a Proxy definition. The only configuration we
- * need to pass to the LocalStorage proxy is an {@link #id}. This is important as it separates the Model data in this
- * Proxy from all others. The localStorage API puts all data into a single shared namespace, so by setting an id we
- * enable LocalStorageProxy to manage the saved Search data.</p>
- *
- * <p>Saving our data into localStorage is easy and would usually be done with a {@link Ext.data.Store Store}:</p>
- *
-<pre><code>
-//our Store automatically picks up the LocalStorageProxy defined on the Search model
-var store = new Ext.data.Store({
- model: "Search"
-});
-
-//loads any existing Search data from localStorage
-store.load();
-
-//now add some Searches
-store.add({query: 'Sencha Touch'});
-store.add({query: 'Ext JS'});
-
-//finally, save our Search data to localStorage
-store.sync();
-</code></pre>
- *
- * <p>The LocalStorageProxy automatically gives our new Searches an id when we call store.sync(). It encodes the Model
- * data and places it into localStorage. We can also save directly to localStorage, bypassing the Store altogether:</p>
- *
-<pre><code>
-var search = Ext.ModelMgr.create({query: 'Sencha Animator'}, 'Search');
-
-//uses the configured LocalStorageProxy to save the new Search to localStorage
-search.save();
-</code></pre>
- *
- * <p><u>Limitations</u></p>
- *
- * <p>If this proxy is used in a browser where local storage is not supported, the constructor will throw an error.
- * A local storage proxy requires a unique ID which is used as a key in which all record data are stored in the
- * local storage object.</p>
- *
- * <p>It's important to supply this unique ID as it cannot be reliably determined otherwise. If no id is provided
- * but the attached store has a storeId, the storeId will be used. If neither option is presented the proxy will
- * throw an error.</p>
- */
-Ext.data.LocalStorageProxy = Ext.extend(Ext.data.WebStorageProxy, {
- //inherit docs
- getStorageObject: function() {
- return window.localStorage;
- }
-});
-
-Ext.data.ProxyMgr.registerType('localstorage', Ext.data.LocalStorageProxy);
-/**
- * @author Ed Spencer
- * @class Ext.data.SessionStorageProxy
- * @extends Ext.data.WebStorageProxy
- *
- * <p>Proxy which uses HTML5 session storage as its data storage/retrieval mechanism.
- * If this proxy is used in a browser where session storage is not supported, the constructor will throw an error.
- * A session storage proxy requires a unique ID which is used as a key in which all record data are stored in the
- * session storage object.</p>
- *
- * <p>It's important to supply this unique ID as it cannot be reliably determined otherwise. If no id is provided
- * but the attached store has a storeId, the storeId will be used. If neither option is presented the proxy will
- * throw an error.</p>
- *
- * <p>Proxies are almost always used with a {@link Ext.data.Store store}:<p>
- *
-<pre><code>
-new Ext.data.Store({
- proxy: {
- type: 'sessionstorage',
- id : 'myProxyKey'
- }
-});
-</code></pre>
- *
- * <p>Alternatively you can instantiate the Proxy directly:</p>
- *
-<pre><code>
-new Ext.data.SessionStorageProxy({
- id : 'myOtherProxyKey'
-});
- </code></pre>
- *
- * <p>Note that session storage is different to local storage (see {@link Ext.data.LocalStorageProxy}) - if a browser
- * session is ended (e.g. by closing the browser) then all data in a SessionStorageProxy are lost. Browser restarts
- * don't affect the {@link Ext.data.LocalStorageProxy} - the data are preserved.</p>
- */
-Ext.data.SessionStorageProxy = Ext.extend(Ext.data.WebStorageProxy, {
- //inherit docs
- getStorageObject: function() {
- return window.sessionStorage;
- }
-});
-
-Ext.data.ProxyMgr.registerType('sessionstorage', Ext.data.SessionStorageProxy);
-/**
- * @author Ed Spencer
- * @class Ext.data.Reader
- * @extends Object
- *
- * <p>Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link Ext.data.Store Store}
- * - usually in response to an AJAX request. This is normally handled transparently by passing some configuration to either the
- * {@link Ext.data.Model Model} or the {@link Ext.data.Store Store} in question - see their documentation for further details.</p>
- *
- * <p><u>Loading Nested Data</u></p>
- *
- * <p>Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.Association associations}
- * configured on each Model. Below is an example demonstrating the flexibility of these associations in a fictional CRM system which
- * manages a User, their Orders, OrderItems and Products. First we'll define the models:
- *
-<pre><code>
-Ext.regModel("User", {
- fields: [
- 'id', 'name'
- ],
-
- hasMany: {model: 'Order', name: 'orders'},
-
- proxy: {
- type: 'rest',
- url : 'users.json',
- reader: {
- type: 'json',
- root: 'users'
- }
- }
-});
-
-Ext.regModel("Order", {
- fields: [
- 'id', 'total'
- ],
-
- hasMany : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
- belongsTo: 'User'
-});
-
-Ext.regModel("OrderItem", {
- fields: [
- 'id', 'price', 'quantity', 'order_id', 'product_id'
- ],
-
- belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
-});
-
-Ext.regModel("Product", {
- fields: [
- 'id', 'name'
- ],
-
- hasMany: 'OrderItem'
-});
-</code></pre>
- *
- * <p>This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems. Finally,
- * each OrderItem has a single Product. This allows us to consume data like this:</p>
- *
-<pre><code>
-{
- "users": [
- {
- "id": 123,
- "name": "Ed",
- "orders": [
- {
- "id": 50,
- "total": 100,
- "order_items": [
- {
- "id" : 20,
- "price" : 40,
- "quantity": 2,
- "product" : {
- "id": 1000,
- "name": "MacBook Pro"
- }
- },
- {
- "id" : 21,
- "price" : 20,
- "quantity": 3,
- "product" : {
- "id": 1001,
- "name": "iPhone"
- }
- }
- ]
- }
- ]
- }
- ]
-}
-</code></pre>
- *
- * <p>The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the Orders
- * for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case), and finally
- * the Product associated with each OrderItem. Now we can read the data and use it as follows:
- *
-<pre><code>
-var store = new Ext.data.Store({
- model: "User"
-});
-
-store.load({
- callback: function() {
- //the user that was loaded
- var user = store.first();
-
- console.log("Orders for " + user.get('name') + ":")
-
- //iterate over the Orders for each User
- user.orders().each(function(order) {
- console.log("Order ID: " + order.getId() + ", which contains items:");
-
- //iterate over the OrderItems for each Order
- order.orderItems().each(function(orderItem) {
- //we know that the Product data is already loaded, so we can use the synchronous getProduct
- //usually, we would use the asynchronous version (see {@link Ext.data.BelongsToAssociation})
- var product = orderItem.getProduct();
-
- console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
- });
- });
- }
-});
-</code></pre>
- *
- * <p>Running the code above results in the following:</p>
- *
-<pre><code>
-Orders for Ed:
-Order ID: 50, which contains items:
-2 orders of MacBook Pro
-3 orders of iPhone
-</code></pre>
- *
- * @constructor
- * @param {Object} config Optional config object
- */
-Ext.data.Reader = Ext.extend(Object, {
- /**
- * @cfg {String} idProperty Name of the property within a row object
- * that contains a record identifier value. Defaults to <tt>id</tt>
- */
- idProperty: 'id',
-
- /**
- * @cfg {String} totalProperty Name of the property from which to
- * retrieve the total number of records in the dataset. This is only needed
- * if the whole dataset is not passed in one go, but is being paged from
- * the remote server. Defaults to <tt>total</tt>.
- */
- totalProperty: 'total',
-
- /**
- * @cfg {String} successProperty Name of the property from which to
- * retrieve the success attribute. Defaults to <tt>success</tt>. See
- * {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
- * for additional information.
- */
- successProperty: 'success',
-
- /**
- * @cfg {String} root <b>Required</b>. The name of the property
- * which contains the Array of row objects. Defaults to <tt>undefined</tt>.
- * An exception will be thrown if the root property is undefined. The data
- * packet value for this property should be an empty array to clear the data
- * or show no data.
- */
- root: '',
-
- /**
- * @cfg {Boolean} implicitIncludes True to automatically parse models nested within other models in a response
- * object. See the Ext.data.Reader intro docs for full explanation. Defaults to true.
- */
- implicitIncludes: true,
-
- // Private. Empty ResultSet to return when response is falsy (null|undefined|empty string)
- nullResultSet: new Ext.data.ResultSet({
- total : 0,
- count : 0,
- records: [],
- success: true
- }),
-
- constructor: function(config) {
- Ext.apply(this, config || {});
-
- this.model = Ext.ModelMgr.getModel(config.model);
- if (this.model) {
- this.buildExtractors();
- }
- },
-
- /**
- * Sets a new model for the reader.
- * @private
- * @param {Object} model The model to set.
- * @param {Boolean} setOnProxy True to also set on the Proxy, if one is configured
- */
- setModel: function(model, setOnProxy) {
- this.model = Ext.ModelMgr.getModel(model);
- this.buildExtractors(true);
-
- if (setOnProxy && this.proxy) {
- this.proxy.setModel(this.model, true);
- }
- },
-
- /**
- * Reads the given response object. This method normalizes the different types of response object that may be passed
- * to it, before handing off the reading of records to the {@link readRecords} function.
- * @param {Object} response The response object. This may be either an XMLHttpRequest object or a plain JS object
- * @return {Ext.data.ResultSet} The parsed ResultSet object
- */
- read: function(response) {
- var data = response;
-
- if (response) {
- if (response.responseText) {
- data = this.getResponseData(response);
- }
-
- return this.readRecords(data);
- } else {
- return this.nullResultSet;
- }
- },
-
- /**
- * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call
- * this function before running its own logic and returning the Ext.data.ResultSet instance. For most
- * Readers additional processing should not be needed.
- * @param {Mixed} data The raw data object
- * @return {Ext.data.ResultSet} A ResultSet object
- */
- readRecords: function(data) {
- /**
- * The raw data object that was last passed to readRecords. Stored for further processing if needed
- * @property rawData
- * @type Mixed
- */
- this.rawData = data;
-
- data = this.getData(data);
-
- var root = this.getRoot(data),
- total = root.length,
- success = true,
- value, records, recordCount;
-
- if (this.totalProperty) {
- value = parseInt(this.getTotal(data), 10);
- if (!isNaN(value)) {
- total = value;
- }
- }
-
- if (this.successProperty) {
- value = this.getSuccess(data);
- if (value === false || value === 'false') {
- success = false;
- }
- }
-
- records = this.extractData(root, true);
- recordCount = records.length;
-
- return new Ext.data.ResultSet({
- total : total || recordCount,
- count : recordCount,
- records: records,
- success: success
- });
- },
-
- /**
- * Returns extracted, type-cast rows of data. Iterates to call #extractValues for each row
- * @param {Object[]/Object} data-root from server response
- * @param {Boolean} returnRecords [false] Set true to return instances of Ext.data.Record
- * @private
- */
- extractData : function(root, returnRecords) {
- var values = [],
- records = [],
- Model = this.model,
- length = root.length,
- idProp = this.idProperty,
- node, id, record, i;
-
- for (i = 0; i < length; i++) {
- node = root[i];
- values = this.extractValues(node);
- id = this.getId(node);
-
- if (returnRecords === true) {
- record = new Model(values, id);
- record.raw = node;
- records.push(record);
-
- if (this.implicitIncludes) {
- this.readAssociated(record, node);
- }
- } else {
- values[idProp] = id;
- records.push(values);
- }
- }
-
- return records;
- },
-
- /**
- * @private
- * Loads a record's associations from the data object. This prepopulates hasMany and belongsTo associations
- * on the record provided.
- * @param {Ext.data.Model} record The record to load associations for
- * @param {Mixed} data The data object
- * @return {String} Return value description
- */
- readAssociated: function(record, data) {
- var associations = record.associations.items,
- length = associations.length,
- association, associationName, associatedModel, associationData, inverseAssociation, proxy, reader, store, i;
-
- for (i = 0; i < length; i++) {
- association = associations[i];
- associationName = association.name;
- associationData = this.getAssociatedDataRoot(data, association.associationKey || associationName);
- associatedModel = association.associatedModel;
-
- if (associationData) {
- proxy = associatedModel.proxy;
-
- // if the associated model has a Reader already, use that, otherwise attempt to create a sensible one
- if (proxy) {
- reader = proxy.getReader();
- } else {
- reader = new this.constructor({
- model: association.associatedName
- });
- }
-
- if (association.type == 'hasMany') {
- store = record[associationName]();
-
- store.add.apply(store, reader.read(associationData).records);
-
- //now that we've added the related records to the hasMany association, set the inverse belongsTo
- //association on each of them if it exists
- inverseAssociation = associatedModel.prototype.associations.findBy(function(assoc) {
- return assoc.type == 'belongsTo' && assoc.associatedName == record.constructor.modelName;
- });
-
- //if the inverse association was found, set it now on each record we've just created
- if (inverseAssociation) {
- store.data.each(function(associatedRecord) {
- associatedRecord[inverseAssociation.instanceName] = record;
- });
- }
-
- } else if (association.type == 'belongsTo') {
- record[association.instanceName] = reader.read([associationData]).records[0];
- }
- }
- }
- },
-
- /**
- * @private
- * Used internally by {@link #readAssociated}. Given a data object (which could be json, xml etc) for a specific
- * record, this should return the relevant part of that data for the given association name. This is only really
- * needed to support the XML Reader, which has to do a query to get the associated data object
- * @param {Mixed} data The raw data object
- * @param {String} associationName The name of the association to get data for (uses associationKey if present)
- * @return {Mixed} The root
- */
- getAssociatedDataRoot: function(data, associationName) {
- return data[associationName];
- },
-
- /**
- * @private
- * Given an object representing a single model instance's data, iterates over the model's fields and
- * builds an object with the value for each field.
- * @param {Object} data The data object to convert
- * @return {Object} Data object suitable for use with a model constructor
- */
- extractValues: function(data) {
- var fields = this.model.prototype.fields.items,
- length = fields.length,
- output = {},
- field, value, i;
-
- for (i = 0; i < length; i++) {
- field = fields[i];
- value = this.extractorFunctions[i](data) || field.defaultValue;
-
- output[field.name] = value;
- }
-
- return output;
- },
-
- /**
- * @private
- * By default this function just returns what is passed to it. It can be overridden in a subclass
- * to return something else. See XmlReader for an example.
- * @param {Object} data The data object
- * @return {Object} The normalized data object
- */
- getData: function(data) {
- return data;
- },
-
- /**
- * @private
- * This will usually need to be implemented in a subclass. Given a generic data object (the type depends on the type
- * of data we are reading), this function should return the object as configured by the Reader's 'root' meta data config.
- * See XmlReader's getRoot implementation for an example. By default the same data object will simply be returned.
- * @param {Mixed} data The data object
- * @return {Mixed} The same data object
- */
- getRoot: function(data) {
- return data;
- },
-
- /**
- * Takes a raw response object (as passed to this.read) and returns the useful data segment of it. This must be implemented by each subclass
- * @param {Object} response The responce object
- * @return {Object} The useful data from the response
- */
- getResponseData: function(response) {
- throw new Error("getResponseData must be implemented in the Ext.data.Reader subclass");
- },
-
- /**
- * @private
- * Reconfigures the meta data tied to this Reader
- */
- onMetaChange : function(meta) {
- var fields = meta.fields,
- newModel;
-
- Ext.apply(this, meta);
-
- if (fields) {
- newModel = Ext.regModel("JsonReader-Model" + Ext.id(), {fields: fields});
- this.setModel(newModel, true);
- } else {
- this.buildExtractors(true);
- }
- },
-
- /**
- * @private
- * This builds optimized functions for retrieving record data and meta data from an object.
- * Subclasses may need to implement their own getRoot function.
- * @param {Boolean} force True to automatically remove existing extractor functions first (defaults to false)
- */
- buildExtractors: function(force) {
- if (force === true) {
- delete this.extractorFunctions;
- }
-
- if (this.extractorFunctions) {
- return;
- }
-
- var idProp = this.id || this.idProperty,
- totalProp = this.totalProperty,
- successProp = this.successProperty,
- messageProp = this.messageProperty;
-
- //build the extractors for all the meta data
- if (totalProp) {
- this.getTotal = this.createAccessor(totalProp);
- }
-
- if (successProp) {
- this.getSuccess = this.createAccessor(successProp);
- }
-
- if (messageProp) {
- this.getMessage = this.createAccessor(messageProp);
- }
-
- if (idProp) {
- var accessor = this.createAccessor(idProp);
-
- this.getId = function(record) {
- var id = accessor(record);
-
- return (id == undefined || id == '') ? null : id;
- };
- } else {
- this.getId = function() {
- return null;
- };
- }
- this.buildFieldExtractors();
- },
-
- /**
- * @private
- */
- buildFieldExtractors: function() {
- //now build the extractors for all the fields
- var fields = this.model.prototype.fields.items,
- ln = fields.length,
- i = 0,
- extractorFunctions = [],
- field, map;
-
- for (; i < ln; i++) {
- field = fields[i];
- map = (field.mapping !== undefined && field.mapping !== null) ? field.mapping : field.name;
-
- extractorFunctions.push(this.createAccessor(map));
- }
-
- this.extractorFunctions = extractorFunctions;
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.data.Writer
- * @extends Object
- *
- * <p>Base Writer class used by most subclasses of {@link Ext.data.ServerProxy}. This class is
- * responsible for taking a set of {@link Ext.data.Operation} objects and a {@link Ext.data.Request}
- * object and modifying that request based on the Operations.</p>
- *
- * <p>For example a {@link Ext.data.JsonWriter} would format the Operations and their {@link Ext.data.Model}
- * instances based on the config options passed to the {@link Ext.data.JsonWriter JsonWriter's} constructor.</p>
- *
- * <p>Writers are not needed for any kind of local storage - whether via a
- * {@link Ext.data.WebStorageProxy Web Storage proxy} (see {@link Ext.data.LocalStorageProxy localStorage}
- * and {@link Ext.data.SessionStorageProxy sessionStorage}) or just in memory via a
- * {@link Ext.data.MemoryProxy MemoryProxy}.</p>
- *
- * @constructor
- * @param {Object} config Optional config object
- */
-Ext.data.Writer = Ext.extend(Object, {
-
- constructor: function(config) {
- Ext.apply(this, config);
- },
-
- /**
- * Prepares a Proxy's Ext.data.Request object
- * @param {Ext.data.Request} request The request object
- * @return {Ext.data.Request} The modified request object
- */
- write: function(request) {
- var operation = request.operation,
- records = operation.records || [],
- ln = records.length,
- i = 0,
- data = [];
-
- for (; i < ln; i++) {
- data.push(this.getRecordData(records[i]));
- }
- return this.writeRecords(request, data);
- },
-
- /**
- * Formats the data for each record before sending it to the server. This
- * method should be overridden to format the data in a way that differs from the default.
- * @param {Object} record The record that we are writing to the server.
- * @return {Object} An object literal of name/value keys to be written to the server.
- * By default this method returns the data property on the record.
- */
- getRecordData: function(record) {
- return record.data;
- }
-});
-
-Ext.data.WriterMgr.registerType('base', Ext.data.Writer);
-
-/**
- * @author Ed Spencer
- * @class Ext.data.JsonWriter
- * @extends Ext.data.Writer
- *
- * <p>Writer that outputs model data in JSON format</p>
- */
-Ext.data.JsonWriter = Ext.extend(Ext.data.Writer, {
- /**
- * @cfg {String} root The key under which the records in this Writer will be placed. Defaults to 'records'.
- * Example generated request:
-<pre><code>
-{'records': [{name: 'my record'}, {name: 'another record'}]}
-</code></pre>
- */
- root: 'records',
-
- /**
- * @cfg {Boolean} encode True to use Ext.encode() on the data before sending. Defaults to <tt>false</tt>.
- */
- encode: false,
-
- //inherit docs
- writeRecords: function(request, data) {
- if (this.encode === true) {
- data = Ext.encode(data);
- }
-
- request.jsonData = request.jsonData || {};
- request.jsonData[this.root] = data;
-
- return request;
- }
-});
-
-Ext.data.WriterMgr.registerType('json', Ext.data.JsonWriter);
-
-/**
- * @author Ed Spencer
- * @class Ext.data.JsonReader
- * @extends Ext.data.Reader
- *
- * <p>The JSON Reader is used by a Proxy to read a server response that is sent back in JSON format. This usually
- * happens as a result of loading a Store - for example we might create something like this:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: ['id', 'name', 'email']
-});
-
-var store = new Ext.data.Store({
- model: 'User',
- proxy: {
- type: 'ajax',
- url : 'users.json',
- reader: {
- type: 'json'
- }
- }
-});
-</code></pre>
- *
- * <p>The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're
- * not already familiar with them.</p>
- *
- * <p>We created the simplest type of JSON Reader possible by simply telling our {@link Ext.data.Store Store}'s
- * {@link Ext.data.Proxy Proxy} that we want a JSON Reader. The Store automatically passes the configured model to the
- * Store, so it is as if we passed this instead:
- *
-<pre><code>
-reader: {
- type : 'json',
- model: 'User'
-}
-</code></pre>
- *
- * <p>The reader we set up is ready to read data from our server - at the moment it will accept a response like this:</p>
- *
-<pre><code>
-[
- {
- "id": 1,
- "name": "Ed Spencer",
- "email": "ed at sencha.com"
- },
- {
- "id": 2,
- "name": "Abe Elias",
- "email": "abe at sencha.com"
- }
-]
-</code></pre>
- *
- * <p><u>Reading other JSON formats</u></p>
- *
- * <p>If you already have your JSON format defined and it doesn't look quite like what we have above, you can usually
- * pass JsonReader a couple of configuration options to make it parse your format. For example, we can use the
- * {@link #root} configuration to parse data that comes back like this:</p>
- *
-<pre><code>
-{
- "users": [
- {
- "id": 1,
- "name": "Ed Spencer",
- "email": "ed at sencha.com"
- },
- {
- "id": 2,
- "name": "Abe Elias",
- "email": "abe at sencha.com"
- }
- ]
-}
-</code></pre>
- *
- * <p>To parse this we just pass in a {@link #root} configuration that matches the 'users' above:</p>
- *
-<pre><code>
-reader: {
- type: 'json',
- root: 'users'
-}
-</code></pre>
- *
- * <p>Sometimes the JSON structure is even more complicated. Document databases like CouchDB often provide metadata
- * around each record inside a nested structure like this:</p>
- *
-<pre><code>
-{
- "total": 122,
- "offset": 0,
- "users": [
- {
- "id": "ed-spencer-1",
- "value": 1,
- "user": {
- "id": 1,
- "name": "Ed Spencer",
- "email": "ed at sencha.com"
- }
- }
- ]
-}
-</code></pre>
- *
- * <p>In the case above the record data is nested an additional level inside the "users" array as each "user" item has
- * additional metadata surrounding it ('id' and 'value' in this case). To parse data out of each "user" item in the
- * JSON above we need to specify the {@link #record} configuration like this:</p>
- *
-<pre><code>
-reader: {
- type : 'json',
- root : 'users',
- record: 'user'
-}
-</code></pre>
- *
- * <p><u>Response metadata</u></p>
- *
- * <p>The server can return additional data in its response, such as the {@link #totalProperty total number of records}
- * and the {@link #successProperty success status of the response}. These are typically included in the JSON response
- * like this:</p>
- *
-<pre><code>
-{
- "total": 100,
- "success": true,
- "users": [
- {
- "id": 1,
- "name": "Ed Spencer",
- "email": "ed at sencha.com"
- }
- ]
-}
-</code></pre>
- *
- * <p>If these properties are present in the JSON response they can be parsed out by the JsonReader and used by the
- * Store that loaded it. We can set up the names of these properties by specifying a final pair of configuration
- * options:</p>
- *
-<pre><code>
-reader: {
- type : 'json',
- root : 'users',
- totalProperty : 'total',
- successProperty: 'success'
-}
-</code></pre>
- *
- * <p>These final options are not necessary to make the Reader work, but can be useful when the server needs to report
- * an error or if it needs to indicate that there is a lot of data available of which only a subset is currently being
- * returned.</p>
- */
-Ext.data.JsonReader = Ext.extend(Ext.data.Reader, {
- root: '',
-
- /**
- * @cfg {String} record The optional location within the JSON response that the record data itself can be found at.
- * See the JsonReader intro docs for more details. This is not often needed and defaults to undefined.
- */
-
- /**
- * Reads a JSON object and returns a ResultSet. Uses the internal getTotal and getSuccess extractors to
- * retrieve meta data from the response, and extractData to turn the JSON data into model instances.
- * @param {Object} data The raw JSON data
- * @return {Ext.data.ResultSet} A ResultSet containing model instances and meta data about the results
- */
- readRecords: function(data) {
- //this has to be before the call to super because we use the meta data in the superclass readRecords
- if (data.metaData) {
- this.onMetaChange(data.metaData);
- }
-
- /**
- * DEPRECATED - will be removed in Ext JS 5.0. This is just a copy of this.rawData - use that instead
- * @property jsonData
- * @type Mixed
- */
- this.jsonData = data;
-
- return Ext.data.JsonReader.superclass.readRecords.call(this, data);
- },
-
- //inherit docs
- getResponseData: function(response) {
- try {
- var data = Ext.decode(response.responseText);
- }
- catch (ex) {
- throw 'Ext.data.JsonReader.getResponseData: Unable to parse JSON returned by Server.';
- }
-
- if (!data) {
- throw 'Ext.data.JsonReader.getResponseData: JSON object not found';
- }
-
- return data;
- },
-
- //inherit docs
- buildExtractors : function() {
- Ext.data.JsonReader.superclass.buildExtractors.apply(this, arguments);
-
- if (this.root) {
- this.getRoot = this.createAccessor(this.root);
- } else {
- this.getRoot = function(root) {
- return root;
- };
- }
- },
-
- /**
- * @private
- * We're just preparing the data for the superclass by pulling out the record objects we want. If a {@link #record}
- * was specified we have to pull those out of the larger JSON object, which is most of what this function is doing
- * @param {Object} root The JSON root node
- * @return {Array} The records
- */
- extractData: function(root, returnRecords) {
- var recordName = this.record,
- data = [],
- length, i;
-
- if (recordName) {
- length = root.length;
-
- for (i = 0; i < length; i++) {
- data[i] = root[i][recordName];
- }
- } else {
- data = root;
- }
-
- return Ext.data.XmlReader.superclass.extractData.call(this, data, returnRecords);
- },
-
- /**
- * @private
- * Returns an accessor function for the given property string. Gives support for properties such as the following:
- * 'someProperty'
- * 'some.property'
- * 'some["property"]'
- * This is used by buildExtractors to create optimized extractor functions when casting raw data into model instances.
- */
- createAccessor: function() {
- var re = /[\[\.]/;
-
- return function(expr) {
- if (Ext.isEmpty(expr)) {
- return Ext.emptyFn;
- }
- if (Ext.isFunction(expr)) {
- return expr;
- }
- var i = String(expr).search(re);
- if (i >= 0) {
- return new Function('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
- }
- return function(obj) {
- return obj[expr];
- };
- };
- }()
-});
-
-Ext.data.ReaderMgr.registerType('json', Ext.data.JsonReader);
-Ext.data.TreeReader = Ext.extend(Ext.data.JsonReader, {
- extractData : function(root, returnRecords) {
- var records = Ext.data.TreeReader.superclass.extractData.call(this, root, returnRecords),
- ln = records.length,
- i = 0,
- record;
-
- if (returnRecords) {
- for (; i < ln; i++) {
- record = records[i];
- record.doPreload = !!this.getRoot(record.raw);
- }
- }
- return records;
- }
-});
-Ext.data.ReaderMgr.registerType('tree', Ext.data.TreeReader);
-/**
- * @author Ed Spencer
- * @class Ext.data.ArrayReader
- * @extends Ext.data.JsonReader
- *
- * <p>Data reader class to create an Array of {@link Ext.data.Model} objects from an Array.
- * Each element of that Array represents a row of data fields. The
- * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
- * of the field definition if it exists, or the field's ordinal position in the definition.</p>
- *
- * <p><u>Example code:</u></p>
- *
-<pre><code>
-var Employee = Ext.regModel('Employee', {
- fields: [
- 'id',
- {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
- {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
- ]
-});
-
-var myReader = new Ext.data.ArrayReader({
- model: 'Employee'
-}, Employee);
-</code></pre>
- *
- * <p>This would consume an Array like this:</p>
- *
-<pre><code>
-[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
-</code></pre>
- *
- * @constructor
- * Create a new ArrayReader
- * @param {Object} meta Metadata configuration options.
- */
-Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
-
- /**
- * @private
- * Most of the work is done for us by JsonReader, but we need to overwrite the field accessors to just
- * reference the correct position in the array.
- */
- buildExtractors: function() {
- Ext.data.ArrayReader.superclass.buildExtractors.apply(this, arguments);
-
- var fields = this.model.prototype.fields.items,
- length = fields.length,
- extractorFunctions = [],
- i;
-
- for (i = 0; i < length; i++) {
- extractorFunctions.push(function(index) {
- return function(data) {
- return data[index];
- };
- }(fields[i].mapping || i));
- }
-
- this.extractorFunctions = extractorFunctions;
- }
-});
-
-Ext.data.ReaderMgr.registerType('array', Ext.data.ArrayReader);
-
-/**
- * @author Ed Spencer
- * @class Ext.data.ArrayStore
- * @extends Ext.data.Store
- * @ignore
- *
- * <p>Small helper class to make creating {@link Ext.data.Store}s from Array data easier.
- * An ArrayStore will be automatically configured with a {@link Ext.data.ArrayReader}.</p>
- *
- * <p>A store configuration would be something like:</p>
-<pre><code>
-var store = new Ext.data.ArrayStore({
- // store configs
- autoDestroy: true,
- storeId: 'myStore',
- // reader configs
- idIndex: 0,
- fields: [
- 'company',
- {name: 'price', type: 'float'},
- {name: 'change', type: 'float'},
- {name: 'pctChange', type: 'float'},
- {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
- ]
-});
-</code></pre>
- * <p>This store is configured to consume a returned object of the form:
-<pre><code>
-var myData = [
- ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
- ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
- ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
- ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
- ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
-];
-</code></pre>
-*
- * <p>An object literal of this form could also be used as the {@link #data} config option.</p>
- *
- * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
- * <b>{@link Ext.data.ArrayReader ArrayReader}</b>.</p>
- *
- * @constructor
- * @param {Object} config
- * @xtype arraystore
- */
-Ext.data.ArrayStore = Ext.extend(Ext.data.Store, {
- /**
- * @cfg {Ext.data.DataReader} reader @hide
- */
- constructor: function(config) {
- config = config || {};
-
- Ext.applyIf(config, {
- proxy: {
- type: 'memory',
- reader: 'array'
- }
- });
-
- Ext.data.ArrayStore.superclass.constructor.call(this, config);
- },
-
- loadData: function(data, append) {
- if (this.expandData === true) {
- var r = [],
- i = 0,
- ln = data.length;
-
- for (; i < ln; i++) {
- r[r.length] = [data[i]];
- }
-
- data = r;
- }
-
- Ext.data.ArrayStore.superclass.loadData.call(this, data, append);
- }
-});
-Ext.reg('arraystore', Ext.data.ArrayStore);
-
-// backwards compat
-Ext.data.SimpleStore = Ext.data.ArrayStore;
-Ext.reg('simplestore', Ext.data.SimpleStore);
-/**
- * @author Ed Spencer
- * @class Ext.data.JsonStore
- * @extends Ext.data.Store
- * @ignore
- *
- * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
- * A JsonStore will be automatically configured with a {@link Ext.data.JsonReader}.</p>
- *
- * <p>A store configuration would be something like:</p>
- *
-<pre><code>
-var store = new Ext.data.JsonStore({
- // store configs
- autoDestroy: true,
- storeId: 'myStore'
-
- proxy: {
- type: 'ajax',
- url: 'get-images.php',
- reader: {
- type: 'json',
- root: 'images',
- idProperty: 'name'
- }
- },
-
- //alternatively, a {@link Ext.data.Model} name can be given (see {@link Ext.data.Store} for an example)
- fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
-});
-</code></pre>
- *
- * <p>This store is configured to consume a returned object of the form:<pre><code>
-{
- images: [
- {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
- {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
- ]
-}
-</code></pre>
- *
- * <p>An object literal of this form could also be used as the {@link #data} config option.</p>
- *
- * @constructor
- * @param {Object} config
- * @xtype jsonstore
- */
-Ext.data.JsonStore = Ext.extend(Ext.data.Store, {
- /**
- * @cfg {Ext.data.DataReader} reader @hide
- */
- constructor: function(config) {
- config = config || {};
-
- Ext.applyIf(config, {
- proxy: {
- type : 'ajax',
- reader: 'json',
- writer: 'json'
- }
- });
-
- Ext.data.JsonStore.superclass.constructor.call(this, config);
- }
-});
-
-Ext.reg('jsonstore', Ext.data.JsonStore);
-/**
- * @class Ext.data.JsonPStore
- * @extends Ext.data.Store
- * @ignore
- * @private
- * <p><b>NOTE:</b> This class is in need of migration to the new API.</p>
- * <p>Small helper class to make creating {@link Ext.data.Store}s from different domain JSON data easier.
- * A JsonPStore will be automatically configured with a {@link Ext.data.JsonReader} and a {@link Ext.data.ScriptTagProxy ScriptTagProxy}.</p>
- * <p>A store configuration would be something like:<pre><code>
-var store = new Ext.data.JsonPStore({
- // store configs
- autoDestroy: true,
- storeId: 'myStore',
-
- // proxy configs
- url: 'get-images.php',
-
- // reader configs
- root: 'images',
- idProperty: 'name',
- fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
-});
- * </code></pre></p>
- * <p>This store is configured to consume a returned object of the form:<pre><code>
-stcCallback({
- images: [
- {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
- {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
- ]
-})
- * </code></pre>
- * <p>Where stcCallback is the callback name passed in the request to the remote domain. See {@link Ext.data.ScriptTagProxy ScriptTagProxy}
- * for details of how this works.</p>
- * An object literal of this form could also be used as the {@link #data} config option.</p>
- * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
- * <b>{@link Ext.data.JsonReader JsonReader}</b> and <b>{@link Ext.data.ScriptTagProxy ScriptTagProxy}</b>.</p>
- * @constructor
- * @param {Object} config
- * @xtype jsonpstore
- */
-Ext.data.JsonPStore = Ext.extend(Ext.data.Store, {
- /**
- * @cfg {Ext.data.DataReader} reader @hide
- */
- constructor: function(config) {
- Ext.data.JsonPStore.superclass.constructor.call(this, Ext.apply(config, {
- reader: new Ext.data.JsonReader(config),
- proxy : new Ext.data.ScriptTagProxy(config)
- }));
- }
-});
-
-Ext.reg('jsonpstore', Ext.data.JsonPStore);
-
-/**
- * @author Ed Spencer
- * @class Ext.data.XmlWriter
- * @extends Ext.data.Writer
- *
- * <p>Writer that outputs model data in XML format</p>
- */
-Ext.data.XmlWriter = Ext.extend(Ext.data.Writer, {
- /**
- * @cfg {String} documentRoot The name of the root element of the document. Defaults to <tt>'xmlData'</tt>.
- */
- documentRoot: 'xmlData',
-
- /**
- * @cfg {String} header A header to use in the XML document (such as setting the encoding or version).
- * Defaults to <tt>''</tt>.
- */
- header: '',
-
- /**
- * @cfg {String} record The name of the node to use for each record. Defaults to <tt>'record'</tt>.
- */
- record: 'record',
-
- //inherit docs
- writeRecords: function(request, data) {
- var tpl = this.buildTpl(request, data);
-
- request.xmlData = tpl.apply(data);
-
- return request;
- },
-
- buildTpl: function(request, data) {
- if (this.tpl) {
- return this.tpl;
- }
-
- var tpl = [],
- root = this.documentRoot,
- record = this.record,
- first,
- key;
-
- if (this.header) {
- tpl.push(this.header);
- }
- tpl.push('<', root, '>');
- if (data.length > 0) {
- tpl.push('<tpl for="."><', record, '>');
- first = data[0];
- for (key in first) {
- if (first.hasOwnProperty(key)) {
- tpl.push('<', key, '>{', key, '}</', key, '>');
- }
- }
- tpl.push('</', record, '></tpl>');
- }
- tpl.push('</', root, '>');
- this.tpl = new Ext.XTemplate(tpl.join(''));
- return this.tpl;
- }
-});
-
-Ext.data.WriterMgr.registerType('xml', Ext.data.XmlWriter);
-/**
- * @author Ed Spencer
- * @class Ext.data.XmlReader
- * @extends Ext.data.Reader
- *
- *
- * <p>The XML Reader is used by a Proxy to read a server response that is sent back in XML format. This usually
- * happens as a result of loading a Store - for example we might create something like this:</p>
- *
-<pre><code>
-Ext.regModel('User', {
- fields: ['id', 'name', 'email']
-});
-
-var store = new Ext.data.Store({
- model: 'User',
- proxy: {
- type: 'ajax',
- url : 'users.xml',
- reader: {
- type: 'xml',
- record: 'user'
- }
- }
-});
-</code></pre>
- *
- * <p>The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're
- * not already familiar with them.</p>
- *
- * <p>We created the simplest type of XML Reader possible by simply telling our {@link Ext.data.Store Store}'s
- * {@link Ext.data.Proxy Proxy} that we want a XML Reader. The Store automatically passes the configured model to the
- * Store, so it is as if we passed this instead:
- *
-<pre><code>
-reader: {
- type : 'xml',
- model: 'User',
- record: 'user'
-}
-</code></pre>
- *
- * <p>The reader we set up is ready to read data from our server - at the moment it will accept a response like this:</p>
- *
-<pre><code>
-<?xml version="1.0" encoding="UTF-8"?>
-<user>
- <id>1</id>
- <name>Ed Spencer</name>
- <email>ed at sencha.com</email>
-</user>
-<user>
- <id>2</id>
- <name>Abe Elias</name>
- <email>abe at sencha.com</email>
-</user>
-</code></pre>
- *
- * <p>The XML Reader uses the configured {@link #record} option to pull out the data for each record - in this case we
- * set record to 'user', so each <user> above will be converted into a User model.</p>
- *
- * <p><u>Reading other XML formats</u></p>
- *
- * <p>If you already have your XML format defined and it doesn't look quite like what we have above, you can usually
- * pass XmlReader a couple of configuration options to make it parse your format. For example, we can use the
- * {@link #root} configuration to parse data that comes back like this:</p>
- *
-<pre><code>
-<?xml version="1.0" encoding="UTF-8"?>
-<users>
- <user>
- <id>1</id>
- <name>Ed Spencer</name>
- <email>ed at sencha.com</email>
- </user>
- <user>
- <id>2</id>
- <name>Abe Elias</name>
- <email>abe at sencha.com</email>
- </user>
-</users>
-</code></pre>
- *
- * <p>To parse this we just pass in a {@link #root} configuration that matches the 'users' above:</p>
- *
-<pre><code>
-reader: {
- type : 'xml',
- root : 'users',
- record: 'user'
-}
-</code></pre>
- *
- * <p>Note that XmlReader doesn't care whether your {@link #root} and {@link #record} elements are nested deep inside
- * a larger structure, so a response like this will still work:
- *
-<pre><code>
-<?xml version="1.0" encoding="UTF-8"?>
-<deeply>
- <nested>
- <xml>
- <users>
- <user>
- <id>1</id>
- <name>Ed Spencer</name>
- <email>ed at sencha.com</email>
- </user>
- <user>
- <id>2</id>
- <name>Abe Elias</name>
- <email>abe at sencha.com</email>
- </user>
- </users>
- </xml>
- </nested>
-</deeply>
-</code></pre>
- *
- * <p><u>Response metadata</u></p>
- *
- * <p>The server can return additional data in its response, such as the {@link #totalProperty total number of records}
- * and the {@link #successProperty success status of the response}. These are typically included in the XML response
- * like this:</p>
- *
-<pre><code>
-<?xml version="1.0" encoding="UTF-8"?>
-<total>100</total>
-<success>true</success>
-<users>
- <user>
- <id>1</id>
- <name>Ed Spencer</name>
- <email>ed at sencha.com</email>
- </user>
- <user>
- <id>2</id>
- <name>Abe Elias</name>
- <email>abe at sencha.com</email>
- </user>
-</users>
-</code></pre>
- *
- * <p>If these properties are present in the XML response they can be parsed out by the XmlReader and used by the
- * Store that loaded it. We can set up the names of these properties by specifying a final pair of configuration
- * options:</p>
- *
-<pre><code>
-reader: {
- type: 'xml',
- root: 'users',
- totalProperty : 'total',
- successProperty: 'success'
-}
-</code></pre>
- *
- * <p>These final options are not necessary to make the Reader work, but can be useful when the server needs to report
- * an error or if it needs to indicate that there is a lot of data available of which only a subset is currently being
- * returned.</p>
- *
- * <p><u>Response format</u></p>
- *
- * <p><b>Note:</b> in order for the browser to parse a returned XML document, the Content-Type header in the HTTP
- * response must be set to "text/xml" or "application/xml". This is very important - the XmlReader will not
- * work correctly otherwise.</p>
- */
-Ext.data.XmlReader = Ext.extend(Ext.data.Reader, {
- /**
- * @private
- * Creates a function to return some particular key of data from a response. The totalProperty and
- * successProperty are treated as special cases for type casting, everything else is just a simple selector.
- * @param {String} key
- * @return {Function}
- */
-
- /**
- * @cfg {String} record The DomQuery path to the repeated element which contains record information.
- * <b>This is an alias for the {@link #root} config option.</b>
- */
-
- createAccessor: function() {
- var selectValue = function(key, root, defaultValue){
- var node = Ext.DomQuery.selectNode(key, root),
- val;
- if (node && node.firstChild) {
- val = node.firstChild.nodeValue;
- }
- return Ext.isEmpty(val) ? defaultValue : val;
- };
-
- return function(key) {
- var fn;
-
- if (key == this.totalProperty) {
- fn = function(root, defaultValue) {
- var value = selectValue(key, root, defaultValue);
- return parseFloat(value);
- };
- }
-
- else if (key == this.successProperty) {
- fn = function(root, defaultValue) {
- var value = selectValue(key, root, true);
- return (value !== false && value !== 'false');
- };
- }
-
- else {
- fn = function(root, defaultValue) {
- return selectValue(key, root, defaultValue);
- };
- }
-
- return fn;
- };
- }(),
-
- //inherit docs
- getResponseData: function(response) {
- var xml = response.responseXML;
-
- if (!xml) {
- throw {message: 'Ext.data.XmlReader.read: XML data not found'};
- }
-
- return xml;
- },
-
- /**
- * Normalizes the data object
- * @param {Object} data The raw data object
- * @return {Object} Returns the documentElement property of the data object if present, or the same object if not
- */
- getData: function(data) {
- return data.documentElement || data;
- },
-
- /**
- * @private
- * Given an XML object, returns the Element that represents the root as configured by the Reader's meta data
- * @param {Object} data The XML data object
- * @return {Element} The root node element
- */
- getRoot: function(data) {
- var nodeName = data.nodeName,
- root = this.root;
-
- if (!root || (nodeName && nodeName == root)) {
- return data;
- } else {
- return Ext.DomQuery.selectNode(root, data);
- }
- },
-
-
- //EVERYTHING BELOW THIS LINE WILL BE DEPRECATED IN EXT JS 5.0
-
-
- /**
- * @cfg {String} idPath DEPRECATED - this will be removed in Ext JS 5.0. Please use idProperty instead
- */
-
- /**
- * @cfg {String} id DEPRECATED - this will be removed in Ext JS 5.0. Please use idProperty instead
- */
-
- /**
- * @cfg {String} success DEPRECATED - this will be removed in Ext JS 5.0. Please use successProperty instead
- */
-
- /**
- * @constructor
- * @ignore
- * TODO: This can be removed in 5.0 as all it does is support some deprecated config
- */
- constructor: function(config) {
- config = config || {};
-
- // backwards compat, convert idPath or id / success
- // DEPRECATED - remove this in 5.0
-
- Ext.applyIf(config, {
- idProperty : config.idPath || config.id,
- successProperty: config.success
- });
-
- Ext.data.XmlReader.superclass.constructor.call(this, config);
- },
-
- /**
- * @private
- * We're just preparing the data for the superclass by pulling out the record nodes we want
- * @param {Element} root The XML root node
- * @return {Array} The records
- */
- extractData: function(root, returnRecords) {
- var recordName = this.record;
-
- if (recordName != root.nodeName) {
- root = Ext.DomQuery.select(recordName, root);
- } else {
- root = [root];
- }
-
- return Ext.data.XmlReader.superclass.extractData.call(this, root, returnRecords);
- },
-
- /**
- * @private
- * See Ext.data.Reader's getAssociatedDataRoot docs
- * @param {Mixed} data The raw data object
- * @param {String} associationName The name of the association to get data for (uses associationKey if present)
- * @return {Mixed} The root
- */
- getAssociatedDataRoot: function(data, associationName) {
- return Ext.DomQuery.select(associationName, data)[0];
- },
-
- /**
- * Parses an XML document and returns a ResultSet containing the model instances
- * @param {Object} doc Parsed XML document
- * @return {Ext.data.ResultSet} The parsed result set
- */
- readRecords: function(doc) {
- //it's possible that we get passed an array here by associations. Make sure we strip that out (see Ext.data.Reader#readAssociated)
- if (Ext.isArray(doc)) {
- doc = doc[0];
- }
-
- /**
- * DEPRECATED - will be removed in Ext JS 5.0. This is just a copy of this.rawData - use that instead
- * @property xmlData
- * @type Object
- */
- this.xmlData = doc;
-
- return Ext.data.XmlReader.superclass.readRecords.call(this, doc);
- }
-});
-
-Ext.data.ReaderMgr.registerType('xml', Ext.data.XmlReader);
-/**
- * @author Ed Spencer
- * @class Ext.data.XmlStore
- * @extends Ext.data.Store
- * @private
- * @ignore
- * <p>Small helper class to make creating {@link Ext.data.Store}s from XML data easier.
- * A XmlStore will be automatically configured with a {@link Ext.data.XmlReader}.</p>
- * <p>A store configuration would be something like:<pre><code>
-var store = new Ext.data.XmlStore({
- // store configs
- autoDestroy: true,
- storeId: 'myStore',
- url: 'sheldon.xml', // automatically configures a HttpProxy
- // reader configs
- record: 'Item', // records will have an "Item" tag
- idPath: 'ASIN',
- totalRecords: '@TotalResults'
- fields: [
- // set up the fields mapping into the xml doc
- // The first needs mapping, the others are very basic
- {name: 'Author', mapping: 'ItemAttributes > Author'},
- 'Title', 'Manufacturer', 'ProductGroup'
- ]
-});
- * </code></pre></p>
- * <p>This store is configured to consume a returned object of the form:<pre><code>
-<?xml version="1.0" encoding="UTF-8"?>
-<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2009-05-15">
- <Items>
- <Request>
- <IsValid>True</IsValid>
- <ItemSearchRequest>
- <Author>Sidney Sheldon</Author>
- <SearchIndex>Books</SearchIndex>
- </ItemSearchRequest>
- </Request>
- <TotalResults>203</TotalResults>
- <TotalPages>21</TotalPages>
- <Item>
- <ASIN>0446355453</ASIN>
- <DetailPageURL>
- http://www.amazon.com/
- </DetailPageURL>
- <ItemAttributes>
- <Author>Sidney Sheldon</Author>
- <Manufacturer>Warner Books</Manufacturer>
- <ProductGroup>Book</ProductGroup>
- <Title>Master of the Game</Title>
- </ItemAttributes>
- </Item>
- </Items>
-</ItemSearchResponse>
- * </code></pre>
- * An object literal of this form could also be used as the {@link #data} config option.</p>
- * <p><b>Note:</b> Although not listed here, this class accepts all of the configuration options of
- * <b>{@link Ext.data.XmlReader XmlReader}</b>.</p>
- * @constructor
- * @param {Object} config
- * @xtype xmlstore
- */
-Ext.data.XmlStore = Ext.extend(Ext.data.Store, {
- /**
- * @cfg {Ext.data.DataReader} reader @hide
- */
- constructor: function(config){
- config = config || {};
- config = config || {};
-
- Ext.applyIf(config, {
- proxy: {
- type: 'ajax',
- reader: 'xml',
- writer: 'xml'
- }
- });
- Ext.data.XmlStore.superclass.constructor.call(this, config);
- }
-});
-Ext.reg('xmlstore', Ext.data.XmlStore);
-
-/**
- * @class Ext.History
- * @extends Ext.util.Observable
- * @ignore
- * @private
- *
- * Mobile-optimized port of Ext.History. Note - iPad on iOS < 4.2 does not have HTML5 History support so we still
- * have to poll for changes.
- */
-Ext.History = new Ext.util.Observable({
- constructor: function() {
- Ext.History.superclass.constructor.call(this, config);
-
- this.addEvents(
- /**
- * @event change
- */
- 'change'
- );
- },
-
- /**
- * @private
- * Initializes event listeners
- */
- init: function() {
- var me = this;
-
- me.setToken(window.location.hash);
-
- if (Ext.supports.History) {
- window.addEventListener('hashchange', this.onChange);
- } else {
- setInterval(function() {
- var newToken = me.cleanToken(window.location.hash),
- oldToken = me.getToken();
-
- if (newToken != oldToken) {
- me.onChange();
- }
- }, 50);
- }
- },
-
- /**
- * @private
- * Event listener for the hashchange event
- */
- onChange: function() {
- var me = Ext.History,
- newToken = me.cleanToken(window.location.hash);
-
- if (me.token != newToken) {
- me.fireEvent('change', newToken);
- }
-
- me.setToken(newToken);
- },
-
- /**
- * Sets a new token, stripping of the leading # if present. Does not fire the 'change' event
- * @param {String} token The new token
- * @return {String} The cleaned token
- */
- setToken: function(token) {
- return this.token = this.cleanToken(token);
- },
-
- /**
- * @private
- * Cleans a token by stripping off the leading # if it is present
- * @param {String} token The unclean token
- * @return {String} The clean token
- */
- cleanToken: function(token) {
- return token[0] == '#' ? token.substr(1) : token;
- },
-
- /**
- * Returns the current history token
- * @return {String} The current token
- */
- getToken: function() {
- return this.token;
- },
-
- /**
- * Adds a token to the history stack by updation the address bar hash
- * @param {String} token The new token
- */
- add: function(token) {
- window.location.hash = this.setToken(token);
-
- if (!Ext.supports.History) {
- this.onChange();
- }
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.ControllerManager
- * @extends Ext.AbstractManager
- * @singleton
- *
- * <p>Keeps track of all of the registered controllers. This should very rarely need to be used by developers. This
- * is simply an {@link Ext.AbstractManager AbstractManager} with a custom {@link #register} function which sets up
- * the controller and its linked {@link Ext.Application application}.</p>
- */
-Ext.ControllerManager = new Ext.AbstractManager({
- register: function(id, options) {
- options.id = id;
-
- Ext.applyIf(options, {
- application: Ext.ApplicationManager.currentApplication
- });
-
- var controller = new Ext.Controller(options);
-
- if (controller.init) {
- controller.init();
- }
-
- this.all.add(controller);
-
- return controller;
- }
-});
-
-/**
- * Shorthand for {@link Ext.ControllerMgr#register}
- * Creates a new Controller class from the specified config object. See {@link Ext.Controller} for full examples.
- *
- * @param {Object} config A configuration object for the Controller you wish to create.
- * @return {Ext.Controller} The newly registered Controller
- * @member Ext
- * @method regController
- */
-Ext.regController = function() {
- return Ext.ControllerManager.register.apply(Ext.ControllerManager, arguments);
-};
-/**
- * @author Ed Spencer
- * @class Ext.Controller
- * @extends Ext.util.Observable
- *
- * @constructor
- */
-Ext.Controller = Ext.extend(Ext.util.Observable, {
- constructor: function(config) {
- this.addEvents(
- /**
- * @event instance-created
- * Fired when a new model instance has been successfully created by this controller
- * @param {Ext.data.Model} instance The newly-created model instance
- */
- 'instance-created',
-
- /**
- * @event instance-creation-failed
- * Fired when an attempt at saving a new instance failed
- * @param {Ext.data.Model} instance The instance that could not be saved
- * @param {Object} errors The set of errors (if any) that caused the failure
- */
- 'instance-creation-failed',
-
- /**
- * @event instance-updated
- * Fired when an existing model instance has been successfully updated by this controller
- * @param {Ext.data.Model} instance The instance that was updated
- */
- 'instance-updated',
-
- /**
- * @event instance-update-failed
- * Fired when an update to existing model instance could not be successfully completed
- * @param {Ext.data.Model} instance The instance that could not be updated
- * @param {Object} errors The set of errors (if any) that caused the failure
- */
- 'instance-update-failed',
-
- /**
- * @event instance-destroyed
- * Fired when an existing instance has been successfully destroyed by this controller
- * @param {Ext.data.Model} instance The instance that was destroyed
- */
- 'instance-destroyed',
-
- /**
- * @event instance-destruction-failed
- * Fired when an existing instance could not be destroyed
- * @param {Ext.data.Model} instance The instance that could not be destroyed
- * @param {Object} errors The set of errors (if any) that caused the failure
- */
- 'instance-destruction-failed'
- );
-
- Ext.Controller.superclass.constructor.call(this, config);
-
- Ext.apply(this, config || {});
-
- if (typeof this.model == 'string') {
- this.model = Ext.ModelMgr.getModel(this.model);
- }
- },
-
- index: function() {
- this.render('index', {
- listeners: {
- scope : this,
- edit : this.edit,
- build : this.build,
- create : this.onCreateInstance,
- destroy: this.onDestroyInstance
- }
- });
- },
-
- /**
- * Renders the edit form for a given model instance
- * @param {Ext.data.Model} instance The instance to edit
- */
- edit: function(instance) {
- var view = this.render('edit', {
- listeners: this.getEditListeners()
- });
-
- view.loadModel(instance);
- },
-
- /**
- * Callback automatically tied to the index view's 'build' event. By default this just renders the registered
- * 'build' view
- */
- build: function() {
- this.render('build', {
- listeners: this.getBuildListeners()
- });
- },
-
- /**
- * Saves a phantom Model instance via its configured Proxy. Fires the 'instance-created' event if successful,
- * the 'instance-creation-failed' event if not.
- * @param {Object} data The data to create the instance from
- * @param {Object} options Optional options object containing callbacks for success and failure plus optional scope
- */
- create: function(data, options) {
- options = options || {};
-
- var model = this.getModel(),
- instance = new model(data),
- successCb = options.success,
- failureCb = options.failure,
- scope = options.scope || this;
-
- instance.save({
- scope : this,
- success: function(instance) {
- if (typeof successCb == 'function') {
- successCb.call(scope, instance);
- }
-
- this.fireEvent('instance-created', instance);
- },
- failure: function(instance, errors) {
- if (typeof failureCb == 'function') {
- failureCb.call(scope, instance, errors);
- }
-
- this.fireEvent('instance-creation-failed', instance, errors);
- }
- });
- },
-
- /**
- * Updates an existing model instance by applying optional updates to it and attempting to save
- * @param {Ext.data.Model} instance The existing instance
- * @param {Object} updates Optional additional updates to apply to the instance before saving
- * @param {Object} options success and failure callback functions
- */
- update: function(instance, updates, options) {
- options = options || {};
-
- var successCb = options.success,
- failureCb = options.failure,
- scope = options.scope || this;
-
- if (Ext.isObject(updates)) {
- instance.set(updates);
- }
-
- instance.save({
- scope : this,
- success: function(instance) {
- if (typeof successCb == 'function') {
- successCb.call(scope, instance);
- }
-
- this.fireEvent('instance-updated', instance);
- },
- failure: function(instance, errors) {
- if (typeof failureCb == 'function') {
- failureCb.call(scope, instance, errors);
- }
-
- this.fireEvent('instance-update-failed', instance, errors);
- }
- });
- },
-
- /**
- * Destroys one or more existing, previously saved model instances
- * @param {Ext.data.Model} instance The model instance to destroy
- * @param {Object} options success and failure callbacks
- */
- destroy: function(instance, options) {
- options = options || {};
-
- var successCb = options.success,
- failureCb = options.failure,
- scope = options.scope || this;
-
- instance.destroy({
- scope : this,
- success: function(instance) {
- if (typeof successCb == 'function') {
- successCb.call(scope, instance);
- }
-
- this.fireEvent('instance-destroyed', instance);
- },
- failure: function(instance, errors) {
- if (typeof failureCb == 'function') {
- failureCb.call(scope, instance, errors);
- }
-
- this.fireEvent('instance-destruction-failed', instance, errors);
- }
- });
- },
-
- /**
- * Returns the listeners to attach to the view rendered by the {@link #build} action. By default this returns listeners
- * for save and cancel, but this can be overridden
- * @return {Object} listeners
- */
- getBuildListeners: function() {
- return {
- scope : this,
- save : this.onCreateInstance,
- cancel: this.onCancelBuild
- };
- },
-
- /**
- * Returns the listeners to attach to the view rendered by the {@link #edit} action. By default this returns listeners
- * for save and cancel, but this can be overridden
- * @return {Object} listeners
- */
- getEditListeners: function() {
- return {
- scope : this,
- save : this.onUpdateInstance,
- cancel: this.onCancelEdit
- };
- },
-
- /**
- * Handler for the 'cancel' event fired by an {@link #edit} view. By default this just closes the view
- * @param {Ext.Component} view The edit form
- */
- onCancelEdit: function(view) {
- return this.closeView(view);
- },
-
- /**
- * Handler for the 'cancel' event fired by an {@link #build} view. By default this just closes the view
- * @param {Ext.Component} view The build form
- */
- onCancelBuild: function(view) {
- return this.closeView(view);
- },
-
- /**
- * Callback automatically tied to the index view's 'create' event. By default this just calls the controller's
- * create function with the data and some basic callbacks to handle errors or show success. Can be overridden
- * to provide custom behavior
- * @param {Ext.View} view The view instance that fired the event
- */
- onCreateInstance: function(view) {
- this.create(view.getValues(), {
- scope : this,
- success: function(instance) {
- this.closeView(view);
- },
- failure: function(instance, errors) {
- console.log('fail');
- }
- });
- },
-
- /**
- * Callback automatically tied to the index view's 'update' event. By default this just calls the controller's
- * update function with the data and some basic callbacks to handle errors or show success. Can be overridden
- * to provide custom behavior
- * @param {Ext.Component} view The view instance that fired the event
- */
- onUpdateInstance: function(view) {
- this.update(view.getRecord(), view.getValues(), {
- scope : this,
- success: function(instance) {
- this.closeView(view);
- },
- failure: function(instance, errors) {
-
- }
- });
- },
-
- /**
- * Callback automatically tied to the index view's 'destroy' event. By default that just calls the controller's
- * destroy function with the model instance and some basic callbacks to handle errors or show success. Can be
- * overridden to provide custom behavior.
- * @param {Ext.data.Model} instance The instance to destroy
- * @param {Ext.View} view The view instance that fired the event
- */
- onDestroyInstance: function(instance, view) {
- this.destroy(instance, {
- scope : this,
- success: function(instance) {
-
- },
- failure: function(instance, errors) {
-
- }
- });
- },
-
- /**
- * Sets the default container that components rendered using {@link #render} will be added to.
- * In many applications there is a fixed navigation panel and a content panel - the content
- * panel would usually form the render target in this type of setup.
- * @param {Ext.Container} target The container to add rendered components to
- */
- setRenderTarget: function(target) {
- /**
- * @property renderTarget
- * @type Ext.Container
- * The current {@link #setRenderTarget render target}. Read only
- */
- Ext.Controller.renderTarget = target;
- },
-
- /**
- * Renders a given view based on a registered name
- * @param {String} viewName The name of the view to render
- * @param {Object} config Optional config object
- * @return {Ext.View} The view instance
- */
- render: function(config, target) {
- var Controller = Ext.Controller,
- application = this.application,
- profile = application ? application.currentProfile : undefined,
- profileTarget, view;
-
- Ext.applyIf(config, {
- profile: profile
- });
-
- view = Ext.ComponentMgr.create(config);
-
- if (target !== false) {
- //give the current Ext.Profile a chance to set the target
- profileTarget = profile ? profile.getRenderTarget(config, application) : target;
-
- if (target == undefined) {
- target = profileTarget || (application ? application.defaultTarget : undefined);
- }
-
- if (typeof target == 'string') {
- target = Ext.getCmp(target);
- }
-
- if (target != undefined && target.add) {
- if (profile) {
- profile.beforeLayout(view, target, application);
- }
-
- target.add(view);
-
- if (target.layout && target.layout.setActiveItem) {
- target.layout.setActiveItem(view);
- }
-
- target.doComponentLayout();
-
- if (profile) {
- profile.afterLayout(view, target, application);
- }
- }
- }
-
- return view;
- },
-
- /**
- * This function allows you to add listeners to a view
- * in a convenient way
- */
- control : function(view, actions, itemName) {
- if (!view || !view.isView) {
- throw 'Trying to control a view that doesnt exist';
- }
-
- var item = itemName ? view.refs[itemName] : view,
- key, value, name, child, listener;
-
- if (!item) {
- throw "No item called " + itemName + " found inside the " + view.name + " view.";
- }
-
- for (key in actions) {
- value = actions[key];
-
- // If it is an object, it can either be a listener with a handler defined
- // in which case the key is the event name, or it can be an object containing
- // listener(s), in which case key will be the items name
- if (Ext.isObject(value) && !value.fn) {
- this.control(view, value, key);
- }
- else {
- // Now hopefully we can be sure that key is an event name. We will loop over all
- // child items and enable bubbling for this event
- if (item.refs) {
- for (name in item.refs) {
- child = item.refs[name];
- if (child.isObservable && child.events[key]) {
- child.enableBubble(key);
- }
- }
- }
-
- if (!value.fn) {
- listener = {};
- listener[key] = value;
- listener.scope = this;
- }
- else {
- listener = value;
- if (listener.scope === undefined) {
- listener.scope = this;
- }
- }
-
- // Finally we bind the listener on this item
- item.addListener(listener);
- }
- }
-
- return view;
- },
-
- /**
- * Returns the constructor for the model type linked to this controller
- * @return {Ext.data.Model} The model constructor
- */
- getModel: function() {
- return Ext.ModelMgr.getModel(this.model);
- },
-
- /**
- * @private
- * Used internally whenever we want to remove a component from its parent container. See onCancelEdit and onCancelBuild
- * @param {Ext.Component} view The component to close
- */
- closeView: function(view) {
- var ownerCt = view.ownerCt;
-
- if (ownerCt) {
- ownerCt.remove(view);
- ownerCt.doLayout();
- ownerCt.setActiveItem(ownerCt.items.last());
- }
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.util.Dispatcher
- * @extends Ext.util.Observable
- *
- * <p>The Dispatcher class is used to send requests through to a controller action. Usually, only a single Dispatcher
- * is required on the page, and by default a single instance is already created - {@link Ext.Dispatcher}. See the
- * {@link Ext.Dispatcher Dispatcher docs} for details on how this works.</p>
- *
- * @constructor
- */
-Ext.util.Dispatcher = Ext.extend(Ext.util.Observable, {
-
- constructor: function(config) {
- this.addEvents(
- /**
- * @event before-dispatch
- * Fires before an interaction is dispatched. Return false from any listen to cancel the dispatch
- * @param {Ext.Interaction} interaction The Interaction about to be dispatched
- */
- 'before-dispatch',
-
- /**
- * @event dispatch
- * Fired once an Interaction has been dispatched
- * @param {Ext.Interaction} interaction The Interaction that was just dispatched
- */
- 'dispatch'
- );
-
- Ext.util.Dispatcher.superclass.constructor.call(this, config);
- },
-
- /**
- * Dispatches a single interaction to a controller/action pair
- * @param {Object} options Options representing at least the controller and action to dispatch to
- */
- dispatch: function(options) {
- var interaction = new Ext.Interaction(options),
- controller = interaction.controller,
- action = interaction.action,
- History = Ext.History;
-
- if (this.fireEvent('before-dispatch', interaction) !== false) {
- if (History && options.historyUrl) {
- History.suspendEvents(false);
- History.add(options.historyUrl);
- Ext.defer(History.resumeEvents, 100, History);
- }
-
- if (controller && action) {
- controller[action].call(controller, interaction);
- interaction.dispatched = true;
- }
-
- this.fireEvent('dispatch', interaction);
- }
- },
-
- /**
- * Dispatches to a controller/action pair, adding a new url to the History stack
- */
- redirect: function(options) {
- if (options instanceof Ext.data.Model) {
- //compose a route for the model
-
- } else if (typeof options == 'string') {
- //use router
- var route = Ext.Router.recognize(options);
-
- if (route) {
- return this.dispatch(route);
- }
- }
- return null;
- },
-
- /**
- * Convenience method which returns a function that calls Ext.Dispatcher.redirect. Useful when setting
- * up several listeners that should redirect, e.g.:
-<pre><code>
-myComponent.on({
- homeTap : Ext.Dispatcher.createRedirect('home'),
- inboxTap: Ext.Dispatcher.createRedirect('inbox'),
-});
-</code></pre>
- * @param {String/Object} url The url to create the redirect function for
- * @return {Function} The redirect function
- */
- createRedirect: function(url) {
- return function() {
- Ext.Dispatcher.redirect(url);
- };
- }
-});
-
-/**
- * @class Ext.Dispatcher
- * @extends Ext.util.Dispatcher
- *
- * <p>The Dispatcher is responsible for sending requests through to a specific {@link Ext.Controller controller}
- * action. It is usually invoked either by a UI event handler calling {@link Ext#dispatch}, or by the
- * {@link Ext.Router Router} recognizing a change in the page url.</p>
- *
- * <p>Ext.Dispatcher is the default instance of {@link Ext.util.Dispatcher} that is automatically created for every
- * application. Usually it is the only instance that you will need.</p>
- *
- * <p>Let's say we have an application that manages instances of a Contact model using a contacts controller:</p>
- *
-<pre><code>
-Ext.regModel('Contact', {
- fields: ['id', 'name', 'email']
-});
-
-//the controller has a single action - list - which just loads the Contacts and logs them to the console
-Ext.regController('contacts', {
- list: function() {
- new Ext.data.Store({
- model: 'Contact',
- autoLoad: {
- callback: function(contacts) {
- console.log(contacts);
- }
- }
- });
- }
-});
-</code></pre>
- *
- * <p>We can easily dispatch to the contacts controller's list action from anywhere in our app:</p>
- *
-<pre><code>
-Ext.dispatch({
- controller: 'contacts',
- action : 'list',
-
- historyUrl: 'contacts/list',
-
- anotherOption: 'some value'
-});
-</code></pre>
- *
- * <p>The Dispatcher finds the contacts controller and calls its list action. We also passed in a couple of additional
- * options to dispatch - historyUrl and anotherOption. 'historyUrl' is a special parameter which automatically changes
- * the browser's url when passed. For example, if your application is being served from http://yourapp.com, dispatching
- * with the options we passed above would update the url to http://yourapp.com/#contacts/list, as well as calling the
- * controller action as before.</p>
- *
- * <p>We also passed a second configuration into dispatch - anotherOption. We can access this inside our controller
- * action like this:</p>
- *
-<pre><code>
-Ext.regController('contacts', {
- list: function(options) {
- console.log(options.anotherOption); // 'some value'
- }
-});
-</code></pre>
- *
- * <p>We can pass anything in to Ext.dispatch and have it come through to our controller action. Internally, all of the
- * options that we pass to dispatch are rolled into an {@link Ext.Interaction}. Interaction is a very simple class that
- * represents a single request into the application - typically the controller and action names plus any additional
- * information like the Model instance that a particular action is concerned with.</p>
- *
- * @singleton
- */
-Ext.Dispatcher = new Ext.util.Dispatcher();
-
-/**
- * Shorthand for {@link Ext.Dispatcher#dispatch}. Dispatches a request to a controller action
- *
- * @member Ext
- * @method dispatch
- */
-Ext.dispatch = function() {
- return Ext.Dispatcher.dispatch.apply(Ext.Dispatcher, arguments);
-};
-
-/**
- * Shorthand for {@link Ext.Dispatcher#redirect}. Dispatches a request to a controller action, adding to the History
- * stack and updating the page url as necessary.
- *
- * @member Ext
- * @method redirect
- */
-Ext.redirect = function() {
- return Ext.Dispatcher.redirect.apply(Ext.Dispatcher, arguments);
-};
-
-Ext.createRedirect = Ext.Dispatcher.createRedirect;
-/**
- * @author Ed Spencer
- * @class Ext.util.Router
- * @extends Ext.util.Observable
- * @ignore
- */
-Ext.util.Router = Ext.extend(Ext.util.Observable, {
-
- constructor: function(config) {
- config = config || {};
-
- Ext.apply(this, config, {
- defaults: {
- action: 'index'
- }
- });
-
- this.routes = [];
-
- Ext.util.Router.superclass.constructor.call(this, config);
- },
-
- /**
- * Connects a url-based route to a controller/action pair plus additional params
- * @param {String} url The url to recognize
- */
- connect: function(url, params) {
- params = Ext.apply({url: url}, params || {}, this.defaults);
- var route = new Ext.util.Route(params);
-
- this.routes.push(route);
-
- return route;
- },
-
- /**
- * Recognizes a url string connected to the Router, return the controller/action pair plus any additional
- * config associated with it
- * @param {String} url The url to recognize
- * @return {Object/undefined} If the url was recognized, the controller and action to call, else undefined
- */
- recognize: function(url) {
- var routes = this.routes,
- length = routes.length,
- i, result;
-
- for (i = 0; i < length; i++) {
- result = routes[i].recognize(url);
-
- if (result != undefined) {
- return result;
- }
- }
- return undefined;
- },
-
- /**
- * Convenience method which just calls the supplied function with the Router instance. Example usage:
-<pre><code>
-Ext.Router.draw(function(map) {
- map.connect('activate/:token', {controller: 'users', action: 'activate'});
- map.connect('home', {controller: 'index', action: 'home'});
-});
-</code></pre>
- * @param {Function} fn The fn to call
- */
- draw: function(fn) {
- fn.call(this, this);
- }
-});
-
-Ext.Router = new Ext.util.Router();
-/**
- * @author Ed Spencer
- * @class Ext.util.Route
- * @extends Object
- * @ignore
- * <p>Represents a mapping between a url and a controller/action pair. May also contain additional params</p>
- */
-Ext.util.Route = Ext.extend(Object, {
- /**
- * @cfg {String} url The url string to match. Required.
- */
-
- constructor: function(config) {
- Ext.apply(this, config, {
- conditions: {}
- });
-
- /*
- * The regular expression we use to match a segment of a route mapping
- * this will recognise segments starting with a colon,
- * e.g. on 'namespace/:controller/:action', :controller and :action will be recognised
- */
- this.paramMatchingRegex = new RegExp(/:([0-9A-Za-z\_]*)/g);
-
- /*
- * Converts a route string into an array of symbols starting with a colon. e.g.
- * ":controller/:action/:id" => [':controller', ':action', ':id']
- */
- this.paramsInMatchString = this.url.match(this.paramMatchingRegex) || [];
-
- this.matcherRegex = this.createMatcherRegex(this.url);
- },
-
- /**
- * Attempts to recognize a given url string and return controller/action pair for it
- * @param {String} url The url to recognize
- * @return {Object} The matched data, or false if no match
- */
- recognize: function(url) {
- if (this.recognizes(url)) {
- var matches = this.matchesFor(url);
-
- return Ext.applyIf(matches, {
- controller: this.controller,
- action : this.action,
- historyUrl: url
- });
- }
- },
-
- /**
- * Returns true if this Route matches the given url string
- * @param {String} url The url to test
- * @return {Boolean} True if this Route recognizes the url
- */
- recognizes: function(url) {
- return this.matcherRegex.test(url);
- },
-
- /**
- * @private
- * Returns a hash of matching url segments for the given url.
- * @param {String} url The url to extract matches for
- * @return {Object} matching url segments
- */
- matchesFor: function(url) {
- var params = {},
- keys = this.paramsInMatchString,
- values = url.match(this.matcherRegex),
- length = keys.length,
- i;
-
- //first value is the entire match so reject
- values.shift();
-
- for (i = 0; i < length; i++) {
- params[keys[i].replace(":", "")] = values[i];
- }
-
- return params;
- },
-
- /**
- * Constructs a url for the given config object by replacing wildcard placeholders in the Route's url
- * @param {Object} config The config object
- * @return {String} The constructed url
- */
- urlFor: function(config) {
-
- },
-
- /**
- * @private
- * Takes the configured url string including wildcards and returns a regex that can be used to match
- * against a url
- * @param {String} url The url string
- * @return {RegExp} The matcher regex
- */
- createMatcherRegex: function(url) {
- /**
- * Converts a route string into an array of symbols starting with a colon. e.g.
- * ":controller/:action/:id" => [':controller', ':action', ':id']
- */
- var paramsInMatchString = this.paramsInMatchString,
- length = paramsInMatchString.length,
- i, cond, matcher;
-
- for (i = 0; i < length; i++) {
- cond = this.conditions[paramsInMatchString[i]];
- matcher = Ext.util.Format.format("({0})", cond || "[%a-zA-Z0-9\\_\\s,]+");
-
- url = url.replace(new RegExp(paramsInMatchString[i]), matcher);
- }
-
- //we want to match the whole string, so include the anchors
- return new RegExp("^" + url + "$");
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.Interaction
- * @extends Ext.util.Observable
- *
- * <p>Interactions are very simple objects that represent an action performed by specific {@link Ext.Controller}
- * action. The must consist of the {@link #controller} and {@link #action} to be called, but can contain any other
- * data too. See {@link Ext.Dispatcher} for more details on how Interactions fit into the application ecosystem.</p>
- *
- * <p>Interactions are an internal representation that most developers will not have much direct use for. They
- * help provide a normalized API for controller actions - each action should simply be set up to receive an Interaction
- * object. Because Interaction objects are always created when dispatching to a controller action, it is possible to
- * store the Interaction objects that were created in a session to perform simple analytics on how the application
- * is used. This is not built into the framework at the moment, but is left open for custom development if needed.</p>
- *
- * @constructor
- * @param {Object} config Options object containing at least a controller/action pair
- */
-Ext.Interaction = Ext.extend(Ext.util.Observable, {
- /**
- * @cfg {String} controller The controller to dispatch to
- */
- controller: '',
-
- /**
- * @cfg {String} action The controller action to invoke
- */
- action: '',
-
- /**
- * @cfg {Array} args Any arguments to pass to the action
- */
-
- /**
- * @cfg {Object} scope Optional scope to execute the controller action in
- */
-
- /**
- * True if this Interaction has already been dispatched
- * @property dispatched
- * @type Boolean
- */
- dispatched: false,
-
- constructor: function(config) {
- Ext.Interaction.superclass.constructor.apply(this, arguments);
-
- config = config || {};
-
- Ext.applyIf(config, {
- scope: this
- });
-
- Ext.apply(this, config);
-
- if (typeof this.controller == 'string') {
- this.controller = Ext.ControllerManager.get(this.controller);
- }
- }
-});
-/**
- * @author Ed Spencer
- * @class Ext.Application
- * @extends Ext.util.Observable
- *
- * <p>Represents a Sencha Application. Most Applications consist of at least the application's name and a launch
- * function:</p>
- *
-<pre><code>
-new Ext.Application({
- name: 'MyApp',
-
- launch: function() {
- this.viewport = new Ext.Panel({
- fullscreen: true,
-
- id : 'mainPanel',
- layout: 'card',
- items : [
- {
- html: 'Welcome to My App!'
- }
- ]
- });
- }
-});
-</code></pre>
- *
- * <p>Instantiating a new application automatically creates a global variable using the configured {@link #name}
- * property and sets up namespaces for views, stores, models and controllers within the app:</p>
- *
-<pre><code>
-//this code is run internally automatically when creating the app
-{@link Ext.ns}('MyApp', 'MyApp.views', 'MyApp.stores', 'MyApp.models', 'MyApp.controllers');
-</code></pre>
- *
- * <p>The launch function usually creates the Application's Viewport and runs any actions the Application needs to
- * perform when it boots up. The launch function is only expected to be run once.</p>
- *
- * <p><u>Routes and history support</u></p>
- *
- * <p>Sencha Applications provide in-app deep linking and history support, allowing your users both to use the back
- * button inside your application and to refresh the page and come back to the same screen even after navigating.
- * In-app history support relies on the Routing engine, which maps urls to controller/action pairs. Here's an example
- * route definition:</p>
- *
-<pre><code>
-//Note the # in the url examples below
-Ext.Router.draw(function(map) {
- //maps the url http://mydomain.com/#dashboard to the home controller's index action
- map.connect('dashboard', {controller: 'home', action: 'index'});
-
- //fallback route - would match routes like http://mydomain.com/#users/list to the 'users' controller's
- //'list' action
- map.connect(':controller/:action');
-});
-</code></pre>
- *
- * <p>If you generated your Sencha app using the Sencha Command application generator script, you'll see this file is
- * already present in your application's app/routes.js file. History-driven apps can specify the {@link #defaultUrl}
- * configuration option, which will dispatch to that url if no url is currently set:</p>
- *
-<pre><code>
-new Ext.Application({
- name: 'MyApp',
- defaultUrl: 'dashboard'
-});
-</code></pre>
- *
- * <p><u>Application profiles</u></p>
- *
- * <p>Applications support multiple app profiles and reconfigure itself accordingly. Here we set up an Application
- * with 3 profiles - one if the device is a phone, one for tablets in landscape orientation and one for tablets in
- * portrait orientation:</p>
- *
-<pre><code>
-new Ext.Application({
- name: 'MyApp',
-
- profiles: {
- phone: function() {
- return Ext.is.Phone;
- },
- tabletPortrait: function() {
- return Ext.is.Tablet && Ext.orientation == 'portrait';
- },
- tabletLandscape: function() {
- return Ext.is.Tablet && Ext.orientation == 'landscape';
- }
- }
-});
-</code></pre>
- *
- * <p>When the Application checks its list of profiles, the first function that returns true becomes the current profile.
- * The Application will normally automatically detect when a profile change has occurred (e.g. if a tablet is rotated
- * from portrait to landscape mode) and fire the {@link #profilechange} event. It will also by default inform all
- * {@link Ext.Component Components} on the page that the current profile has changed by calling their
- * {@link Ext.Component#setProfile setProfile} functions. The setProfile function is left as an empty function for you
- * to implement if your component needs to react to different device/application profiles.</p>
- *
- * <p>The current profile can be found using {@link #getProfile}. If the Application does not correctly detect device
- * profile changes, calling the {@link #determineProfile} function will force it to re-check.</p>
- */
-Ext.Application = Ext.extend(Ext.util.Observable, {
- /**
- * @cfg {String} name The name of the Application. This should be the same as the single global variable that the
- * application uses, and should not contain spaces
- */
-
- /**
- * @cfg {Object} scope The scope to execute the {@link #launch} function in. Defaults to the Application
- * instance.
- */
- scope: undefined,
-
- /**
- * @cfg {Boolean} useHistory True to automatically set up Ext.History support (defaults to true)
- */
- useHistory: true,
-
- /**
- * @cfg {String} defaultUrl When the app is first loaded, this url will be redirected to. Defaults to undefined
- */
-
- /**
- * @cfg {Boolean} autoUpdateComponentProfiles If true, automatically calls {@link Ext.Component#setProfile} on
- * all components whenever a application/device profile change is detected (defaults to true)
- */
- autoUpdateComponentProfiles: true,
-
- /**
- * @cfg {Boolean} setProfilesOnLaunch If true, determines the current application profile on launch and calls
- * {@link #updateComponentProfiles}. Defaults to true
- */
- setProfilesOnLaunch: true,
-
- /**
- * @cfg {Object} profiles A set of named profile specifications that this application supports. See the intro
- * docs for an example
- */
-
- constructor: function(config) {
- this.addEvents(
- /**
- * @event launch
- * Fires when the application is launched
- * @param {Ext.Application} app The Application instance
- */
- 'launch',
-
- /**
- * @event beforeprofilechange
- * Fires when a change in Application profile has been detected, but before any action is taken to
- * update the application's components about the change. Return false from any listener to cancel the
- * automatic updating of application components (see {@link #autoUpdateComponentProfiles})
- * @param {String} profile The name of the new profile
- * @param {String} oldProfile The name of the old profile (may be undefined)
- */
- 'beforeprofilechange',
-
- /**
- * @event profilechange
- * Fires when a change in Applicatino profile has been detected and the application's components have
- * already been informed. Listeners can perform additional processing here if required
- * @param {String} profile The name of the new profile
- * @param {String} oldProfile The name of the old profile (may be undefined)
- */
- 'profilechange'
- );
-
- Ext.Application.superclass.constructor.call(this, config);
-
- this.bindReady();
-
- var name = this.name;
-
- if (name) {
- window[name] = this;
-
- Ext.ns(
- name,
- name + ".views",
- name + ".stores",
- name + ".models",
- name + ".controllers"
- );
- }
-
- if (Ext.addMetaTags) {
- Ext.addMetaTags(config);
- }
- },
-
- /**
- * @private
- * We bind this outside the constructor so that we can cancel it in the test environment
- */
- bindReady : function() {
- Ext.onReady(this.onReady, this);
- },
-
- /**
- * Called automatically when the page has completely loaded. This is an empty function that should be
- * overridden by each application that needs to take action on page load
- * @property launch
- * @type Function
- * @param {String} profile The detected {@link #profiles application profile}
- * @return {Boolean} By default, the Application will dispatch to the configured startup controller and
- * action immediately after running the launch function. Return false to prevent this behavior.
- */
- launch: Ext.emptyFn,
-
- /**
- * @cfg {Boolean/String} useLoadMask True to automatically remove an application loading mask when the
- * DOM is ready. If set to true, this expects a div called "loading-mask" to be present in the body.
- * Pass the id of some other DOM node if using a custom loading mask element. Defaults to false.
- */
- useLoadMask: false,
-
- /**
- * @cfg {Number} loadMaskFadeDuration The number of milliseconds the load mask takes to fade out. Defaults to 1000
- */
- loadMaskFadeDuration: 1000,
-
- /**
- * @cfg {Number} loadMaskRemoveDuration The number of milliseconds until the load mask is removed after starting the
- * {@link #loadMaskFadeDuration fadeout}. Defaults to 1050.
- */
- loadMaskRemoveDuration: 1050,
-
- /**
- * @cfg {Boolean} autoInitViewport Will automatically set up the application to work in full screen mode by calling
- * {@link Ext.Viewport#init} if true (defaults to true)
- */
- autoInitViewport: true,
-
- /**
- * Dispatches to a given controller/action combo with optional arguments.
- * @param {Object} options Object containing strings referencing the controller and action to dispatch
- * to, plus optional args array
- * @return {Boolean} True if the controller and action were found and dispatched to, false otherwise
- */
- dispatch: function(options) {
- return Ext.dispatch(options);
- },
-
- /**
- * @private
- * Initializes the loading mask, called automatically by onReady if {@link #useLoadMask} is configured
- */
- initLoadMask: function() {
- var useLoadMask = this.useLoadMask,
- defaultId = 'loading-mask',
- loadMaskId = typeof useLoadMask == 'string' ? useLoadMask : defaultId;
-
- if (useLoadMask) {
- if (loadMaskId == defaultId) {
- Ext.getBody().createChild({id: defaultId});
- }
-
- var loadingMask = Ext.get('loading-mask'),
- fadeDuration = this.loadMaskFadeDuration,
- hideDuration = this.loadMaskRemoveDuration;
-
- Ext.defer(function() {
- loadingMask.addCls('fadeout');
-
- Ext.defer(function() {
- loadingMask.remove();
- }, hideDuration);
- }, fadeDuration);
- }
- },
-
- /**
- * @private
- */
- onBeforeLaunch: function() {
- var History = Ext.History,
- useHistory = History && this.useHistory,
- profile = this.determineProfile(true);
-
- if (useHistory) {
- this.historyForm = Ext.getBody().createChild({
- id : 'history-form',
- cls : 'x-hide-display',
- style : 'display: none;',
- tag : 'form',
- action: '#',
- children: [
- {
- tag: 'div',
- children: [
- {
- tag : 'input',
- id : History.fieldId,
- type: 'hidden'
- },
- {
- tag: 'iframe',
- id : History.iframeId
- }
- ]
- }
- ]
- });
-
- History.init();
- History.on('change', this.onHistoryChange, this);
-
- var token = History.getToken();
-
- if (this.launch.call(this.scope || this, profile) !== false) {
- Ext.redirect(token || this.defaultUrl || {controller: 'application', action: 'index'});
- }
- } else {
- this.launch.call(this.scope || this, profile);
- }
-
- this.launched = true;
-
- this.fireEvent('launch', this);
-
- if (this.setProfilesOnLaunch) {
- this.updateComponentProfiles(profile);
- }
- },
-
- /**
- * @private
- * Called when the DOM is ready. Calls the application-specific launch function and dispatches to the
- * first controller/action combo
- */
- onReady: function() {
- if (this.useLoadMask) {
- this.initLoadMask();
- }
-
- Ext.EventManager.onOrientationChange(this.determineProfile, this);
-
- if (this.autoInitViewport) {
- Ext.Viewport.init(this.onBeforeLaunch, this);
- } else {
- this.onBeforeLaunch();
- }
-
- return this;
- },
-
- /**
- * Calls each configured {@link #profile} function, marking the first one that returns true as the current
- * application profile. Fires the 'beforeprofilechange' and 'profilechange' events if the profile has changed
- * @param {Boolean} silent If true, the events profilechange event is not fired
- */
- determineProfile: function(silent) {
- var currentProfile = this.currentProfile,
- profiles = this.profiles,
- name;
-
- for (name in profiles) {
- if (profiles[name]() === true) {
- if (name != currentProfile && this.fireEvent('beforeprofilechange', name, currentProfile) !== false) {
- if (this.autoUpdateComponentProfiles) {
- this.updateComponentProfiles(name);
- }
-
- if (silent !== true) {
- this.fireEvent('profilechange', name, currentProfile);
- }
- }
-
- this.currentProfile = name;
- break;
- }
- }
-
- return this.currentProfile;
- },
-
- /**
- * @private
- * Sets the profile on every component on the page. Will probably refactor this to something less hacky.
- * @param {String} profile The new profile name
- */
- updateComponentProfiles: function(profile) {
- Ext.ComponentMgr.each(function(key, component){
- if (component.setProfile) {
- component.setProfile(profile);
- }
- });
- },
-
- /**
- * Gets the name of the currently-detected application profile
- * @return {String} The profile name
- */
- getProfile: function() {
- return this.currentProfile;
- },
-
- /**
- * @private
- */
- onHistoryChange: function(token) {
- return Ext.redirect(token);
- }
-});
-/**
- * @class Ext.ApplicationManager
- * @extends Ext.AbstractManager
- * @singleton
- * @ignore
- */
-Ext.ApplicationManager = new Ext.AbstractManager({
- register: function(name, options) {
- if (Ext.isObject(name)) {
- options = name;
- } else {
- options.name = name;
- }
-
- var application = new Ext.Application(options);
-
- this.all.add(application);
-
- this.currentApplication = application;
-
- return application;
- }
-});
-
-/**
- * Shorthand for {@link Ext.ApplicationManager#register}
- * Creates a new Application class from the specified config object. See {@link Ext.Application} for full examples.
- *
- * @param {Object} config A configuration object for the Model you wish to create.
- * @return {Ext.Application} The newly created Application
- * @member Ext
- * @method regApplication
- */
-Ext.regApplication = function() {
- return Ext.ApplicationManager.register.apply(Ext.ApplicationManager, arguments);
-};
-
-/**
- * @class Ext.Element
- * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
- * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
- * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
- * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
- * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
- * Usage:<br>
-<pre><code>
-// by id
-var el = Ext.get("my-div");
-
-// by DOM element reference
-var el = Ext.get(myDivElement);
-</code></pre>
- * <b>Animations</b><br />
- * <p>When an element is manipulated, by default there is no animation.</p>
- * <pre><code>
-var el = Ext.get("my-div");
-
-// no animation
-el.setWidth(100);
- * </code></pre>
- * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
- * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
- * <pre><code>
-// default animation
-el.setWidth(100, true);
- * </code></pre>
- *
- * <p>To configure the effects, an object literal with animation options to use as the Element animation
- * configuration object can also be specified. Note that the supported Element animation configuration
- * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects. The supported
- * Element animation configuration options are:</p>
-<pre>
-Option Default Description
---------- -------- ---------------------------------------------
-{@link Ext.Fx#duration duration} .35 The duration of the animation in seconds
-{@link Ext.Fx#easing easing} easeOut The easing method
-{@link Ext.Fx#callback callback} none A function to execute when the anim completes
-{@link Ext.Fx#scope scope} this The scope (this) of the callback function
-</pre>
- *
- * <pre><code>
-// Element animation options object
-var opt = {
- {@link Ext.Fx#duration duration}: 1,
- {@link Ext.Fx#easing easing}: 'elasticIn',
- {@link Ext.Fx#callback callback}: this.foo,
- {@link Ext.Fx#scope scope}: this
-};
-// animation with some options set
-el.setWidth(100, opt);
- * </code></pre>
- * <p>The Element animation object being used for the animation will be set on the options
- * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
- * <pre><code>
-// using the "anim" property to get the Anim object
-if(opt.anim.isAnimated()){
- opt.anim.stop();
-}
- * </code></pre>
- * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
- * <p><b> Composite (Collections of) Elements</b></p>
- * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
- * @constructor Create a new Element directly.
- * @param {String/HTMLElement} element
- * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
- */
-
-(function() {
-var El = Ext.Element = Ext.extend(Object, {
- /**
- * The default unit to append to CSS values where a unit isn't provided (defaults to px).
- * @type String
- */
- defaultUnit : "px",
-
- constructor : function(element, forceNew) {
- var dom = typeof element == 'string'
- ? document.getElementById(element)
- : element,
- id;
-
- if (!dom) {
- return null;
- }
-
- id = dom.id;
- if (!forceNew && id && Ext.cache[id]) {
- return Ext.cache[id].el;
- }
-
- /**
- * The DOM element
- * @type HTMLElement
- */
- this.dom = dom;
-
- /**
- * The DOM element ID
- * @type String
- */
- this.id = id || Ext.id(dom);
- return this;
- },
-
- /**
- * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
- * @param {Object} o The object with the attributes
- * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
- * @return {Ext.Element} this
- */
- set : function(o, useSet) {
- var el = this.dom,
- attr,
- value;
-
- for (attr in o) {
- if (o.hasOwnProperty(attr)) {
- value = o[attr];
- if (attr == 'style') {
- this.applyStyles(value);
- }
- else if (attr == 'cls') {
- el.className = value;
- }
- else if (useSet !== false) {
- el.setAttribute(attr, value);
- }
- else {
- el[attr] = value;
- }
- }
- }
- return this;
- },
-
- /**
- * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
- * @param {String} selector The simple selector to test
- * @return {Boolean} True if this element matches the selector, else false
- */
- is : function(simpleSelector) {
- return Ext.DomQuery.is(this.dom, simpleSelector);
- },
-
- /**
- * Returns the value of the "value" attribute
- * @param {Boolean} asNumber true to parse the value as a number
- * @return {String/Number}
- */
- getValue : function(asNumber){
- var val = this.dom.value;
- return asNumber ? parseInt(val, 10) : val;
- },
-
- /**
- * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
- * @param {String} eventName The name of event to handle.
- * @param {Function} fn The handler function the event invokes. This function is passed
- * the following parameters:<ul>
- * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
- * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
- * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
- * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
- * </ul>
- * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
- * <b>If omitted, defaults to this Element.</b>.
- * @param {Object} options (optional) An object containing handler configuration properties.
- * This may contain any of the following properties:<ul>
- * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
- * <b>If omitted, defaults to this Element.</b></div></li>
- * <li><b>delegate</b> String: <div class="sub-desc">A simple selector to filter the target or look for a descendant of the target. See below for additional details.</div></li>
- * <li><b>stopEvent</b> Boolean: <div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
- * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
- * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
- * <li><b>normalized</b> Boolean: <div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
- * <li><b>target</b> Ext.Element: <div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
- * <li><b>delay</b> Number: <div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
- * <li><b>single</b> Boolean: <div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
- * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
- * by the specified number of milliseconds. If the event fires again within that time, the original
- * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
- * </ul><br>
- * <p>
- * <b>Combining Options</b><br>
- * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
- * addListener. The two are equivalent. Using the options argument, it is possible to combine different
- * types of listeners:<br>
- * <br>
- * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
- * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
- * Code:<pre><code>
-el.on('tap', this.onTap, this, {
- single: true,
- delay: 100,
- stopEvent : true
-});</code></pre></p>
- * <p>
- * <b>Attaching multiple handlers in 1 call</b><br>
- * The method also allows for a single argument to be passed which is a config object containing properties
- * which specify multiple handlers.</p>
- * <p>
- * Code:<pre><code>
-el.on({
- 'tap' : {
- fn: this.onTap,
- scope: this
- },
- 'doubletap' : {
- fn: this.onDoubleTap,
- scope: this
- },
- 'swipe' : {
- fn: this.onSwipe,
- scope: this
- }
-});</code></pre>
- * <p>
- * Or a shorthand syntax:<br>
- * Code:<pre><code></p>
-el.on({
- 'tap' : this.onTap,
- 'doubletap' : this.onDoubleTap,
- 'swipe' : this.onSwipe,
- scope: this
-});
- * </code></pre></p>
- * <p><b>delegate</b></p>
- * <p>This is a configuration option that you can pass along when registering a handler for
- * an event to assist with event delegation. Event delegation is a technique that is used to
- * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
- * for a container element as opposed to each element within a container. By setting this
- * configuration option to a simple selector, the target element will be filtered to look for
- * a descendant of the target.
- * For example:<pre><code>
-// using this markup:
-<div id='elId'>
- <p id='p1'>paragraph one</p>
- <p id='p2' class='clickable'>paragraph two</p>
- <p id='p3'>paragraph three</p>
-</div>
-// utilize event delegation to registering just one handler on the container element:
-el = Ext.get('elId');
-el.on(
- 'tap',
- function(e,t) {
- // handle click
- console.info(t.id); // 'p2'
- },
- this,
- {
- // filter the target element to be a descendant with the class 'tappable'
- delegate: '.tappable'
- }
-);
- * </code></pre></p>
- * @return {Ext.Element} this
- */
- addListener : function(eventName, fn, scope, options){
- Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
- return this;
- },
-
- /**
- * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
- * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
- * listener, the same scope must be specified here.
- * Example:
- * <pre><code>
-el.removeListener('tap', this.handlerFn);
-// or
-el.un('tap', this.handlerFn);
-</code></pre>
- * @param {String} eventName The name of the event from which to remove the handler.
- * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
- * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
- * then this must refer to the same object.
- * @return {Ext.Element} this
- */
- removeListener : function(eventName, fn, scope) {
- Ext.EventManager.un(this.dom, eventName, fn, scope);
- return this;
- },
-
- /**
- * Removes all previous added listeners from this element
- * @return {Ext.Element} this
- */
- removeAllListeners : function(){
- Ext.EventManager.removeAll(this.dom);
- return this;
- },
-
- /**
- * Recursively removes all previous added listeners from this element and its children
- * @return {Ext.Element} this
- */
- purgeAllListeners : function() {
- Ext.EventManager.purgeElement(this, true);
- return this;
- },
-
- /**
- * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode}</p>
- */
- remove : function() {
- var me = this,
- dom = me.dom;
-
- if (dom) {
- delete me.dom;
- Ext.removeNode(dom);
- }
- },
-
- isAncestor : function(c) {
- var p = this.dom;
- c = Ext.getDom(c);
- if (p && c) {
- return p.contains(c);
- }
- return false;
- },
-
- /**
- * Determines if this element is a descendent of the passed in Element.
- * @param {Mixed} element An Ext.Element, HTMLElement or string linking to an id of an Element.
- * @returns {Boolean}
- */
- isDescendent : function(p) {
- return Ext.fly(p, '_internal').isAncestor(this);
- },
-
- /**
- * Returns true if this element is an ancestor of the passed element
- * @param {HTMLElement/String} el The element to check
- * @return {Boolean} True if this element is an ancestor of el, else false
- */
- contains : function(el) {
- return !el ? false : this.isAncestor(el);
- },
-
- /**
- * Returns the value of an attribute from the element's underlying DOM node.
- * @param {String} name The attribute name
- * @param {String} namespace (optional) The namespace in which to look for the attribute
- * @return {String} The attribute value
- */
- getAttribute : function(name, ns) {
- var d = this.dom;
- return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
- },
-
- /**
- * Set the innerHTML of this element
- * @param {String} html The new HTML
- * @return {Ext.Element} this
- */
- setHTML : function(html) {
- if(this.dom) {
- this.dom.innerHTML = html;
- }
- return this;
- },
-
- /**
- * Returns the innerHTML of an Element or an empty string if the element's
- * dom no longer exists.
- */
- getHTML : function() {
- return this.dom ? this.dom.innerHTML : '';
- },
-
- /**
- * 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
- */
- hide : function() {
- this.setVisible(false);
- return this;
- },
-
- /**
- * 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
- */
- show : function() {
- this.setVisible(true);
- return this;
- },
-
- /**
- * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
- * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
- * @param {Boolean} visible Whether the element is visible
- * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
- * @return {Ext.Element} this
- */
- setVisible : function(visible, animate) {
- var me = this,
- dom = me.dom,
- mode = this.getVisibilityMode();
-
- switch (mode) {
- case El.VISIBILITY:
- this.removeCls(['x-hidden-display', 'x-hidden-offsets']);
- this[visible ? 'removeCls' : 'addCls']('x-hidden-visibility');
- break;
-
- case El.DISPLAY:
- this.removeCls(['x-hidden-visibility', 'x-hidden-offsets']);
- this[visible ? 'removeCls' : 'addCls']('x-hidden-display');
- break;
-
- case El.OFFSETS:
- this.removeCls(['x-hidden-visibility', 'x-hidden-display']);
- this[visible ? 'removeCls' : 'addCls']('x-hidden-offsets');
- break;
- }
-
- return me;
- },
-
- getVisibilityMode: function() {
- var dom = this.dom,
- mode = El.data(dom, 'visibilityMode');
-
- if (mode === undefined) {
- El.data(dom, 'visibilityMode', mode = El.DISPLAY);
- }
-
- return mode;
- },
-
- setDisplayMode : function(mode) {
- El.data(this.dom, 'visibilityMode', mode);
- return this;
- }
-});
-
-var Elp = El.prototype;
-
-/**
- * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
- * @static
- * @type Number
- */
-El.VISIBILITY = 1;
-/**
- * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
- * @static
- * @type Number
- */
-El.DISPLAY = 2;
-/**
- * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets to hide element
- * @static
- * @type Number
- */
-El.OFFSETS = 3;
-
-
-El.addMethods = function(o){
- Ext.apply(Elp, o);
-};
-
-
-Elp.on = Elp.addListener;
-Elp.un = Elp.removeListener;
-
-// Alias for people used to Ext JS and Ext Core
-Elp.update = Elp.setHTML;
-
-/**
- * Retrieves Ext.Element objects.
- * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
- * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
- * its ID, use {@link Ext.ComponentMgr#get}.</p>
- * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
- * object was recreated with the same id via AJAX or DOM.</p>
- * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
- * @return {Element} The Element object (or null if no matching element was found)
- * @static
- * @member Ext.Element
- * @method get
- */
-El.get = function(el){
- var extEl,
- dom,
- id;
-
- if(!el){
- return null;
- }
-
- if (typeof el == "string") { // element id
- if (!(dom = document.getElementById(el))) {
- return null;
- }
- if (Ext.cache[el] && Ext.cache[el].el) {
- extEl = Ext.cache[el].el;
- extEl.dom = dom;
- } else {
- extEl = El.addToCache(new El(dom));
- }
- return extEl;
- } else if (el.tagName) { // dom element
- if(!(id = el.id)){
- id = Ext.id(el);
- }
- if (Ext.cache[id] && Ext.cache[id].el) {
- extEl = Ext.cache[id].el;
- extEl.dom = el;
- } else {
- extEl = El.addToCache(new El(el));
- }
- return extEl;
- } else if (el instanceof El) {
- if(el != El.docEl){
- // refresh dom element in case no longer valid,
- // catch case where it hasn't been appended
- el.dom = document.getElementById(el.id) || el.dom;
- }
- return el;
- } else if(el.isComposite) {
- return el;
- } else if(Ext.isArray(el)) {
- return El.select(el);
- } else if(el == document) {
- // create a bogus element object representing the document object
- if(!El.docEl){
- var F = function(){};
- F.prototype = Elp;
- El.docEl = new F();
- El.docEl.dom = document;
- El.docEl.id = Ext.id(document);
- }
- return El.docEl;
- }
- return null;
-};
-
-// private
-El.addToCache = function(el, id){
- id = id || el.id;
- Ext.cache[id] = {
- el: el,
- data: {},
- events: {}
- };
- return el;
-};
-
-// private method for getting and setting element data
-El.data = function(el, key, value) {
- el = El.get(el);
- if (!el) {
- return null;
- }
- var c = Ext.cache[el.id].data;
- if (arguments.length == 2) {
- return c[key];
- }
- else {
- return (c[key] = value);
- }
-};
-
-// private
-// Garbage collection - uncache elements/purge listeners on orphaned elements
-// so we don't hold a reference and cause the browser to retain them
-El.garbageCollect = function() {
- if (!Ext.enableGarbageCollector) {
- clearInterval(El.collectorThreadId);
- }
- else {
- var id,
- dom,
- EC = Ext.cache;
-
- for (id in EC) {
- if (!EC.hasOwnProperty(id)) {
- continue;
- }
- if(EC[id].skipGarbageCollection){
- continue;
- }
- dom = EC[id].el.dom;
- if(!dom || !dom.parentNode || (!dom.offsetParent && !document.getElementById(id))){
- if(Ext.enableListenerCollection){
- Ext.EventManager.removeAll(dom);
- }
- delete EC[id];
- }
- }
- }
-};
-//El.collectorThreadId = setInterval(El.garbageCollect, 20000);
-
-// dom is optional
-El.Flyweight = function(dom) {
- this.dom = dom;
-};
-
-var F = function(){};
-F.prototype = Elp;
-
-El.Flyweight.prototype = new F;
-El.Flyweight.prototype.isFlyweight = true;
-
-El._flyweights = {};
-
-/**
- * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
- * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
- * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
- * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
- * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
- * @param {String/HTMLElement} el The dom node or id
- * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
- * (e.g. internally Ext uses "_global")
- * @return {Element} The shared Element object (or null if no matching element was found)
- * @member Ext.Element
- * @method fly
- */
-El.fly = function(el, named) {
- var ret = null;
- named = named || '_global';
-
- el = Ext.getDom(el);
- if (el) {
- (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
- ret = El._flyweights[named];
- }
-
- return ret;
-};
-
-/**
- * Retrieves Ext.Element objects.
- * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
- * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
- * its ID, use {@link Ext.ComponentMgr#get}.</p>
- * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
- * object was recreated with the same id via AJAX or DOM.</p>
- * Shorthand of {@link Ext.Element#get}
- * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
- * @return {Element} The Element object (or null if no matching element was found)
- * @member Ext
- * @method get
- */
-Ext.get = El.get;
-
-/**
- * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
- * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
- * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
- * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
- * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
- * @param {String/HTMLElement} el The dom node or id
- * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
- * (e.g. internally Ext uses "_global")
- * @return {Element} The shared Element object (or null if no matching element was found)
- * @member Ext
- * @method fly
- */
-Ext.fly = El.fly;
-
-/*Ext.EventManager.on(window, 'unload', function(){
- delete Ext.cache;
- delete El._flyweights;
-});*/
-
-})();
-
-Ext.applyIf(Ext.Element, {
- unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
- camelRe: /(-[a-z])/gi,
- opacityRe: /alpha\(opacity=(.*)\)/i,
- propertyCache: {},
- defaultUnit : "px",
- borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
- paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
- margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
-
- addUnits : function(size, units) {
- if (size === "" || size == "auto" || size === null || size === undefined) {
- size = size || '';
- }
- else if (!isNaN(size) || !this.unitRe.test(size)) {
- size = size + (units || this.defaultUnit || 'px');
- }
- return size;
- },
-
- /**
- * 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} box The encoded margins
- * @return {Object} An object with margin sizes for top, right, bottom and left
- */
- parseBox : function(box) {
- if (typeof box != 'string') {
- box = box.toString();
- }
- var parts = box.split(' '),
- ln = parts.length;
-
- if (ln == 1) {
- parts[1] = parts[2] = parts[3] = parts[0];
- }
- else if (ln == 2) {
- parts[2] = parts[0];
- parts[3] = parts[1];
- }
- else if (ln == 3) {
- parts[3] = parts[1];
- }
-
- return {
- top :parseFloat(parts[0]) || 0,
- right :parseFloat(parts[1]) || 0,
- bottom:parseFloat(parts[2]) || 0,
- left :parseFloat(parts[3]) || 0
- };
- },
-
- /**
- * 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} box The encoded margins
- * @param {String} units The type of units to add
- * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
- */
- unitizeBox : function(box, units) {
- var A = this.addUnits,
- B = this.parseBox(box);
-
- return A(B.top, units) + ' ' +
- A(B.right, units) + ' ' +
- A(B.bottom, units) + ' ' +
- A(B.left, units);
-
- },
-
- // private
- camelReplaceFn : function(m, a) {
- return a.charAt(1).toUpperCase();
- },
-
- /**
- * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
- * For example:
- * <ul>
- * <li>border-width -> borderWidth</li>
- * <li>padding-top -> paddingTop</li>
- * </ul>
- */
- normalize : function(prop) {
- return this.propertyCache[prop] || (this.propertyCache[prop] = prop == 'float' ? 'cssFloat' : prop.replace(this.camelRe, this.camelReplaceFn));
- },
-
- /**
- * Retrieves the document height
- * @returns {Number} documentHeight
- */
- getDocumentHeight: function() {
- return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
- },
-
- /**
- * Retrieves the document width
- * @returns {Number} documentWidth
- */
- getDocumentWidth: function() {
- return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
- },
-
- /**
- * Retrieves the viewport height of the window.
- * @returns {Number} viewportHeight
- */
- getViewportHeight: function(){
- return window.innerHeight;
- },
-
- /**
- * Retrieves the viewport width of the window.
- * @returns {Number} viewportWidth
- */
- getViewportWidth : function() {
- return window.innerWidth;
- },
-
- /**
- * Retrieves the viewport size of the window.
- * @returns {Object} object containing width and height properties
- */
- getViewSize : function() {
- return {
- width: window.innerWidth,
- height: window.innerHeight
- };
- },
-
- /**
- * Retrieves the current orientation of the window. This is calculated by
- * determing if the height is greater than the width.
- * @returns {String} Orientation of window: 'portrait' or 'landscape'
- */
- getOrientation : function() {
- if (Ext.supports.OrientationChange) {
- return (window.orientation == 0) ? 'portrait' : 'landscape';
- }
-
- return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
- },
-
- /** Returns the top Element that is located at the passed coordinates
- * Function description
- * @param {Number} x The x coordinate
- * @param {Number} x The y coordinate
- * @return {String} The found Element
- */
- fromPoint: function(x, y) {
- return Ext.get(document.elementFromPoint(x, y));
- }
-});
-
-Ext.applyIf(Ext.Element, {
-
- /**
- * Returns the calculated CSS 2D transform offset values (translate x and y)
- * @static
- * @param {Ext.Element/Element} el the element
- * @return {Ext.util.Offset} instance of Ext.util.Offset, with x and y properties
- */
- getComputedTransformOffset: function(el) {
- if (el instanceof Ext.Element)
- el = el.dom;
-
- var transform = window.getComputedStyle(el).webkitTransform,
- cssMatrix = transform != 'none' ? new WebKitCSSMatrix(transform) : new WebKitCSSMatrix();
-
- if (typeof cssMatrix.m41 != 'undefined') {
- return new Ext.util.Offset(cssMatrix.m41, cssMatrix.m42);
- } else if (typeof cssMatrix.d != 'undefined') {
- return new Ext.util.Offset(cssMatrix.d, cssMatrix.e);
- }
-
- return new Ext.util.Offset(0, 0);
- },
-
- /**
- * Transform an element using CSS 3
- * @static
- * @param {Ext.Element/Element} el the element
- * @param {Object} transforms an object with all transformation to be applied. The keys are transformation method names,
- * the values are arrays of params or a single number if there's only one param e.g:
- *
- * {
- * translate: [0, 1, 2],
- * scale: 0.5,
- * skew: -25,
- * rotate: 7
- * }
- */
- cssTransform: function(el, transforms) {
- if (el instanceof Ext.Element)
- el = el.dom;
-
- var m = new WebKitCSSMatrix();
-
- Ext.iterate(transforms, function(n, v) {
- v = Ext.isArray(v) ? v : [v];
- m = m[n].apply(m, v);
- });
-
- // To enable hardware accelerated transforms on iOS (v3 only, fixed in v4?) we have to build the string manually
- // Other than that simply apply the matrix works perfectly on the rest of devices including Androids & Blackberry
- if (Ext.supports.CSS3DTransform) {
- el.style.webkitTransform = 'matrix3d(' +
- m.m11+', '+m.m12+', '+m.m13+', '+m.m14+', '+
- m.m21+', '+m.m22+', '+m.m23+', '+m.m24+', '+
- m.m31+', '+m.m32+', '+m.m33+', '+m.m34+', '+
- m.m41+', '+m.m42+', '+m.m43+', '+m.m44+
- ')';
- } else {
- el.style.webkitTransform = m;
- }
- },
-
- /**
- * Translate an element using CSS 3 in 2D. This is supposed to be faster than cssTransform when we only need to translate
- * an element without reserving its original matrix
- * @param {Ext.Element/Element} el the element
- * @param {Ext.util.Offset/Object} offset The new offset with format
- *
- * {
- * x: offsetX,
- * y: offsetY
- * }
- */
- cssTranslate: function(el, offset) {
- if (el instanceof Ext.Element)
- el = el.dom;
-
- if (Ext.supports.CSS3DTransform) {
- el.style.webkitTransform = 'translate3d('+offset.x+'px, '+offset.y+'px, 0px)';
- } else {
- el.style.webkitTransform = 'translate('+offset.x+'px, '+offset.y+'px)';
- }
- }
-
-});
-
-/**
- * @class Ext.Element
- */
-Ext.Element.addMethods({
- /**
- * 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(el) {
- return this.getXY(el)[1];
- },
-
- /**
- * 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(el) {
- return this.getXY(el)[0];
- },
-
- /**
- * 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() {
- // @FEATUREDETECT
- var point = window.webkitConvertPointFromNodeToPage(this.dom, new WebKitPoint(0, 0));
- return [point.x, point.y];
- },
-
- /**
- * 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 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)
- * @return {Ext.Element} this
- */
- setXY : function(pos) {
- var me = this;
-
- if(arguments.length > 1) {
- pos = [pos, arguments[1]];
- }
-
- // me.position();
- var pts = me.translatePoints(pos),
- style = me.dom.style;
-
- for (pos in pts) {
- if (!pts.hasOwnProperty(pos)) {
- continue;
- }
- if(!isNaN(pts[pos])) style[pos] = pts[pos] + "px";
- }
- return me;
- },
-
- /**
- * 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
- * @return {Ext.Element} this
- */
- setX : function(x){
- return this.setXY([x, this.getY()]);
- },
-
- /**
- * 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) {
- return this.setXY([this.getX(), y]);
- },
-
- /**
- * 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', Ext.Element.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', Ext.Element.addUnits(top));
- return this;
- },
-
- /**
- * Sets the element's top and left positions directly using CSS style (instead of {@link #setXY})
- * @param {String} top The top CSS property value
- * @param {String} left The left CSS property value
- */
- setTopLeft: function(top, left) {
- var addUnits = Ext.Element.addUnits;
-
- this.setStyle('top', addUnits(top));
- this.setStyle('left', addUnits(left));
-
- 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', Ext.Element.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', Ext.Element.addUnits(bottom));
- return this;
- },
-
- /**
- * 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 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) {
- return parseInt(this.getStyle('right'), 10) || 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 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) {
- return parseInt(this.getStyle('bottom'), 10) || 0;
- },
-
- /**
- * 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}
- * @return {Ext.Element} this
- */
- setBox : function(left, top, width, height) {
- var undefined;
- if (Ext.isObject(left)) {
- width = left.width;
- height = left.height;
- top = left.top;
- left = left.left;
- }
-
- if (left !== undefined) {
- this.setLeft(left);
- }
- if (top !== undefined) {
- this.setTop(top);
- }
- if (width !== undefined) {
- this.setWidth(width);
- }
- if (height !== undefined) {
- this.setHeight(height);
- }
-
- return this;
- },
-
- /**
- * 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<pre><code>
-{
- 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>
-}
-</code></pre>
- * 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,
- dom = me.dom,
- width = dom.offsetWidth,
- height = dom.offsetHeight,
- xy, box, l, r, t, b;
-
- if (!local) {
- xy = me.getXY();
- }
- else if (contentBox) {
- xy = [0,0];
- }
- else {
- xy = [parseInt(me.getStyle("left"), 10) || 0, parseInt(me.getStyle("top"), 10) || 0];
- }
-
- if (!contentBox) {
- box = {
- x: xy[0],
- y: xy[1],
- 0: xy[0],
- 1: xy[1],
- width: width,
- height: height
- };
- }
- else {
- l = me.getBorderWidth.call(me, "l") + me.getPadding.call(me, "l");
- r = me.getBorderWidth.call(me, "r") + me.getPadding.call(me, "r");
- t = me.getBorderWidth.call(me, "t") + me.getPadding.call(me, "t");
- b = me.getBorderWidth.call(me, "b") + me.getPadding.call(me, "b");
- box = {
- x: xy[0] + l,
- y: xy[1] + t,
- 0: xy[0] + l,
- 1: xy[1] + t,
- width: width - (l + r),
- height: height - (t + b)
- };
- }
-
- box.left = box.x;
- box.top = box.y;
- box.right = box.x + box.width;
- box.bottom = box.y + box.height;
-
- return box;
- },
-
- /**
- * 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} asRegion(optional) If true an Ext.util.Region will be returned
- * @return {Object} box An object in the format<pre><code>
-{
- 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>
-}
-</code></pre>
- * 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}
- */
- getPageBox : function(getRegion) {
- var me = this,
- el = me.dom,
- w = el.offsetWidth,
- h = el.offsetHeight,
- xy = me.getXY(),
- t = xy[1],
- r = xy[0] + w,
- b = xy[1] + h,
- l = xy[0];
-
- if (!el) {
- return new Ext.util.Region();
- }
-
- if (getRegion) {
- return new Ext.util.Region(t, r, b, l);
- }
- else {
- return {
- left: l,
- top: t,
- width: w,
- height: h,
- right: r,
- bottom: b
- };
- }
- },
-
- /**
- * 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)};
- }
-});
-
-(function() {
- /**
- * @class Ext.Element
- */
- Ext.Element.classReCache = {};
- var El = Ext.Element,
- view = document.defaultView;
-
- El.addMethods({
- marginRightRe: /marginRight/i,
- trimRe: /^\s+|\s+$/g,
- spacesRe: /\s+/,
-
- /**
- * 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
- */
- addCls: function(className) {
- var me = this,
- i,
- len,
- v,
- cls = [];
-
- if (!Ext.isArray(className)) {
- if (className && !this.hasCls(className)) {
- me.dom.className += " " + className;
- }
- }
- else {
- for (i = 0, len = className.length; i < len; i++) {
- v = className[i];
- if (v && !me.hasCls(v)) {
- cls.push(v);
- }
- }
- if (cls.length) {
- me.dom.className += " " + cls.join(" ");
- }
- }
- return me;
- },
-
- addClass : function() {
- throw new Error("Component: addClass has been deprecated. Please use addCls.");
- },
-
- /**
- * 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
- */
- removeCls: 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(this.trimRe, '').split(this.spacesRe);
- for (i = 0, len = className.length; i < len; i++) {
- cls = className[i];
- if (typeof cls == 'string') {
- cls = cls.replace(this.trimRe, '');
- idx = elClasses.indexOf(cls);
- if (idx != -1) {
- elClasses.splice(idx, 1);
- }
- }
- }
- me.dom.className = elClasses.join(" ");
- }
- return me;
- },
-
- removeClass : function() {
- throw new Error("Component: removeClass has been deprecated. Please use removeCls.");
- },
-
- /**
- * Puts a mask over this element to disable user interaction.
- * This method can only be applied to elements which accept child nodes.
- * @param {String} msg (optional) A message to display in the mask. This can be html.
- * @param {String} msgCls (optional) A css class to apply to the msg element
- * @param {Boolean} transparent (optional) False to show make the mask gray with opacity. (defaults to true)
- * @return {Element} The mask element
- */
- mask: function(msg, msgCls, transparent) {
- var me = this,
- dom = me.dom,
- el = Ext.Element.data(dom, 'mask'),
- mask,
- size,
- cls = '';
-
- me.addCls('x-masked');
- if (me.getStyle("position") == "static") {
- me.addCls('x-masked-relative');
- }
- if (el) {
- el.remove();
- }
- if (Ext.isString(msgCls) && !Ext.isEmpty(msgCls)) {
- cls = ' ' + msgCls;
- }
- else {
- if (msgCls) {
- cls = ' x-mask-gray';
- }
- }
-
- mask = me.createChild({
- cls: 'x-mask' + ((transparent !== false) ? '' : ' x-mask-gray'),
- html: msg ? ('<div class="' + (msgCls || 'x-mask-message') + '">' + msg + '</div>') : ''
- });
-
- size = me.getSize();
-
- Ext.Element.data(dom, 'mask', mask);
-
- if (dom === document.body) {
- size.height = window.innerHeight;
- if (me.orientationHandler) {
- Ext.EventManager.unOrientationChange(me.orientationHandler, me);
- }
-
- me.orientationHandler = function() {
- size = me.getSize();
- size.height = window.innerHeight;
- mask.setSize(size);
- };
-
- Ext.EventManager.onOrientationChange(me.orientationHandler, me);
- }
- mask.setSize(size);
- if (Ext.is.iPad) {
- Ext.repaint();
- }
- },
-
- /**
- * Removes a previously applied mask.
- */
- unmask: function() {
- var me = this,
- dom = me.dom,
- mask = Ext.Element.data(dom, 'mask');
-
- if (mask) {
- mask.remove();
- Ext.Element.data(dom, 'mask', undefined);
- }
- me.removeCls(['x-masked', 'x-masked-relative']);
-
- if (dom === document.body) {
- Ext.EventManager.unOrientationChange(me.orientationHandler, me);
- delete me.orientationHandler;
- }
- },
-
- /**
- * 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
- */
- radioCls: function(className) {
- var cn = this.dom.parentNode.childNodes,
- v;
- className = Ext.isArray(className) ? className: [className];
- for (var i = 0, len = cn.length; i < len; i++) {
- v = cn[i];
- if (v && v.nodeType == 1) {
- Ext.fly(v, '_internal').removeCls(className);
- }
- };
- return this.addCls(className);
- },
-
- radioClass : function() {
- throw new Error("Component: radioClass has been deprecated. Please use radioCls.");
- },
-
- /**
- * 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
- */
- toggleCls: function(className) {
- return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
- },
-
- toggleClass : function() {
- throw new Error("Component: toggleClass has been deprecated. Please use toggleCls.");
- },
-
- /**
- * 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
- */
- hasCls: function(className) {
- return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
- },
-
- hasClass : function() {
- throw new Error("Element: hasClass has been deprecated. Please use hasCls.");
- return this.hasCls.apply(this, arguments);
- },
-
- /**
- * 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
- */
- replaceCls: function(oldClassName, newClassName) {
- return this.removeCls(oldClassName).addCls(newClassName);
- },
-
- replaceClass : function() {
- throw new Error("Component: replaceClass has been deprecated. Please use replaceCls.");
- },
-
- isStyle: function(style, val) {
- return this.getStyle(style) == val;
- },
-
- /**
- * Normalizes currentStyle and computedStyle.
- * @param {String} property The style property whose value is returned.
- * @return {String} The current value of the style property for this element.
- */
- getStyle: function(prop) {
- var dom = this.dom,
- result,
- display,
- cs,
- platform = Ext.is,
- style = dom.style;
-
- prop = El.normalize(prop);
- cs = (view) ? view.getComputedStyle(dom, '') : dom.currentStyle;
- result = (cs) ? cs[prop] : null;
-
- // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
- if (result && !platform.correctRightMargin &&
- this.marginRightRe.test(prop) &&
- style.position != 'absolute' &&
- result != '0px') {
- display = style.display;
- style.display = 'inline-block';
- result = view.getComputedStyle(dom, null)[prop];
- style.display = display;
- }
-
- result || (result = style[prop]);
-
- // Webkit returns rgb values for transparent.
- if (!platform.correctTransparentColor && result == 'rgba(0, 0, 0, 0)') {
- result = 'transparent';
- }
-
- return result;
- },
-
- /**
- * 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 == 'string') {
- tmp = {};
- tmp[prop] = value;
- prop = tmp;
- }
-
- for (style in prop) {
- if (prop.hasOwnProperty(style)) {
- this.dom.style[El.normalize(style)] = prop[style];
- }
- }
-
- return this;
- },
-
- /**
- * 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.
- */
- applyStyles: function(styles) {
- if (styles) {
- var i,
- len,
- dom = this.dom;
-
- if (typeof styles == 'function') {
- styles = styles.call();
- }
- if (typeof styles == 'string') {
- styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
- for (i = 0, len = styles.length; i < len;) {
- dom.style[El.normalize(styles[i++])] = styles[i++];
- }
- }
- else if (typeof styles == 'object') {
- this.setStyle(styles);
- }
- }
- },
-
- /**
- * 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
- */
- getHeight: function(contentHeight) {
- var dom = this.dom,
- height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight;
- return height > 0 ? height: 0;
- },
-
- /**
- * 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 dom = this.dom,
- width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth;
- return width > 0 ? width: 0;
- },
-
- /**
- * Set the width of this Element.
- * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
- * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
- * </ul></div>
- * @return {Ext.Element} this
- */
- setWidth: function(width) {
- var me = this;
- me.dom.style.width = El.addUnits(width);
- return me;
- },
-
- /**
- * Set the height of this Element.
- * <pre><code>
- // change the height to 200px and animate with default configuration
- Ext.fly('elementId').setHeight(200, true);
-
- // 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"); }
- });
- * </code></pre>
- * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
- * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
- * </ul></div>
- * @return {Ext.Element} this
- */
- setHeight: function(height) {
- var me = this;
- me.dom.style.height = El.addUnits(height);
- 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:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
- * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
- * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
- * </ul></div>
- * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
- * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
- * </ul></div>
- * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
- * @return {Ext.Element} this
- */
- setSize: function(width, height) {
- var me = this,
- style = me.dom.style;
-
- if (Ext.isObject(width)) {
- // in case of object from getSize()
- height = width.height;
- width = width.width;
- }
-
- style.width = El.addUnits(width);
- style.height = El.addUnits(height);
- return me;
- },
-
- /**
- * 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 <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
- * @return {Number} The width of the sides passed added together
- */
- getBorderWidth: function(side) {
- return this.sumStyles(side, El.borders);
- },
-
- /**
- * Gets the size 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 <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
- * @return {Number} The padding of the sides passed added together
- */
- getPadding: function(side) {
- return this.sumStyles(side, El.paddings);
- },
-
- /**
- * Gets the size of the margins(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 <tt>'lr'</tt> would get the margin <b><u>l</u></b>eft + the margin <b><u>r</u></b>ight.
- * @return {Number} The margin of the sides passed added together
- */
- getMargin: function(side) {
- return this.sumStyles(side, El.margins);
- },
-
- /**
- * <p>Returns the dimensions of the element available to lay content out in.<p>
- * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
- */
- getViewSize: function() {
- var doc = document,
- dom = this.dom;
-
- if (dom == doc || dom == doc.body) {
- return {
- width: El.getViewportWidth(),
- height: El.getViewportHeight()
- };
- }
- else {
- return {
- width: dom.clientWidth,
- height: dom.clientHeight
- };
- }
- },
-
- /**
- * 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)}
- */
- getSize: function(contentSize) {
- var dom = this.dom;
- return {
- width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth),
- height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight)
- };
- },
-
- /**
- * Forces the browser to repaint this element
- * @return {Ext.Element} this
- */
- repaint: function() {
- var dom = this.dom;
- this.addCls("x-repaint");
- dom.style.background = 'transparent none';
- setTimeout(function() {
- dom.style.background = null;
- Ext.get(dom).removeCls("x-repaint");
- },
- 1);
- return this;
- },
-
- /**
- * Retrieves the width of the element accounting for the left and right
- * margins.
- */
- getOuterWidth: function() {
- return this.getWidth() + this.getMargin('lr');
- },
-
- /**
- * Retrieves the height of the element account for the top and bottom
- * margins.
- */
- getOuterHeight: function() {
- return this.getHeight() + this.getMargin('tb');
- },
-
- // private
- sumStyles: function(sides, styles) {
- var val = 0,
- m = sides.match(/\w/g),
- len = m.length,
- s,
- i;
-
- for (i = 0; i < len; i++) {
- s = m[i] && parseFloat(this.getStyle(styles[m[i]])) || 0;
- if (s) {
- val += Math.abs(s);
- }
- }
- return val;
- }
- });
-})();
-/**
- * @class Ext.Element
- */
-Ext.Element.addMethods({
- /**
- * 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;
-
- 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 (Ext.DomQuery.is(p, simpleSelector)) {
- return returnEl ? Ext.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/CompositeElement} The composite element
- */
- select : function(selector, composite) {
- return Ext.Element.select(selector, this.dom, composite);
- },
-
- /**
- * 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 Ext.DomQuery.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)
- */
- down : function(selector, returnDom) {
- var n = Ext.DomQuery.selectNode(selector, this.dom);
- return returnDom ? n : Ext.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)
- */
- child : function(selector, returnDom) {
- var node,
- me = this,
- id;
- id = Ext.get(me).id;
- // Escape . or :
- id = id.replace(/[\.:]/g, "\\$0");
- node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
- return returnDom ? node : Ext.get(node);
- },
-
- /**
- * 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) {
- if (!this.dom)
- return null;
-
- var n = this.dom[start];
- while (n) {
- if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
- return !returnDom ? Ext.get(n) : n;
- }
- n = n[dir];
- }
- return null;
- }
-});
-
-/**
- * @class Ext.Element
- */
-Ext.Element.addMethods({
- /**
- * Gets the Scroller instance of the first parent that has one.
- * @return {Ext.util.Scroller/null} The first parent scroller
- */
- getScrollParent : function() {
- var parent = this.dom, scroller;
- while (parent && parent != document.body) {
- if (parent.id && (scroller = Ext.ScrollManager.get(parent.id))) {
- return scroller;
- }
- parent = parent.parentNode;
- }
- return null;
- }
-});
-
-/**
- * @class Ext.Element
- */
-Ext.Element.addMethods({
- /**
- * Appends the passed element(s) to this element
- * @param {String/HTMLElement/Array/Element/CompositeElement} el
- * @return {Ext.Element} this
- */
- appendChild : function(el) {
- return Ext.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) {
- Ext.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 = Ext.getDom(el);
- 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 = Ext.getDom(el);
- 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 = Ext.getDom(el);
- this.dom.insertBefore(el, this.dom.firstChild);
- return !returnDom ? Ext.get(el) : el;
- }
- else { // dh config
- return this.createChild(el, this.dom.firstChild, returnDom);
- }
- },
-
- /**
- * 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 .;ll;l,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(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
- if (!returnDom) {
- rt = Ext.get(rt);
- }
- }else{
- if (isAfter && !me.dom.nextSibling) {
- rt = Ext.DomHelper.append(me.dom.parentNode, el, !returnDom);
- } else {
- rt = Ext.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
- }
- }
- return rt;
- },
-
- /**
- * Replaces the passed element with this element
- * @param {Mixed} el The element to replace
- * @return {Ext.Element} this
- */
- replace : function(el) {
- el = Ext.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 = Ext.get(el);
- me.dom.parentNode.insertBefore(el, me.dom);
- }else{
- el = Ext.DomHelper.insertBefore(me.dom, el);
- }
-
- delete Ext.cache[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'};
- if (insertBefore) {
- return Ext.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
- }
- else {
- return Ext.DomHelper[!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 = Ext.DomHelper.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 = Ext.DomHelper.insertHtml(where, this.dom, html);
- return returnEl ? Ext.get(el) : el;
- }
-});
-
-/**
- * @class Ext.Element
- */
-Ext.Element.addMethods({
- /**
- * 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 {Object} size (optional) An object containing the size to use for calculating anchor position
- * {width: (target width), height: (target height)} (defaults to the element's current size)
- * @return {Array} [x, y] An array containing the element's x and y coordinates
- */
- getAnchorXY: function(anchor, local, size) {
- //Passing a different size is useful for pre-calculating anchors,
- //especially for anchored animations that change the el size.
- anchor = (anchor || "tl").toLowerCase();
- size = size || {};
-
- var me = this,
- vp = me.dom == document.body || me.dom == document,
- width = size.width || vp ? window.innerWidth: me.getWidth(),
- height = size.height || vp ? window.innerHeight: me.getHeight(),
- xy,
- rnd = Math.round,
- myXY = me.getXY(),
- extraX = vp ? 0: !local ? myXY[0] : 0,
- extraY = vp ? 0: !local ? myXY[1] : 0,
- hash = {
- c: [rnd(width * 0.5), rnd(height * 0.5)],
- t: [rnd(width * 0.5), 0],
- l: [0, rnd(height * 0.5)],
- r: [width, rnd(height * 0.5)],
- b: [rnd(width * 0.5), height],
- tl: [0, 0],
- bl: [0, height],
- br: [width, height],
- tr: [width, 0]
- };
-
- xy = hash[anchor];
- return [xy[0] + extraX, xy[1] + extraY];
- },
-
- /**
- * 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]
- */
- getAlignToXY: function(el, position, offsets) {
- el = Ext.get(el);
-
- if (!el || !el.dom) {
- throw new Error("Element.alignToXY with an element that doesn't exist");
- }
- offsets = offsets || [0, 0];
-
- if (!position || position == '?') {
- position = 'tl-bl?';
- }
- else if (! (/-/).test(position) && position !== "") {
- position = 'tl-' + position;
- }
- position = position.toLowerCase();
-
- var me = this,
- matches = position.match(/^([a-z]+)-([a-z]+)(\?)?$/),
- dw = window.innerWidth,
- dh = window.innerHeight,
- p1 = "",
- p2 = "",
- a1,
- a2,
- x,
- y,
- swapX,
- swapY,
- p1x,
- p1y,
- p2x,
- p2y,
- width,
- height,
- region,
- constrain;
-
- if (!matches) {
- throw "Element.alignTo with an invalid alignment " + position;
- }
-
- p1 = matches[1];
- p2 = matches[2];
- constrain = !!matches[3];
-
- //Subtract the aligned el's internal xy from the target's offset xy
- //plus custom offset to get the aligned el's new offset xy
- a1 = me.getAnchorXY(p1, true);
- a2 = el.getAnchorXY(p2, false);
-
- x = a2[0] - a1[0] + offsets[0];
- y = a2[1] - a1[1] + offsets[1];
-
- if (constrain) {
- width = me.getWidth();
- height = me.getHeight();
-
- region = el.getPageBox();
-
- //If we are at a viewport boundary and the aligned el is anchored on a target border that is
- //perpendicular to the vp border, allow the aligned el to slide on that border,
- //otherwise swap the aligned el to the opposite border of the target.
- p1y = p1.charAt(0);
- p1x = p1.charAt(p1.length - 1);
- p2y = p2.charAt(0);
- p2x = p2.charAt(p2.length - 1);
-
- swapY = ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
- swapX = ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));
-
- if (x + width > dw) {
- x = swapX ? region.left - width: dw - width;
- }
- if (x < 0) {
- x = swapX ? region.right: 0;
- }
- if (y + height > dh) {
- y = swapY ? region.top - height: dh - height;
- }
- if (y < 0) {
- y = swapY ? region.bottom: 0;
- }
- }
-
- return [x, y];
- }
-
- /**
- * 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
- // });
- //
- // 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;
- // },
- /**
- * Remove any anchor to this element. See {@link #anchorTo}.
- * @return {Ext.Element} this
- */
- // removeAnchor : function(){
- // var me = this,
- // 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 me;
- // },
- //
- // // private
- // getAnchor : function(){
- // var data = Ext.Element.data,
- // dom = this.dom;
- // if (!dom) {
- // return;
- // }
- // var anchor = data(dom, '_anchor');
- //
- // if(!anchor){
- // anchor = data(dom, '_anchor', {});
- // }
- // return anchor;
- // },
- /**
- * 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:
- * <ul>
- * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
- * <li><b>One anchor (deprecated)</b>: 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. <i>This method has been
- * deprecated in favor of the newer two anchor syntax below</i>.</li>
- * <li><b>Two anchors</b>: 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.</li>
- * </ul>
- * 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:
-<pre>
-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
-</pre>
-Example Usage:
-<pre><code>
-// align el to other-el using the default positioning ("tl-bl", non-constrained)
-el.alignTo("other-el");
-
-// align the top left corner of el with the top right corner of other-el (constrained to viewport)
-el.alignTo("other-el", "tr?");
-
-// 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]);
-</code></pre>
- * @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
- */
- // 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;
- // },
- //
- // // 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];
- // }
- // }
- //
- // var s = el.getScroll();
- //
- // vx += offsets.left + s.left;
- // vy += offsets.top + s.top;
- //
- // 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;
- //
- // // only move it if it needs it
- // var moved = false;
- //
- // // first validate right/bottom
- // if((x + w) > vr){
- // x = vr - w;
- // moved = true;
- // }
- // if((y + h) > vb){
- // y = vb - h;
- // moved = true;
- // }
- // // then make sure top/left isn't negative
- // if(x < vx){
- // x = vx;
- // moved = true;
- // }
- // if(y < vy){
- // y = vy;
- // moved = true;
- // }
- // return moved ? [x, y] : false;
- // };
- // }(),
- //
- // // 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');
- // },
- //
- // /**
- // * 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.CompositeElement
- * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
- * members, or to perform collective actions upon the whole set.</p>
- *
- * Example:<pre><code>
-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);
-</code>
- */
-Ext.CompositeElement = function(els, root) {
- /**
- * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
- * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
- * to augment the capabilities of the CompositeElement class may use it when adding
- * methods to the class.</p>
- * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
- * following siblings of selected elements, the code would be</p><code><pre>
-Ext.override(Ext.CompositeElement, {
- 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);
- }
-});</pre></code>
- * @type Array
- * @property elements
- */
- this.elements = [];
- this.add(els, root);
- this.el = new Ext.Element.Flyweight();
-};
-
-Ext.CompositeElement.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;
- },
-
- /**
- * 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.
- */
- 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;
- },
-
- invoke : function(fn, args) {
- var me = this,
- els = me.elements,
- len = els.length,
- e,
- i;
-
- for (i = 0; i < len; i++) {
- e = els[i];
- if (e) {
- Ext.Element.prototype[fn].apply(me.getElement(e), args);
- }
- }
- return me;
- },
- /**
- * Returns a flyweight Element of the dom element object at the specified index
- * @param {Number} index
- * @return {Ext.Element}
- */
- item : function(index) {
- var me = this,
- el = me.elements[index],
- out = null;
-
- if (el){
- out = me.getElement(el);
- }
- return out;
- },
-
- // fixes scope with flyweight
- addListener : function(eventName, handler, scope, opt) {
- var els = this.elements,
- len = els.length,
- i, e;
-
- for (i = 0; i<len; i++) {
- e = els[i];
- if (e) {
- Ext.EventManager.on(e, eventName, handler, scope || e, opt);
- }
- }
- return this;
- },
-
- /**
- * <p>Calls the passed function for each element in this composite.</p>
- * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
- * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
- * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
- * a reference to the dom node, use el.dom.</b></div></li>
- * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
- * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
- * </ul>
- * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
- * @return {CompositeElement} this
- */
- each : function(fn, scope) {
- var me = this,
- els = me.elements,
- len = els.length,
- i, e;
-
- for (i = 0; i<len; i++) {
- e = els[i];
- if (e) {
- e = this.getElement(e);
- if(fn.call(scope || e, e, me, i)){
- break;
- }
- }
- }
- 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;
- },
-
- /**
- * 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:<ul>
- * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
- * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
- * </ul>
- * @return {CompositeElement} this
- */
- filter : function(selector) {
- var els = [],
- me = this,
- elements = me.elements,
- 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;
- },
-
- /**
- * Returns the first Element
- * @return {Ext.Element}
- */
- first : function() {
- return this.item(0);
- },
-
- /**
- * Returns the last Element
- * @return {Ext.Element}
- */
- last : function() {
- return this.item(this.getCount()-1);
- },
-
- /**
- * Returns true if this composite contains the passed element
- * @param {Mixed} el 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;
- },
-
- /**
- * Find the index of the passed element within the composite collection.
- * @param {Mixed} el 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));
- },
-
- /**
- * Removes all elements.
- */
- clear : function() {
- this.elements = [];
- }
-};
-
-Ext.CompositeElement.prototype.on = Ext.CompositeElement.prototype.addListener;
-
-(function(){
-var fnName,
- ElProto = Ext.Element.prototype,
- CelProto = Ext.CompositeElement.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);
-
- }
-}
-})();
-
-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.CompositeElement CompositeElement} 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 {CompositeElement}
- * @member Ext.Element
- * @method select
- */
-Ext.Element.select = function(selector, root, composite) {
- var els;
- composite = (composite === false) ? false : true;
- if (typeof selector == "string") {
- els = Ext.Element.selectorFunction(selector, root);
- } else if (selector.length !== undefined) {
- els = selector;
- } else {
- throw new Error("Invalid selector");
- }
- return composite ? new Ext.CompositeElement(els) : 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.CompositeElement CompositeElement} 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 {CompositeElement}
- * @member Ext
- * @method select
- */
-Ext.select = Ext.Element.select;
-
-// Backwards compatibility with desktop
-Ext.CompositeElementLite = Ext.CompositeElement;
-
-/**
- * @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;
- },
-
- /**
- * 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;
- },
-
- /**
- * 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;
- }
-});
-
-/**
- * @class Ext.DomHelper
- * <p>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.</p>
- *
- * <p><b><u>DomHelper element specification object</u></b></p>
- * <p>A specification object is used when creating elements. Attributes of this object
- * are assumed to be element attributes, except for 4 special attributes:
- * <div class="mdetail-params"><ul>
- * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
- * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
- * same kind of element definition objects to be created and appended. These can be nested
- * as deep as you want.</div></li>
- * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
- * This will end up being either the "class" attribute on a HTML fragment or className
- * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
- * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
- * </ul></div></p>
- *
- * <p><b><u>Insertion methods</u></b></p>
- * <p>Commonly used insertion methods:
- * <div class="mdetail-params"><ul>
- * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
- * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
- * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
- * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
- * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
- * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
- * </ul></div></p>
- *
- * <p><b><u>Example</u></b></p>
- * <p>This is an example, where an unordered list with 3 children items is appended to an existing
- * element with id <tt>'my-div'</tt>:<br>
- <pre><code>
-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
-);
- </code></pre></p>
- * <p>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:<pre><code>
-dh.append('my-ul', [
- {tag: 'li', id: 'item3', html: 'List Item 3'},
- {tag: 'li', id: 'item4', html: 'List Item 4'}
-]);
- * </code></pre></p>
- *
- * <p><b><u>Templating</u></b></p>
- * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
- * <tt>{@link #createTemplate}</tt> 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:
- * <pre><code>
-// 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
-}
- * </code></pre></p>
- * <p>An example using a template:<pre><code>
-var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
-
-var tpl = new Ext.DomHelper.createTemplate(html);
-tpl.append('blog-roll', ['link1', 'http://www.tommymaintz.com/', "Tommy's Site"]);
-tpl.append('blog-roll', ['link2', 'http://www.avins.org/', "Jamie's Site"]);
- * </code></pre></p>
- *
- * <p>The same example using named parameters:<pre><code>
-var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
-
-var tpl = new Ext.DomHelper.createTemplate(html);
-tpl.append('blog-roll', {
- id: 'link1',
- url: 'http://www.tommymaintz.com/',
- text: "Tommy's Site"
-});
-tpl.append('blog-roll', {
- id: 'link2',
- url: 'http://www.avins.org/',
- text: "Jamie's Site"
-});
- * </code></pre></p>
- *
- * <p><b><u>Compiling Templates</u></b></p>
- * <p>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.
- * <pre><code>
-var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
-
-var tpl = new Ext.DomHelper.createTemplate(html);
-tpl.compile();
-
-//... use template like normal
- * </code></pre></p>
- *
- * <p><b><u>Performance Boost</u></b></p>
- * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
- * of DOM can significantly boost performance.</p>
- * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
- * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
- * results in the creation of a text node. Usage:</p>
- * <pre><code>
-Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
- * </code></pre>
- * @singleton
- */
-Ext.DomHelper = {
- emptyTags : /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
- confRe : /tag|children|cn|html$/i,
- endRe : /end/i,
-
- /**
- * Returns the markup for the passed Element(s) config.
- * @param {Object} o The DOM object spec (and children)
- * @return {String}
- */
- markup : function(o) {
- var b = '',
- attr,
- val,
- key,
- keyVal,
- cn;
-
- if (typeof o == "string") {
- b = o;
- }
- else if (Ext.isArray(o)) {
- for (var i=0; i < o.length; i++) {
- if (o[i]) {
- b += this.markup(o[i]);
- }
- };
- }
- else {
- b += '<' + (o.tag = o.tag || 'div');
- for (attr in o) {
- if (!o.hasOwnProperty(attr)) {
- continue;
- }
- val = o[attr];
- if (!this.confRe.test(attr)) {
- if (typeof val == "object") {
- b += ' ' + attr + '="';
- for (key in val) {
- if (!val.hasOwnProperty(key)) {
- continue;
- }
- 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 (this.emptyTags.test(o.tag)) {
- b += '/>';
- }
- else {
- b += '>';
- if ((cn = o.children || o.cn)) {
- b += this.markup(cn);
- }
- else if (o.html) {
- b += o.html;
- }
- b += '</' + o.tag + '>';
- }
- }
- return b;
- },
-
- /**
- * 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.
- */
- applyStyles : function(el, styles) {
- if (styles) {
- var i = 0,
- len,
- style;
-
- el = Ext.fly(el);
- if (typeof styles == 'function') {
- styles = styles.call();
- }
- if (typeof styles == 'string'){
- styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
- for(len = styles.length; i < len;){
- el.setStyle(styles[i++], styles[i++]);
- }
- } else if (Ext.isObject(styles)) {
- el.setStyle(styles);
- }
- }
- },
-
- /**
- * Inserts an HTML fragment into the DOM.
- * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
- * @param {HTMLElement} el The context element
- * @param {String} html The HTML fragment
- * @return {HTMLElement} The new node
- */
- insertHtml : function(where, el, html) {
- var hash = {},
- hashVal,
- setStart,
- range,
- frag,
- rangeEl,
- rs;
-
- where = where.toLowerCase();
-
- // add these here because they are used in both branches of the condition.
- hash['beforebegin'] = ['BeforeBegin', 'previousSibling'];
- hash['afterend'] = ['AfterEnd', 'nextSibling'];
-
- range = el.ownerDocument.createRange();
- setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');
- if (hash[where]) {
- range[setStart](el);
- frag = range.createContextualFragment(html);
- el.parentNode.insertBefore(frag, where == 'beforebegin' ? el : el.nextSibling);
- return el[(where == 'beforebegin' ? 'previous' : 'next') + 'Sibling'];
- }
- else {
- rangeEl = (where == 'afterbegin' ? 'first' : 'last') + 'Child';
- if (el.firstChild) {
- range[setStart](el[rangeEl]);
- frag = range.createContextualFragment(html);
- if (where == 'afterbegin') {
- el.insertBefore(frag, el.firstChild);
- }
- else {
- el.appendChild(frag);
- }
- }
- else {
- el.innerHTML = html;
- }
- return el[rangeEl];
- }
-
- throw 'Illegal insertion point -> "' + where + '"';
- },
-
- /**
- * 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
- */
- insertBefore : function(el, o, returnElement) {
- return this.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
- */
- insertAfter : function(el, o, returnElement) {
- return this.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
- */
- insertFirst : function(el, o, returnElement) {
- return this.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
- */
- append : function(el, o, returnElement) {
- return this.doInsert(el, o, returnElement, 'beforeend', '', true);
- },
-
- /**
- * Creates new DOM element(s) and overwrites the contents of el with them.
- * @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
- */
- overwrite : function(el, o, returnElement) {
- el = Ext.getDom(el);
- el.innerHTML = this.markup(o);
- return returnElement ? Ext.get(el.firstChild) : el.firstChild;
- },
-
- doInsert : function(el, o, returnElement, pos, sibling, append) {
- var newNode = this.insertHtml(pos, Ext.getDom(el), this.markup(o));
- return returnElement ? Ext.get(newNode, true) : newNode;
- }
-};
-
-/**
- * @class Ext.DomQuery
- * Provides functionality to select elements on the page based on a CSS selector.
- *
-<p>
-All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector.
-</p>
-<h4>Element Selectors:</h4>
-<ul class="list">
- <li> <b>*</b> any element</li>
- <li> <b>E</b> an element with the tag E</li>
- <li> <b>E F</b> All descendent elements of E that have the tag F</li>
- <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
- <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
- <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
-</ul>
-<h4>Attribute Selectors:</h4>
-<p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
-<ul class="list">
- <li> <b>E[foo]</b> has an attribute "foo"</li>
- <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
- <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
- <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
- <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
- <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
- <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
-</ul>
-<h4>Pseudo Classes:</h4>
-<ul class="list">
- <li> <b>E:first-child</b> E is the first child of its parent</li>
- <li> <b>E:last-child</b> E is the last child of its parent</li>
- <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
- <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
- <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
- <li> <b>E:only-child</b> E is the only child of its parent</li>
- <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
- <li> <b>E:first</b> the first E in the resultset</li>
- <li> <b>E:last</b> the last E in the resultset</li>
- <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
- <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
- <li> <b>E:even</b> shortcut for :nth-child(even)</li>
- <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
- <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
- <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
- <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
- <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
- <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
- <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
-</ul>
-<h4>CSS Value Selectors:</h4>
-<ul class="list">
- <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
- <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
- <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
- <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
- <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
- <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
-</ul>
- * @singleton
- */
-Ext.DomQuery = {
- /**
- * Selects a group of elements.
- * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
- * @param {Node/String} root (optional) The start of the query (defaults to document).
- * @return {Array} An Array of DOM elements which match the selector. If there are
- * no matches, and empty Array is returned.
- */
- select : function(q, root) {
- var results = [],
- nodes,
- i,
- j,
- qlen,
- nlen;
-
- root = root || document;
- if (typeof root == 'string') {
- root = document.getElementById(root);
- }
-
- q = q.split(",");
- for (i = 0, qlen = q.length; i < qlen; i++) {
- if (typeof q[i] == 'string') {
- nodes = root.querySelectorAll(q[i]);
-
- for (j = 0, nlen = nodes.length; j < nlen; j++) {
- results.push(nodes[j]);
- }
- }
- }
-
- return results;
- },
-
- /**
- * Selects a single element.
- * @param {String} selector The selector/xpath query
- * @param {Node} root (optional) The start of the query (defaults to document).
- * @return {HtmlElement} The DOM element which matched the selector.
- */
- selectNode : function(q, root) {
- return Ext.DomQuery.select(q, root)[0];
- },
-
- /**
- * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
- * @param {String/HTMLElement/Array} el An element id, element or array of elements
- * @param {String} selector The simple selector to test
- * @return {Boolean}
- */
- is : function(el, q) {
- if (typeof el == "string") {
- el = document.getElementById(el);
- }
- return Ext.DomQuery.select(q).indexOf(el) !== -1;
- }
-};
-
-Ext.Element.selectorFunction = Ext.DomQuery.select;
-Ext.query = Ext.DomQuery.select;
-
-/**
- * @class Ext.Anim
- * @extends Object
- * <p>Ext.Anim is used to excute animations defined in {@link Ext.anims}. The {@link #run} method can take any of the
- * properties defined below.</p>
- *
- * <h2>Example usage:</h2>
- * <code><pre>
-Ext.Anim.run(this, 'fade', {
- out: false,
- autoClear: true
-});
- * </pre></code>
- *
- * <p>Animations are disabled on Android and Blackberry by default using the {@link #disableAnimations} property.</p>
- * @singleton
- */
-Ext.Anim = Ext.extend(Object, {
- isAnim: true,
-
- /**
- * @cfg {Boolean} disableAnimations
- * True to disable animations. By default, animations are disabled on Android and Blackberry
- */
- disableAnimations: false,
-// disableAnimations: (!Ext.is.iOS || Ext.is.Blackberry) ? true : false,
-
- defaultConfig: {
- /**
- * @cfg {Object} from
- * An object of CSS values which the animation begins with. If you define a CSS property here, you must also
- * define it in the {@link #to} config.
- */
- from: {},
-
- /**
- * @cfg {Object} to
- * An object of CSS values which the animation ends with. If you define a CSS property here, you must also
- * define it in the {@link #from} config.
- */
- to: {},
-
- /**
- * @cfg {Number} duration
- * Time in milliseconds for the animation to last.
- * (Defaults to 250).
- */
- duration: 250,
-
- /**
- * @cfg {Number} delay Time to delay before starting the animation.
- * (Defaults to 0).
- */
- delay: 0,
-
- /**
- * @cfg {String} easing
- * Valid values are 'ease', 'linear', ease-in', 'ease-out', 'ease-in-out' or a cubic-bezier curve as defined by CSS.
- * (Defaults to 'ease-in-out').
- */
- easing: 'ease-in-out',
-
- /**
- * @cfg {Boolean} autoClear
- * True to remove all custom CSS defined in the {@link #to} config when the animation is over.
- * (Defaults to true).
- */
- autoClear: true,
-
- /**
- * @cfg {Boolean} out
- * True if you want the animation to slide out of the screen.
- * (Defaults to true).
- */
- out: true,
-
- /**
- * @cfg {String} direction
- * Valid values are 'left', 'right', 'up', 'down' and null.
- * (Defaults to null).
- */
- direction: null,
-
- /**
- * @cfg {Boolean} reverse
- * True to reverse the animation direction. For example, if the animation direction was set to 'left', it would
- * then use 'right'.
- * (Defaults to false).
- */
- reverse: false
- },
-
- /**
- * @cfg {Function} before
- * Code to execute before starting the animation.
- */
-
- /**
- * @cfg {Scope} scope
- * Scope to run the {@link before} function in.
- */
-
- opposites: {
- 'left': 'right',
- 'right': 'left',
- 'up': 'down',
- 'down': 'up'
- },
-
- constructor: function(config) {
- config = Ext.apply({}, config || {}, this.defaultConfig);
- this.config = config;
-
- Ext.Anim.superclass.constructor.call(this);
-
- this.running = [];
- },
-
- initConfig : function(el, runConfig) {
- var me = this,
- runtime = {},
- config = Ext.apply({}, runConfig || {}, me.config);
-
- config.el = el = Ext.get(el);
-
- if (config.reverse && me.opposites[config.direction]) {
- config.direction = me.opposites[config.direction];
- }
-
- if (me.config.before) {
- me.config.before.call(config, el, config);
- }
-
- if (runConfig.before) {
- runConfig.before.call(config.scope || config, el, config);
- }
-
- return config;
- },
-
- run: function(el, config) {
- el = Ext.get(el);
- config = config || {};
-
- var me = this,
- style = el.dom.style,
- property,
- after = config.after;
-
- config = this.initConfig(el, config);
-
- if (this.disableAnimations) {
- for (property in config.to) {
- if (!config.to.hasOwnProperty(property)) {
- continue;
- }
- style[property] = config.to[property];
- }
- this.onTransitionEnd(null, el, {
- config: config,
- after: after
- });
- return me;
- }
-
- el.un('webkitTransitionEnd', me.onTransitionEnd, me);
-
- style.webkitTransitionDuration = '0ms';
- for (property in config.from) {
- if (!config.from.hasOwnProperty(property)) {
- continue;
- }
- style[property] = config.from[property];
- }
-
- setTimeout(function() {
- // If this element has been destroyed since the timeout started, do nothing
- if (!el.dom) {
- return;
- }
-
- // If this is a 3d animation we have to set the perspective on the parent
- if (config.is3d === true) {
- el.parent().setStyle({
- // TODO: Ability to set this with 3dConfig
- '-webkit-perspective': '1200',
- '-webkit-transform-style': 'preserve-3d'
- });
- }
-
- style.webkitTransitionDuration = config.duration + 'ms';
- style.webkitTransitionProperty = 'all';
- style.webkitTransitionTimingFunction = config.easing;
-
- // Bind our listener that fires after the animation ends
- el.on('webkitTransitionEnd', me.onTransitionEnd, me, {
- single: true,
- config: config,
- after: after
- });
-
- for (property in config.to) {
- if (!config.to.hasOwnProperty(property)) {
- continue;
- }
- style[property] = config.to[property];
- }
- }, config.delay || 5);
-
- me.running[el.id] = config;
- return me;
- },
-
- onTransitionEnd: function(ev, el, o) {
- el = Ext.get(el);
- var style = el.dom.style,
- config = o.config,
- property,
- me = this;
-
- if (config.autoClear) {
- for (property in config.to) {
- if (!config.to.hasOwnProperty(property)) {
- continue;
- }
- style[property] = '';
- }
- }
-
- style.webkitTransitionDuration = null;
- style.webkitTransitionProperty = null;
- style.webkitTransitionTimingFunction = null;
-
- if (config.is3d) {
- el.parent().setStyle({
- '-webkit-perspective': '',
- '-webkit-transform-style': ''
- });
- }
-
- if (me.config.after) {
- me.config.after.call(config, el, config);
- }
-
- if (o.after) {
- o.after.call(config.scope || me, el, config);
- }
-
- delete me.running[el.id];
- }
-});
-
-Ext.Anim.seed = 1000;
-
-/**
- * Used to run an animation on a specific element. Use the config argument to customize the animation
- * @param {Ext.Element/Element} el The element to animate
- * @param {String} anim The animation type, defined in {@link #Ext.anims}
- * @param {Object} config The config object for the animation
- * @method run
- */
-Ext.Anim.run = function(el, anim, config) {
- if (el.isComponent) {
- el = el.el;
- }
-
- config = config || {};
-
- if (anim.isAnim) {
- anim.run(el, config);
- }
- else {
- if (Ext.isObject(anim)) {
- if (config.before && anim.before) {
- config.before = Ext.createInterceptor(config.before, anim.before, anim.scope);
- }
- if (config.after && anim.after) {
- config.after = Ext.createInterceptor(config.after, anim.after, anim.scope);
- }
- config = Ext.apply({}, config, anim);
- anim = anim.type;
- }
-
- if (!Ext.anims[anim]) {
- throw anim + ' is not a valid animation type.';
- }
- else {
- // add el check to make sure dom exists.
- if (el && el.dom) {
- Ext.anims[anim].run(el, config);
- }
-
- }
- }
-};
-
-/**
- * @class Ext.anims
- * <p>Defines different types of animations. <strong>flip, cube, wipe</strong> animations do not work on Android.</p>
- * <p>Please refer to {@link Ext.Anim} on how to use animations.</p>
- * @singleton
- */
-Ext.anims = {
- /**
- * Fade Animation
- */
- fade: new Ext.Anim({
- before: function(el) {
- var fromOpacity = 1,
- toOpacity = 1,
- curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
- zIndex = curZ;
-
- if (this.out) {
- toOpacity = 0;
- } else {
- zIndex = curZ + 1;
- fromOpacity = 0;
- }
-
- this.from = {
- 'opacity': fromOpacity,
- 'z-index': zIndex
- };
- this.to = {
- 'opacity': toOpacity,
- 'z-index': zIndex
- };
- }
- }),
-
- /**
- * Slide Animation
- */
- slide: new Ext.Anim({
- direction: 'left',
- cover: false,
- reveal: false,
-
- before: function(el) {
- var curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
- zIndex = curZ + 1,
- toX = 0,
- toY = 0,
- fromX = 0,
- fromY = 0,
- elH = el.getHeight(),
- elW = el.getWidth();
-
- if (this.direction == 'left' || this.direction == 'right') {
- if (this.out == true) {
- toX = -elW;
- }
- else {
- fromX = elW;
- }
- }
- else if (this.direction == 'up' || this.direction == 'down') {
- if (this.out == true) {
- toY = -elH;
- }
- else {
- fromY = elH;
- }
- }
-
- if (this.direction == 'right' || this.direction == 'down') {
- toY *= -1;
- toX *= -1;
- fromY *= -1;
- fromX *= -1;
- }
-
- if (this.cover && this.out) {
- toX = 0;
- toY = 0;
- zIndex = curZ;
- }
- else if (this.reveal && !this.out) {
- fromX = 0;
- fromY = 0;
- zIndex = curZ;
- }
-
- this.from = {
- '-webkit-transform': 'translate3d(' + fromX + 'px, ' + fromY + 'px, 0)',
- 'z-index': zIndex,
- 'opacity': 0.99
- };
- this.to = {
- '-webkit-transform': 'translate3d(' + toX + 'px, ' + toY + 'px, 0)',
- 'z-index': zIndex,
- 'opacity': 1
- };
- }
- }),
-
- /**
- * Pop Animation
- */
- pop: new Ext.Anim({
- scaleOnExit: true,
- before: function(el) {
- var fromScale = 1,
- toScale = 1,
- fromOpacity = 1,
- toOpacity = 1,
- curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
- fromZ = curZ,
- toZ = curZ;
-
- if (!this.out) {
- fromScale = 0.01;
- fromZ = curZ + 1;
- toZ = curZ + 1;
- fromOpacity = 0;
- }
- else {
- if (this.scaleOnExit) {
- toScale = 0.01;
- toOpacity = 0;
- } else {
- toOpacity = 0.8;
- }
- }
-
- this.from = {
- '-webkit-transform': 'scale(' + fromScale + ')',
- '-webkit-transform-origin': '50% 50%',
- 'opacity': fromOpacity,
- 'z-index': fromZ
- };
-
- this.to = {
- '-webkit-transform': 'scale(' + toScale + ')',
- '-webkit-transform-origin': '50% 50%',
- 'opacity': toOpacity,
- 'z-index': toZ
- };
- }
- })
-};
-/**
- * @class Ext.anims
- */
-Ext.apply(Ext.anims, {
- /**
- * Flip Animation
- */
- flip: new Ext.Anim({
- is3d: true,
- direction: 'left',
- before: function(el) {
- var rotateProp = 'Y',
- fromScale = 1,
- toScale = 1,
- fromRotate = 0,
- toRotate = 0;
-
- if (this.out) {
- toRotate = -180;
- toScale = 0.8;
- }
- else {
- fromRotate = 180;
- fromScale = 0.8;
- }
-
- if (this.direction == 'up' || this.direction == 'down') {
- rotateProp = 'X';
- }
-
- if (this.direction == 'right' || this.direction == 'left') {
- toRotate *= -1;
- fromRotate *= -1;
- }
-
- this.from = {
- '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg) scale(' + fromScale + ')',
- '-webkit-backface-visibility': 'hidden'
- };
- this.to = {
- '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) scale(' + toScale + ')',
- '-webkit-backface-visibility': 'hidden'
- };
- }
- }),
-
- /**
- * Cube Animation
- */
- cube: new Ext.Anim({
- is3d: true,
- direction: 'left',
- style: 'outer',
- before: function(el) {
- var origin = '0% 0%',
- fromRotate = 0,
- toRotate = 0,
- rotateProp = 'Y',
- fromZ = 0,
- toZ = 0,
- fromOpacity = 1,
- toOpacity = 1,
- zDepth,
- elW = el.getWidth(),
- elH = el.getHeight(),
- showTranslateZ = true,
- fromTranslate = ' translateX(0)',
- toTranslate = '';
-
- if (this.direction == 'left' || this.direction == 'right') {
- if (this.out) {
- origin = '100% 100%';
- toZ = elW;
- toOpacity = 0.5;
- toRotate = -90;
- } else {
- origin = '0% 0%';
- fromZ = elW;
- fromOpacity = 0.5;
- fromRotate = 90;
- }
- } else if (this.direction == 'up' || this.direction == 'down') {
- rotateProp = 'X';
- if (this.out) {
- origin = '100% 100%';
- toZ = elH;
- toRotate = 90;
- } else {
- origin = '0% 0%';
- fromZ = elH;
- fromRotate = -90;
- }
- }
-
- if (this.direction == 'down' || this.direction == 'right') {
- fromRotate *= -1;
- toRotate *= -1;
- origin = (origin == '0% 0%') ? '100% 100%': '0% 0%';
- }
-
- if (this.style == 'inner') {
- fromZ *= -1;
- toZ *= -1;
- fromRotate *= -1;
- toRotate *= -1;
-
- if (!this.out) {
- toTranslate = ' translateX(0px)';
- origin = '0% 50%';
- } else {
- toTranslate = fromTranslate;
- origin = '100% 50%';
- }
- }
-
- this.from = {
- '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg)' + (showTranslateZ ? ' translateZ(' + fromZ + 'px)': '') + fromTranslate,
- '-webkit-transform-origin': origin
- };
- this.to = {
- '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) translateZ(' + toZ + 'px)' + toTranslate,
- '-webkit-transform-origin': origin
- };
- },
- duration: 250
- }),
-
-
- /**
- * Wipe Animation.
- * <p>Because of the amount of calculations involved, this animation is best used on small display
- * changes or specifically for phone environments. Does not currently accept any parameters.</p>
- */
- wipe: new Ext.Anim({
- before: function(el) {
- var curZ = el.getStyle('z-index'),
- mask = '',
- toSize = '100%',
- fromSize = '100%';
-
- if (!this.out) {
- zIndex = curZ + 1;
- mask = '-webkit-gradient(linear, left bottom, right bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
- toSize = el.getHeight() * 100 + 'px';
- fromSize = el.getHeight();
-
- this.from = {
- '-webkit-mask-image': mask,
- '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
- 'z-index': zIndex,
- '-webkit-mask-position-x': 0
- };
- this.to = {
- '-webkit-mask-image': mask,
- '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
- 'z-index': zIndex,
- '-webkit-mask-position-x': -el.getWidth() * 2 + 'px'
- };
- }
- },
- duration: 500
- })
-});
-
-/**
- * @class Ext
- */
-Ext.apply(Ext, {
- /**
- * The version of the framework
- * @type String
- */
- version : '1.0.1',
- versionDetail : {
- major : 1,
- minor : 0,
- patch : 1
- },
-
- /**
- * Sets up a page for use on a mobile device.
- * @param {Object} config
- *
- * Valid configurations are:
- * <ul>
- * <li>fullscreen - Boolean - Sets an appropriate meta tag for Apple devices to run in full-screen mode.</li>
- * <li>tabletStartupScreen - String - Startup screen to be used on an iPad. The image must be 768x1004 and in portrait orientation.</li>
- * <li>phoneStartupScreen - String - Startup screen to be used on an iPhone or iPod touch. The image must be 320x460 and in
- * portrait orientation.</li>
- * <li>icon - Default icon to use. This will automatically apply to both tablets and phones. These should be 72x72.</li>
- * <li>tabletIcon - String - An icon for only tablets. (This config supersedes icon.) These should be 72x72.</li>
- * <li>phoneIcon - String - An icon for only phones. (This config supersedes icon.) These should be 57x57.</li>
- * <li>glossOnIcon - Boolean - Add gloss on icon on iPhone, iPad and iPod Touch</li>
- * <li>statusBarStyle - String - Sets the status bar style for fullscreen iPhone OS web apps. Valid options are default, black,
- * or black-translucent.</li>
- * <li>onReady - Function - Function to be run when the DOM is ready.<li>
- * <li>scope - Scope - Scope for the onReady configuraiton to be run in.</li>
- * </ul>
- */
- setup: function(config) {
- if (config && typeof config == 'object') {
- if (config.addMetaTags !== false) {
- this.addMetaTags(config);
- }
-
- if (Ext.isFunction(config.onReady)) {
- var me = this;
-
- Ext.onReady(function() {
- var args = arguments;
-
- if (config.fullscreen !== false) {
- Ext.Viewport.init(function() {
- config.onReady.apply(me, args);
- });
- }
- else {
- config.onReady.apply(this, args);
- }
- }, config.scope);
- }
- }
- },
-
- /**
- * Return the dom node for the passed String (id), dom node, or Ext.Element.
- * Here are some examples:
- * <pre><code>
-// gets dom node based on id
-var elDom = Ext.getDom('elId');
-// gets dom node based on the dom node
-var elDom1 = Ext.getDom(elDom);
-
-// If we don't know if we are working with an
-// Ext.Element or a dom node use Ext.getDom
-function(el){
- var dom = Ext.getDom(el);
- // do something with the dom node
-}
- </code></pre>
- * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
- * when this method is called to be successful.
- * @param {Mixed} el
- * @return HTMLElement
- */
- getDom : function(el) {
- if (!el || !document) {
- return null;
- }
-
- return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
- },
-
- /**
- * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
- * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval} is
- * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
- * will be ignored if passed in.</p>
- * @param {HTMLElement} node The node to remove
- */
- removeNode : function(node) {
- if (node && node.parentNode && node.tagName != 'BODY') {
- Ext.EventManager.removeAll(node);
- node.parentNode.removeChild(node);
- delete Ext.cache[node.id];
- }
- },
-
- /**
- * @private
- * Creates meta tags for a given config object. This is usually just called internally from Ext.setup - see
- * that method for full usage. Extracted into its own function so that Ext.Application and other classes can
- * call it without invoking all of the logic inside Ext.setup.
- * @param {Object} config The meta tag configuration object
- */
- addMetaTags: function(config) {
- if (!Ext.isObject(config)) {
- return;
- }
-
- var head = Ext.get(document.getElementsByTagName('head')[0]),
- tag, precomposed;
-
- /*
- * The viewport meta tag. This disables user scaling. This is supported
- * on most Android phones and all iOS devices and will probably be supported
- * on most future devices (like Blackberry, Palm etc).
- */
- if (!Ext.is.Desktop) {
- tag = Ext.get(document.createElement('meta'));
- tag.set({
- name: 'viewport',
- content: 'width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0;'
- });
- head.appendChild(tag);
- }
-
- /*
- * We want to check now for iOS specific meta tags. Unfortunately most
- * of these are not supported on devices other then iOS.
- */
- if (Ext.is.iOS) {
- /*
- * On iOS, if you save to home screen, you can decide if you want
- * to launch the app fullscreen (without address bar). You can also
- * change the styling of the status bar at the top of the screen.
- */
- if (config.fullscreen !== false) {
- tag = Ext.get(document.createElement('meta'));
- tag.set({
- name: 'apple-mobile-web-app-capable',
- content: 'yes'
- });
- head.appendChild(tag);
-
- if (Ext.isString(config.statusBarStyle)) {
- tag = Ext.get(document.createElement('meta'));
- tag.set({
- name: 'apple-mobile-web-app-status-bar-style',
- content: config.statusBarStyle
- });
- head.appendChild(tag);
- }
- }
-
- /*
- * iOS allows you to specify a startup screen. This is displayed during
- * the startup of your app if you save to your homescreen. Since we could
- * be dealing with an iPad or iPhone/iPod, we have a tablet startup screen
- * and a phone startup screen.
- */
- if (config.tabletStartupScreen && Ext.is.iPad) {
- tag = Ext.get(document.createElement('link'));
- tag.set({
- rel: 'apple-touch-startup-image',
- href: config.tabletStartupScreen
- });
- head.appendChild(tag);
- }
-
- if (config.phoneStartupScreen && !Ext.is.iPad) {
- tag = Ext.get(document.createElement('link'));
- tag.set({
- rel: 'apple-touch-startup-image',
- href: config.phoneStartupScreen
- });
- head.appendChild(tag);
- }
-
- /*
- * On iOS you can specify the icon used when you save the app to your
- * homescreen. You can set an icon that will be used for both iPads
- * and iPhone/iPod, or you can specify specific icons for both.
- */
- if (config.icon) {
- config.phoneIcon = config.tabletIcon = config.icon;
- }
-
- precomposed = (config.glossOnIcon === false) ? '-precomposed' : '';
- if (Ext.is.iPad && Ext.isString(config.tabletIcon)) {
- tag = Ext.get(document.createElement('link'));
- tag.set({
- rel: 'apple-touch-icon' + precomposed,
- href: config.tabletIcon
- });
- head.appendChild(tag);
- }
- else if (!Ext.is.iPad && Ext.isString(config.phoneIcon)) {
- tag = Ext.get(document.createElement('link'));
- tag.set({
- rel: 'apple-touch-icon' + precomposed,
- href: config.phoneIcon
- });
- head.appendChild(tag);
- }
- }
- }
-});
-
-
-//Initialize doc classes and feature detections
-(function() {
- var initExt = function() {
- // find the body element
- var bd = Ext.getBody(),
- cls = [];
- if (!bd) {
- return false;
- }
- var Is = Ext.is;
- if (Is.Phone) {
- cls.push('x-phone');
- }
- else if (Is.Tablet) {
- cls.push('x-tablet');
- }
- else if (Is.Desktop) {
- cls.push('x-desktop');
- }
- if (Is.iPad) {
- cls.push('x-ipad');
- }
- if (Is.iOS) {
- cls.push('x-ios');
- }
- if (Is.Android) {
- cls.push('x-android');
- }
- if (Is.Blackberry) {
- cls.push('x-bb');
- }
- if (Is.Standalone) {
- cls.push('x-standalone');
- }
- if (cls.length) {
- bd.addCls(cls);
- }
- return true;
- };
-
- if (!initExt()) {
- Ext.onReady(initExt);
- }
-})();
-
-/**
- * @class Ext.Viewport
- * @singleton
- * @ignore
- * @private
- *
- * Handles viewport sizing for the whole application
- */
-
-Ext.Viewport = new (Ext.extend(Ext.util.Observable, {
- constructor: function() {
- var me = this;
-
- this.addEvents(
- 'orientationchange',
- 'resize'
- );
-
- this.stretchSizes = {};
-
- if (Ext.supports.OrientationChange) {
- window.addEventListener('orientationchange', Ext.createDelegate(me.onOrientationChange, me), false);
- }
- else {
- window.addEventListener('resize', Ext.createDelegate(me.onResize, me), false);
- }
-
- if (!Ext.desktop) {
- document.addEventListener('touchstart', Ext.createDelegate(me.onTouchStartCapturing, me), true);
- }
- },
-
- init: function(fn, scope) {
- var me = this,
- stretchSize = Math.max(window.innerHeight, window.innerWidth) * 2,
- body = Ext.getBody();
-
- me.updateOrientation();
-
- this.initialHeight = window.innerHeight;
- this.initialOrientation = this.orientation;
-
- body.setHeight(stretchSize);
-
- Ext.gesture.Manager.freeze();
-
- this.scrollToTop();
- // These 2 timers here are ugly but it's the only way to
- // make address bar hiding works on all the devices we have
- // including the new Galaxy Tab
- setTimeout(function() {
- me.scrollToTop();
- setTimeout(function() {
- me.scrollToTop();
- me.initialHeight = Math.max(me.initialHeight, window.innerHeight);
-
- if (fn) {
- fn.apply(scope || window);
- }
-
- me.updateBodySize();
-
- Ext.gesture.Manager.thaw();
- }, 500);
- }, 500);
-
- },
-
- scrollToTop: function() {
- if (Ext.is.iOS) {
- document.body.scrollTop = document.body.scrollHeight;
- }
- else {
- window.scrollTo(0, 1);
- }
- },
-
- updateBodySize: function() {
- Ext.getBody().setSize(window.innerWidth, window.innerHeight);
- },
-
- updateOrientation: function() {
- this.lastSize = this.getSize();
- this.orientation = this.getOrientation();
- },
-
- onTouchStartCapturing: function(e) {
- if (!Ext.currentlyFocusedField && Ext.is.iOS) {
- this.scrollToTop();
- }
- },
-
- onOrientationChange: function() {
- var me = this,
- body = Ext.getBody();
-
- Ext.gesture.Manager.freeze();
-
- body.setHeight(body.getWidth());
-
- this.updateOrientation();
-
- this.fireEvent('orientationchange', this, this.orientation);
-
- setTimeout(function() {
- me.scrollToTop();
- setTimeout(function() {
- me.updateBodySize();
- me.fireResizeEvent();
-
- Ext.gesture.Manager.thaw();
- }, 200);
- }, 200);
- },
-
- fireResizeEvent: function() {
- var me = this;
-
- if (!Ext.is.iOS) {
- if (this.resizeEventTimer) {
- clearTimeout(this.resizeEventTimer);
- }
-
- this.resizeEventTimer = setTimeout(function() {
- me.fireEvent('resize', me, me.getSize());
- }, 500);
- } else {
- me.fireEvent('resize', me, me.getSize());
- }
- },
-
- onResize: function() {
- if (this.orientation != this.getOrientation()) {
- this.onOrientationChange();
- } else {
- var size = this.getSize();
-
- if (!Ext.is.iOS && !Ext.is.Desktop) {
- if ((size.width == this.lastSize.width && size.height > this.lastSize.height) ||
- (size.height == this.lastSize.height && size.width > this.lastSize.width)) {
- this.fireEvent('resize', this, size);
- }
- } else {
- this.fireEvent('resize', this, size);
- }
- }
- },
-
- getSize: function() {
- var size = {
- width: window.innerWidth,
- height: window.innerHeight
- };
-
- if (!Ext.is.Desktop) {
- size.height = (this.orientation == this.initialOrientation) ?
- Math.max(this.initialHeight, size.height) :
- size.height
- }
-
- return size;
- },
-
- getOffset: function() {
- return {
- x: window.pageXOffset,
- y: window.pageYOffset
- };
- },
-
- getOrientation: function() {
- var size = this.getSize();
-
- if (window.hasOwnProperty('orientation')) {
- return (window.orientation == 0 || window.orientation == 180) ? 'portrait' : 'landscape';
- }
- else {
- if (!Ext.is.iOS && !Ext.is.Desktop) {
- if ((size.width == this.lastSize.width && size.height < this.lastSize.height) ||
- (size.height == this.lastSize.height && size.width < this.lastSize.width)) {
- return this.orientation;
- }
- }
-
- return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
- }
-
- }
-
-}));
-/**
- * @class Ext.util.TapRepeater
- * @extends Ext.util.Observable
- *
- * A wrapper class which can be applied to any element. Fires a "tap" event while
- * touching the device. The interval between firings may be specified in the config but
- * defaults to 20 milliseconds.
- *
- * @constructor
- * @param {Mixed} el The element to listen on
- * @param {Object} config
- */
-Ext.util.TapRepeater = Ext.extend(Ext.util.Observable, {
-
- constructor: function(el, config) {
- this.el = Ext.get(el);
-
- Ext.apply(this, config);
-
- this.addEvents(
- /**
- * @event touchstart
- * Fires when the touch is started.
- * @param {Ext.util.TapRepeater} this
- * @param {Ext.EventObject} e
- */
- "touchstart",
- /**
- * @event tap
- * Fires on a specified interval during the time the element is pressed.
- * @param {Ext.util.TapRepeater} this
- * @param {Ext.EventObject} e
- */
- "tap",
- /**
- * @event touchend
- * Fires when the touch is ended.
- * @param {Ext.util.TapRepeater} this
- * @param {Ext.EventObject} e
- */
- "touchend"
- );
-
- this.el.on({
- touchstart: this.onTouchStart,
- touchend: this.onTouchEnd,
- scope: this
- });
-
- if (this.preventDefault || this.stopDefault) {
- this.el.on('tap', this.eventOptions, this);
- }
-
- Ext.util.TapRepeater.superclass.constructor.call(this);
- },
-
- interval: 10,
- delay: 250,
- preventDefault: true,
- stopDefault: false,
- timer: 0,
-
- // @private
- eventOptions: function(e) {
- if (this.preventDefault) {
- e.preventDefault();
- }
- if (this.stopDefault) {
- e.stopEvent();
- }
- },
-
- // @private
- destroy: function() {
- Ext.destroy(this.el);
- this.clearListeners();
- },
-
- // @private
- onTouchStart: function(e) {
- clearTimeout(this.timer);
- if (this.pressClass) {
- this.el.addCls(this.pressClass);
- }
- this.tapStartTime = new Date();
-
- this.fireEvent("touchstart", this, e);
- this.fireEvent("tap", this, e);
-
- // Do not honor delay or interval if acceleration wanted.
- if (this.accelerate) {
- this.delay = 400;
- }
- this.timer = Ext.defer(this.tap, this.delay || this.interval, this, [e]);
- },
-
- // @private
- tap: function(e) {
- this.fireEvent("tap", this, e);
- this.timer = Ext.defer(this.tap, this.accelerate ? this.easeOutExpo(Ext.util.Date.getElapsed(this.tapStartTime),
- 400,
- -390,
- 12000) : this.interval, this, [e]);
- },
-
- // Easing calculation
- // @private
- easeOutExpo: function(t, b, c, d) {
- return (t == d) ? b + c : c * ( - Math.pow(2, -10 * t / d) + 1) + b;
- },
-
- // @private
- onTouchEnd: function(e) {
- clearTimeout(this.timer);
- this.el.removeCls(this.pressClass);
- this.fireEvent("touchend", this, e);
- }
-});
-
-/*
- http://www.JSON.org/json2.js
- 2010-03-20
-
- Public Domain.
-
- NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
-
- See http://www.JSON.org/js.html
-
-
- This code should be minified before deployment.
- See http://javascript.crockford.com/jsmin.html
-
- USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
- NOT CONTROL.
-
-
- This file creates a global JSON object containing two methods: stringify
- and parse.
-
- JSON.stringify(value, replacer, space)
- value any JavaScript value, usually an object or array.
-
- replacer an optional parameter that determines how object
- values are stringified for objects. It can be a
- function or an array of strings.
-
- space an optional parameter that specifies the indentation
- of nested structures. If it is omitted, the text will
- be packed without extra whitespace. If it is a number,
- it will specify the number of spaces to indent at each
- level. If it is a string (such as '\t' or ' '),
- it contains the characters used to indent at each level.
-
- This method produces a JSON text from a JavaScript value.
-
- When an object value is found, if the object contains a toJSON
- method, its toJSON method will be called and the result will be
- stringified. A toJSON method does not serialize: it returns the
- value represented by the name/value pair that should be serialized,
- or undefined if nothing should be serialized. The toJSON method
- will be passed the key associated with the value, and this will be
- bound to the value
-
- For example, this would serialize Dates as ISO strings.
-
- Date.prototype.toJSON = function (key) {
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
-
- return this.getUTCFullYear() + '-' +
- f(this.getUTCMonth() + 1) + '-' +
- f(this.getUTCDate()) + 'T' +
- f(this.getUTCHours()) + ':' +
- f(this.getUTCMinutes()) + ':' +
- f(this.getUTCSeconds()) + 'Z';
- };
-
- You can provide an optional replacer method. It will be passed the
- key and value of each member, with this bound to the containing
- object. The value that is returned from your method will be
- serialized. If your method returns undefined, then the member will
- be excluded from the serialization.
-
- If the replacer parameter is an array of strings, then it will be
- used to select the members to be serialized. It filters the results
- such that only members with keys listed in the replacer array are
- stringified.
-
- Values that do not have JSON representations, such as undefined or
- functions, will not be serialized. Such values in objects will be
- dropped; in arrays they will be replaced with null. You can use
- a replacer function to replace those with JSON values.
- JSON.stringify(undefined) returns undefined.
-
- The optional space parameter produces a stringification of the
- value that is filled with line breaks and indentation to make it
- easier to read.
-
- If the space parameter is a non-empty string, then that string will
- be used for indentation. If the space parameter is a number, then
- the indentation will be that many spaces.
-
- Example:
-
- text = JSON.stringify(['e', {pluribus: 'unum'}]);
- // text is '["e",{"pluribus":"unum"}]'
-
-
- text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
- // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
-
- text = JSON.stringify([new Date()], function (key, value) {
- return this[key] instanceof Date ?
- 'Date(' + this[key] + ')' : value;
- });
- // text is '["Date(---current time---)"]'
-
-
- JSON.parse(text, reviver)
- This method parses a JSON text to produce an object or array.
- It can throw a SyntaxError exception.
-
- The optional reviver parameter is a function that can filter and
- transform the results. It receives each of the keys and values,
- and its return value is used instead of the original value.
- If it returns what it received, then the structure is not modified.
- If it returns undefined then the member is deleted.
-
- Example:
-
- // Parse the text. Values that look like ISO date strings will
- // be converted to Date objects.
-
- myData = JSON.parse(text, function (key, value) {
- var a;
- if (typeof value === 'string') {
- a =
-/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
- if (a) {
- return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
- +a[5], +a[6]));
- }
- }
- return value;
- });
-
- myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
- var d;
- if (typeof value === 'string' &&
- value.slice(0, 5) === 'Date(' &&
- value.slice(-1) === ')') {
- d = new Date(value.slice(5, -1));
- if (d) {
- return d;
- }
- }
- return value;
- });
-
-
- This is a reference implementation. You are free to copy, modify, or
- redistribute.
-*/
-
-/*jslint evil: true, strict: false */
-
-/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
- call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
- getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
- lastIndex, length, parse, prototype, push, replace, slice, stringify,
- test, toJSON, toString, valueOf
-*/
-
-
-// Create a JSON object only if one does not already exist. We create the
-// methods in a closure to avoid creating global variables.
-
-if (!this.JSON) {
- this.JSON = {};
-}
-
-(function () {
-
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
-
- if (typeof Date.prototype.toJSON !== 'function') {
-
- Date.prototype.toJSON = function (key) {
-
- return isFinite(this.valueOf()) ?
- this.getUTCFullYear() + '-' +
- f(this.getUTCMonth() + 1) + '-' +
- f(this.getUTCDate()) + 'T' +
- f(this.getUTCHours()) + ':' +
- f(this.getUTCMinutes()) + ':' +
- f(this.getUTCSeconds()) + 'Z' : null;
- };
-
- String.prototype.toJSON =
- Number.prototype.toJSON =
- Boolean.prototype.toJSON = function (key) {
- return this.valueOf();
- };
- }
-
- var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- gap,
- indent,
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- },
- rep;
-
-
- function quote(string) {
-
-// If the string contains no control characters, no quote characters, and no
-// backslash characters, then we can safely slap some quotes around it.
-// Otherwise we must also replace the offending characters with safe escape
-// sequences.
-
- escapable.lastIndex = 0;
- return escapable.test(string) ?
- '"' + string.replace(escapable, function (a) {
- var c = meta[a];
- return typeof c === 'string' ? c :
- '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- }) + '"' :
- '"' + string + '"';
- }
-
-
- function str(key, holder) {
-
-// Produce a string from holder[key].
-
- var i, // The loop counter.
- k, // The member key.
- v, // The member value.
- length,
- mind = gap,
- partial,
- value = holder[key];
-
-// If the value has a toJSON method, call it to obtain a replacement value.
-
- if (value && typeof value === 'object' &&
- typeof value.toJSON === 'function') {
- value = value.toJSON(key);
- }
-
-// If we were called with a replacer function, then call the replacer to
-// obtain a replacement value.
-
- if (typeof rep === 'function') {
- value = rep.call(holder, key, value);
- }
-
-// What happens next depends on the value's type.
-
- switch (typeof value) {
- case 'string':
- return quote(value);
-
- case 'number':
-
-// JSON numbers must be finite. Encode non-finite numbers as null.
-
- return isFinite(value) ? String(value) : 'null';
-
- case 'boolean':
- case 'null':
-
-// If the value is a boolean or null, convert it to a string. Note:
-// typeof null does not produce 'null'. The case is included here in
-// the remote chance that this gets fixed someday.
-
- return String(value);
-
-// If the type is 'object', we might be dealing with an object or an array or
-// null.
-
- case 'object':
-
-// Due to a specification blunder in ECMAScript, typeof null is 'object',
-// so watch out for that case.
-
- if (!value) {
- return 'null';
- }
-
-// Make an array to hold the partial results of stringifying this object value.
-
- gap += indent;
- partial = [];
-
-// Is the value an array?
-
- if (Object.prototype.toString.apply(value) === '[object Array]') {
-
-// The value is an array. Stringify every element. Use null as a placeholder
-// for non-JSON values.
-
- length = value.length;
- for (i = 0; i < length; i += 1) {
- partial[i] = str(i, value) || 'null';
- }
-
-// Join all of the elements together, separated with commas, and wrap them in
-// brackets.
-
- v = partial.length === 0 ? '[]' :
- gap ? '[\n' + gap +
- partial.join(',\n' + gap) + '\n' +
- mind + ']' :
- '[' + partial.join(',') + ']';
- gap = mind;
- return v;
- }
-
-// If the replacer is an array, use it to select the members to be stringified.
-
- if (rep && typeof rep === 'object') {
- length = rep.length;
- for (i = 0; i < length; i += 1) {
- k = rep[i];
- if (typeof k === 'string') {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- } else {
-
-// Otherwise, iterate through all of the keys in the object.
-
- for (k in value) {
- if (Object.hasOwnProperty.call(value, k)) {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- }
-
-// Join all of the member texts together, separated with commas,
-// and wrap them in braces.
-
- v = partial.length === 0 ? '{}' :
- gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
- mind + '}' : '{' + partial.join(',') + '}';
- gap = mind;
- return v;
- }
- return v;
- }
-
-// If the JSON object does not yet have a stringify method, give it one.
-
- if (typeof JSON.stringify !== 'function') {
- JSON.stringify = function (value, replacer, space) {
-
-// The stringify method takes a value and an optional replacer, and an optional
-// space parameter, and returns a JSON text. The replacer can be a function
-// that can replace values, or an array of strings that will select the keys.
-// A default replacer method can be provided. Use of the space parameter can
-// produce text that is more easily readable.
-
- var i;
- gap = '';
- indent = '';
-
-// If the space parameter is a number, make an indent string containing that
-// many spaces.
-
- if (typeof space === 'number') {
- for (i = 0; i < space; i += 1) {
- indent += ' ';
- }
-
-// If the space parameter is a string, it will be used as the indent string.
-
- } else if (typeof space === 'string') {
- indent = space;
- }
-
-// If there is a replacer, it must be a function or an array.
-// Otherwise, throw an error.
-
- rep = replacer;
- if (replacer && typeof replacer !== 'function' &&
- (typeof replacer !== 'object' ||
- typeof replacer.length !== 'number')) {
- throw new Error('JSON.stringify');
- }
-
-// Make a fake root object containing our value under the key of ''.
-// Return the result of stringifying the value.
-
- return str('', {'': value});
- };
- }
-
-
-// If the JSON object does not yet have a parse method, give it one.
-
- if (typeof JSON.parse !== 'function') {
- JSON.parse = function (text, reviver) {
-
-// The parse method takes a text and an optional reviver function, and returns
-// a JavaScript value if the text is a valid JSON text.
-
- var j;
-
- function walk(holder, key) {
-
-// The walk method is used to recursively walk the resulting structure so
-// that modifications can be made.
-
- var k, v, value = holder[key];
- if (value && typeof value === 'object') {
- for (k in value) {
- if (Object.hasOwnProperty.call(value, k)) {
- v = walk(value, k);
- if (v !== undefined) {
- value[k] = v;
- } else {
- delete value[k];
- }
- }
- }
- }
- return reviver.call(holder, key, value);
- }
-
-
-// Parsing happens in four stages. In the first stage, we replace certain
-// Unicode characters with escape sequences. JavaScript handles many characters
-// incorrectly, either silently deleting them, or treating them as line endings.
-
- text = String(text);
- cx.lastIndex = 0;
- if (cx.test(text)) {
- text = text.replace(cx, function (a) {
- return '\\u' +
- ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- });
- }
-
-// In the second stage, we run the text against regular expressions that look
-// for non-JSON patterns. We are especially concerned with '()' and 'new'
-// because they can cause invocation, and '=' because it can cause mutation.
-// But just to be safe, we want to reject all unexpected forms.
-
-// We split the second stage into 4 regexp operations in order to work around
-// crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
-// replace all simple value tokens with ']' characters. Third, we delete all
-// open brackets that follow a colon or comma or that begin the text. Finally,
-// we look to see that the remaining characters are only whitespace or ']' or
-// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
-
- if (/^[\],:{}\s]*$/.
-test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
-replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
-replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
-// In the third stage we use the eval function to compile the text into a
-// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
-// in JavaScript: it can begin a block or an object literal. We wrap the text
-// in parens to eliminate the ambiguity.
-
- j = eval('(' + text + ')');
-
-// In the optional fourth stage, we recursively walk the new structure, passing
-// each name/value pair to a reviver function for possible transformation.
-
- return typeof reviver === 'function' ?
- walk({'': j}, '') : j;
- }
-
-// If the text is not JSON parseable, then a SyntaxError is thrown.
-
- throw new SyntaxError('JSON.parse');
- };
- }
-}());
-
-/**
- * @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.util.JSON = {
- encode : function(o) {
- return JSON.stringify(o);
- },
-
- decode : function(s) {
- return JSON.parse(s);
- }
-};
-
-/**
- * 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.JSONP
- *
- * Provides functionality to make cross-domain requests with JSONP (JSON with Padding).
- * http://en.wikipedia.org/wiki/JSON#JSONP
- * <p>
- * <b>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
- * of the running page, you must use this class, because of the same origin policy.</b><br>
- * <p>
- * The content passed back from a server resource requested by a JSONP request<b>must</b> be executable JavaScript
- * source code because it is used as the source inside a <script> tag.<br>
- * <p>
- * In order for the browser to process the returned data, the server must wrap the data object
- * with a call to a callback function, the name of which is passed as a parameter callbackKey
- * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
- * depending on whether the callback name was passed:
- * <p>
- * <pre><code>
-boolean scriptTag = false;
-String cb = request.getParameter("callback");
-if (cb != null) {
- scriptTag = true;
- response.setContentType("text/javascript");
-} else {
- response.setContentType("application/x-json");
-}
-Writer out = response.getWriter();
-if (scriptTag) {
- out.write(cb + "(");
-}
-out.print(dataBlock.toJsonString());
-if (scriptTag) {
- out.write(");");
-}
-</code></pre>
- * <p>Below is a PHP example to do the same thing:</p><pre><code>
-$callback = $_REQUEST['callback'];
-
-// Create the output object.
-$output = array('a' => 'Apple', 'b' => 'Banana');
-
-//start output
-if ($callback) {
- header('Content-Type: text/javascript');
- echo $callback . '(' . json_encode($output) . ');';
-} else {
- header('Content-Type: application/x-json');
- echo json_encode($output);
-}
-</code></pre>
- * <p>Below is the ASP.Net code to do the same thing:</p><pre><code>
-String jsonString = "{success: true}";
-String cb = Request.Params.Get("callback");
-String responseString = "";
-if (!String.IsNullOrEmpty(cb)) {
- responseString = cb + "(" + jsonString + ")";
-} else {
- responseString = jsonString;
-}
-Response.Write(responseString);
-</code></pre>
- * @singleton
- */
-Ext.util.JSONP = {
-
- /**
- * Read-only queue
- * @type Array
- */
- queue: [],
-
- /**
- * Read-only current executing request
- * @type Object
- */
- current: null,
-
- /**
- * Make a cross-domain request using JSONP.
- * @param {Object} config
- * Valid configurations are:
- * <ul>
- * <li>url - {String} - Url to request data from. (required) </li>
- * <li>params - {Object} - A set of key/value pairs to be url encoded and passed as GET parameters in the request.</li>
- * <li>callbackKey - {String} - Key specified by the server-side provider.</li>
- * <li>callback - {Function} - Will be passed a single argument of the result of the request.</li>
- * <li>scope - {Scope} - Scope to execute your callback in.</li>
- * </ul>
- */
- request : function(o) {
- o = o || {};
- if (!o.url) {
- return;
- }
-
- var me = this;
- o.params = o.params || {};
- if (o.callbackKey) {
- o.params[o.callbackKey] = 'Ext.util.JSONP.callback';
- }
- var params = Ext.urlEncode(o.params);
-
- var script = document.createElement('script');
- script.type = 'text/javascript';
-
- this.queue.push({
- url: o.url,
- script: script,
- callback: o.callback || function(){},
- scope: o.scope || window,
- params: params || null
- });
-
- if (!this.current) {
- this.next();
- }
- },
-
- // private
- next : function() {
- this.current = null;
- if (this.queue.length) {
- this.current = this.queue.shift();
- this.current.script.src = this.current.url + (this.current.params ? ('?' + this.current.params) : '');
- document.getElementsByTagName('head')[0].appendChild(this.current.script);
- }
- },
-
- // @private
- callback: function(json) {
- this.current.callback.call(this.current.scope, json);
- document.getElementsByTagName('head')[0].removeChild(this.current.script);
- this.next();
- }
-};
-
-/**
- * @class Ext.util.Draggable
- * @extends Ext.util.Observable
- * A core util class to bring Draggable behavior to any DOM element, acts as a base class for Scroller and Sortable
- * @constructor
- * @param {Mixed} el The element you want to make draggable.
- * @param {Object} config The configuration object for this Draggable.
- */
-Ext.util.Draggable = Ext.extend(Ext.util.Observable, {
- baseCls: 'x-draggable',
-
- draggingCls: 'x-dragging',
-
- proxyCls: 'x-draggable-proxy',
-
- // @private
- outOfBoundRestrictFactor: 1,
-
- /**
- * @cfg {String} direction
- * Possible values: 'vertical', 'horizontal', or 'both'
- * Defaults to 'both'
- */
- direction: 'both',
-
- /**
- * @cfg {Element/Mixed} constrain Can be either a DOM element, an instance of Ext.Element, 'parent' or null for no constrain
- */
- constrain: window,
-
- /**
- * The amount of pixels you have to move before the drag operation starts.
- * Defaults to 0
- * @type Number
- */
- threshold: 0,
-
- /**
- * @cfg {Number} delay
- * How many milliseconds a user must hold the draggable before starting a
- * drag operation. Defaults to 0 or immediate.
- */
- delay: 0,
-
- /**
- * @cfg {String} cancelSelector
- * A simple CSS selector that represents elements within the draggable
- * that should NOT initiate a drag.
- */
- cancelSelector: null,
-
- /**
- * @cfg {Boolean} disabled
- * Whether or not the draggable behavior is disabled on instantiation
- * Defaults to false
- */
- disabled: false,
-
- /**
- * @cfg {Boolean} revert
- * Whether or not the element or it's proxy will be reverted back (with animation)
- * when it's not dropped and held by a Droppable
- */
- revert: false,
-
- /**
- * @cfg {String} group
- * Draggable and Droppable objects can participate in a group which are
- * capable of interacting. Defaults to 'base'
- */
- group: 'base',
-
- /**
- * @cfg {Ext.Element/Element/String} eventTarget
- * The element to actually bind touch events to, the only string accepted is 'parent'
- * for convenience.
- * Defaults to this class' element itself
- */
-
- /**
- * @cfg {Boolean} useCssTransform
- * Whether or not to translate the element using CSS Transform (much faster) instead of
- * left and top properties, defaults to true
- */
- useCssTransform: true,
-
- // not implemented yet.
- grid: null,
- snap: null,
- proxy: null,
- stack: false,
-
- /**
- * How long animations for this draggable take by default when using setOffset with animate being true.
- * Defaults to 300.
- * @type Number
- */
- animationDuration: 300,
-
- /**
- * Whether or not to automatically re-calculate the Scroller's and its container's size on every
- * touchstart.
- * Defaults to false
- * @type Boolean
- */
- updateBoundaryOnTouchStart: true,
-
- // Properties
- /**
- * Read-only Property representing the region that the Draggable
- * is constrained to.
- * @type Ext.util.Region
- */
- offsetBoundary: null,
-
- /**
- * Read-only Property representing whether or not the Draggable is currently
- * dragging or not.
- * @type Boolean
- */
- dragging: false,
-
- /**
- * Read-only value representing whether the Draggable can be moved vertically.
- * This is automatically calculated by Draggable by the direction configuration.
- * @type Boolean
- */
- vertical: false,
-
- /**
- * Read-only value representing whether the Draggable can be moved horizontally.
- * This is automatically calculated by Draggable by the direction configuration.
- * @type Boolean
- */
- horizontal: false,
-
- // @private
- monitorOrientation: true,
-
- constructor: function(el, config) {
- this.el = Ext.get(el);
- this.id = el.id;
-
- config = config || {};
-
- Ext.apply(this, config);
-
- this.addEvents(
- /**
- * @event offsetchange
- * @param {Ext.Draggable} this
- * @param {Ext.util.Offset} offset
- */
- 'offsetchange'
- );
-
- Ext.util.Draggable.superclass.constructor.call(this, config);
-
- if (this.eventTarget === 'parent') {
- this.eventTarget = this.el.parent();
- } else {
- this.eventTarget = (this.eventTarget) ? Ext.get(this.eventTarget) : this.el;
- }
-
- if (this.direction == 'both') {
- this.horizontal = true;
- this.vertical = true;
- }
- else if (this.direction == 'horizontal') {
- this.horizontal = true;
- }
- else {
- this.vertical = true;
- }
-
- this.el.addCls(this.baseCls);
-
- if (this.proxy) {
- this.getProxyEl().addCls(this.proxyCls);
- }
-
- this.startEventName = (this.delay > 0) ? 'taphold' : 'dragstart';
- this.dragOptions = (this.delay > 0) ? {holdThreshold: this.delay} : {
- direction: this.direction,
- dragThreshold: this.threshold
- };
-
- this.container = window;
-
- if (this.constrain) {
- if (this.constrain === 'parent') {
- this.container = this.el.parent();
- }
- else if (this.constrain !== window) {
- this.container = Ext.get(this.constrain);
- }
- }
-
- this.offset = new Ext.util.Offset();
-
- this.linearAnimation = {
- x: new Ext.util.Draggable.Animation.Linear(),
- y: new Ext.util.Draggable.Animation.Linear()
- };
-
- this.updateBoundary(true);
- this.setDragging(false);
-
- if (!this.disabled) {
- this.enable();
- }
-
- return this;
- },
-
- /**
- * Enable the Draggable.
- * @return {Ext.util.Draggable} this This Draggable instance
- */
- enable: function() {
- return this.setEnabled(true);
- },
-
- /**
- * Disable the Draggable.
- * @return {Ext.util.Draggable} this This Draggable instance
- */
- disable: function() {
- return this.setEnabled(false);
- },
-
- /**
- * Combined method to disable or enable the Draggable. This method is called by the enable and
- * disable methods.
- * @param {Boolean} enabled True to enable, false to disable. Defaults to false.
- * @return {Ext.util.Draggable} this This Draggable instance
- */
- setEnabled: function(enabled) {
- this.eventTarget[enabled ? 'on' : 'un'](this.startEventName, this.onStart, this, this.dragOptions);
- this.eventTarget[enabled ? 'on' : 'un']('drag', this.onDrag, this, this.dragOptions);
- this.eventTarget[enabled ? 'on' : 'un']('dragend', this.onDragEnd, this, this.dragOptions);
- this.eventTarget[enabled ? 'on' : 'un']('touchstart', this.onTouchStart, this);
-
- if (enabled) {
- Ext.EventManager.onOrientationChange(this.onOrientationChange, this);
- } else {
- Ext.EventManager.orientationEvent.removeListener(this.onOrientationChange, this);
- }
-
- this.disabled = !enabled;
-
- return this;
- },
-
- /**
- * Change the Draggable to use css transforms instead of style offsets
- * or the other way around.
- * @param {Boolean} useCssTransform True to use css transforms instead of style offsets.
- * @return {Ext.util.Draggable} this This Draggable instance
- * @public
- */
- setUseCssTransform: function(useCssTransform) {
- if (typeof useCssTransform == 'undefined') {
- useCssTransform = true;
- }
-
- if (useCssTransform != this.useCssTransform) {
- this.useCssTransform = useCssTransform;
-
- var resetOffset = new Ext.util.Offset();
-
- if (useCssTransform == false) {
- this.setStyleOffset(this.offset);
- this.setTransformOffset(resetOffset, true);
- } else {
- this.setTransformOffset(this.offset);
- this.setStyleOffset(resetOffset);
- }
- }
-
- return this;
- },
-
- /**
- * Sets the offset of this Draggable relatively to its original offset.
- * @param {Ext.util.Offset/Object} offset An object or Ext.util.Offset instance containing the
- * x and y coordinates.
- * @param {Boolean/Number} animate Whether or not to animate the setting of the offset. True
- * to use the default animationDuration, a number to specify the duration for this operation.
- * @return {Ext.util.Draggable} this This Draggable instance
- */
- setOffset: function(offset, animate) {
- if (!this.horizontal) {
- offset.x = 0;
- }
-
- if (!this.vertical) {
- offset.y = 0;
- }
-
- if (!(offset instanceof Ext.util.Offset)) {
- offset = Ext.util.Offset.fromObject(offset);
- }
-
- offset.round();
-
- if (!this.offset.equals(offset)) {
- if (animate) {
- this.startAnimation(offset, animate);
- }
- else {
- this.offset = offset;
- this.region = new Ext.util.Region(
- this.initialRegion.top + offset.y,
- this.initialRegion.right + offset.x,
- this.initialRegion.bottom + offset.y,
- this.initialRegion.left + offset.x
- );
-
- if (this.useCssTransform) {
- this.setTransformOffset(offset);
- }
- else {
- this.setStyleOffset(offset);
- }
-
- this.fireEvent('offsetchange', this, this.offset);
- }
- }
-
-
- return this;
- },
-
- /**
- * Internal method that sets the transform of the proxyEl.
- * @param {Ext.util.Offset/Object} offset An object or Ext.util.Offset instance containing the
- * x and y coordinates for the transform.
- * @return {Ext.util.Draggable} this This Draggable instance
- * @private
- */
- setTransformOffset: function(offset, clean) {
-// Ext.Element.cssTransform(this.getProxyEl(), {translate: [offset.x, offset.y]});
- // Temporarily use this instead of Ext.Element.cssTransform to save some CPU
- if (clean) {
- this.getProxyEl().dom.style.webkitTransform = '';
- } else {
- Ext.Element.cssTranslate(this.getProxyEl(), offset);
- }
-
- return this;
- },
-
- /**
- * Internal method that sets the left and top of the proxyEl.
- * @param {Ext.util.Offset/Object} offset An object or Ext.util.Offset instance containing the
- * x and y coordinates.
- * @return {Ext.util.Draggable} this This Draggable instance
- * @private
- */
- setStyleOffset: function(offset) {
- this.getProxyEl().setLeft(offset.x);
- this.getProxyEl().setTop(offset.y);
- return this;
- },
-
- /**
- * Internal method that sets the offset of the Draggable using an animation
- * @param {Ext.util.Offset/Object} offset An object or Ext.util.Offset instance containing the
- * x and y coordinates for the transform.
- * @param {Boolean/Number} animate Whether or not to animate the setting of the offset. True
- * to use the default animationDuration, a number to specify the duration for this operation.
- * @return {Ext.util.Draggable} this This Draggable instance
- * @private
- */
- startAnimation: function(offset, animate) {
- this.stopAnimation();
-
- var currentTime = Date.now();
- animate = Ext.isNumber(animate) ? animate : this.animationDuration;
-
- this.linearAnimation.x.set({
- startOffset: this.offset.x,
- endOffset: offset.x,
- startTime: currentTime,
- duration: animate
- });
-
- this.linearAnimation.y.set({
- startOffset: this.offset.y,
- endOffset: offset.y,
- startTime: currentTime,
- duration: animate
- });
-
- this.isAnimating = true;
- this.animationTimer = Ext.defer(this.handleAnimationFrame, 0, this);
- return this;
- },
-
- /**
- * Internal method that stops the current offset animation
- * @private
- */
- stopAnimation: function() {
- if (this.isAnimating) {
- clearTimeout(this.animationTimer);
- this.isAnimating = false;
- this.setDragging(false);
- }
-
- return this;
- },
-
- /**
- * Internal method that handles a frame of the offset animation.
- * @private
- */
- handleAnimationFrame: function() {
- if (!this.isAnimating) {
- return;
- }
-
- var newOffset = new Ext.util.Offset();
- newOffset.x = this.linearAnimation.x.getOffset();
- newOffset.y = this.linearAnimation.y.getOffset();
-
- this.setOffset(newOffset);
-
- this.animationTimer = Ext.defer(this.handleAnimationFrame, 10, this);
-
- if ((newOffset.x === this.linearAnimation.x.endOffset) && (newOffset.y === this.linearAnimation.y.endOffset)) {
- this.stopAnimation();
- }
- },
-
- /**
- * Returns the current offset relative to the original location of this Draggable.
- * @return {Ext.util.Offset} offset An Ext.util.Offset instance containing the offset.
- */
- getOffset: function() {
- var offset = this.offset.copy();
- offset.y = -offset.y;
- offset.x = -offset.x;
- return offset;
- },
-
- /**
- * Updates the boundary information for this Draggable. This method shouldn't
- * have to be called by the developer and is mostly used for internal purposes.
- * Might be useful when creating subclasses of Draggable.
- * @param {Boolean} init Whether or not this is happing during instantiation, which we need
- * to apply the transform / style to the DOM element
- * @return {Ext.util.Draggable} this This Draggable instance
- * @private
- */
- updateBoundary: function(init) {
- var offsetBoundary;
-
- if (typeof init == 'undefined')
- init = false;
-
- this.size = {
- width: this.el.dom.scrollWidth,
- height: this.el.dom.scrollHeight
- };
-
- if (this.container === window) {
- this.containerBox = {
- left: 0,
- top: 0,
- right: this.container.innerWidth,
- bottom: this.container.innerHeight,
- width: this.container.innerWidth,
- height: this.container.innerHeight
- };
- }
- else {
- this.containerBox = this.container.getPageBox();
- }
-
- var elXY = this.el.getXY();
-
- this.elBox = {
- left: elXY[0] - this.offset.x,
- top: elXY[1] - this.offset.y,
- width: this.size.width,
- height: this.size.height
- };
-
- this.elBox.bottom = this.elBox.top + this.elBox.height;
- this.elBox.right = this.elBox.left + this.elBox.width;
-
- this.initialRegion = this.region = new Ext.util.Region(
- elXY[1], elXY[0] + this.elBox.width, elXY[1] + this.elBox.height, elXY[0]
- );
-
- var top = 0,
- right = 0,
- bottom = 0,
- left = 0;
-
- if (this.elBox.left < this.containerBox.left) {
- right += this.containerBox.left - this.elBox.left;
- }
- else {
- left -= this.elBox.left - this.containerBox.left;
- }
-
- if (this.elBox.right > this.containerBox.right) {
- left -= this.elBox.right - this.containerBox.right;
- }
- else {
- right += this.containerBox.right - this.elBox.right;
- }
-
- if (this.elBox.top < this.containerBox.top) {
- bottom += this.containerBox.top - this.elBox.top;
- }
- else {
- top -= this.elBox.top - this.containerBox.top;
- }
-
- if (this.elBox.bottom > this.containerBox.bottom) {
- top -= this.elBox.bottom - this.containerBox.bottom;
- }
- else {
- bottom += this.containerBox.bottom - this.elBox.bottom;
- }
-
- offsetBoundary = new Ext.util.Region(top, right, bottom, left).round();
-
- if (this.offsetBoundary && this.offsetBoundary.equals(offsetBoundary)) {
- return this;
- }
-
- this.offsetBoundary = offsetBoundary;
-
- var currentComputedOffset;
-
- if (this.useCssTransform) {
- currentComputedOffset = Ext.Element.getComputedTransformOffset(this.getProxyEl());
- } else {
- currentComputedOffset = new Ext.util.Offset(this.getProxyEl().getLeft(), this.getProxyEl().getTop());
- }
-
- if(!this.offset.equals(currentComputedOffset) || init) {
- this.setOffset(currentComputedOffset);
- }
-
- return this;
- },
-
- // @private
- onTouchStart: function() {
-
- },
-
- /**
- * Fires when the Drag operation starts. Internal use only.
- * @param {Event} e The event object for the drag operation
- * @private
- */
- onStart: function(e) {
- if (this.updateBoundaryOnTouchStart) {
- this.updateBoundary();
- }
-
- this.stopAnimation();
-
- if (this.dragging) {
- this.onDragEnd(e);
- }
-
- this.setDragging(true);
- this.startTouchPoint = new Ext.util.Point(e.startX, e.startY);
-
- this.startOffset = this.offset.copy();
-
- this.fireEvent('dragstart', this, e);
-
- return true;
- },
-
- /**
- * Gets the new offset from a touch offset.
- * @param {Ext.util.Offset} touchPoint The touch offset instance.
- * @private
- */
- getNewOffsetFromTouchPoint: function(touchPoint) {
- var xDelta = touchPoint.x - this.startTouchPoint.x,
- yDelta = touchPoint.y - this.startTouchPoint.y,
- newOffset = this.offset.copy();
-
- if(xDelta == 0 && yDelta == 0) {
- return newOffset;
- }
-
- if (this.horizontal)
- newOffset.x = this.startOffset.x + xDelta;
-
- if (this.vertical)
- newOffset.y = this.startOffset.y + yDelta;
-
- return newOffset;
- },
-
- /**
- * Fires when a drag events happens. Internal use only.
- * @param {Event} e The event object for the drag event
- * @private
- */
- onDrag: function(e) {
- if (!this.dragging) {
- return;
- }
-
- this.lastTouchPoint = Ext.util.Point.fromEvent(e);
- var newOffset = this.getNewOffsetFromTouchPoint(this.lastTouchPoint);
-
- if (this.offsetBoundary != null) {
- newOffset = this.offsetBoundary.restrict(newOffset, this.outOfBoundRestrictFactor);
- }
-
- this.setOffset(newOffset);
-
- this.fireEvent('drag', this, e);
-
- // This 'return true' here is to let sub-classes determine whether
- // there's an interuption return before that
- return true;
- },
-
- /**
- * Fires when a dragend event happens. Internal use only.
- * @param {Event} e The event object for the dragend event
- * @private
- */
- onDragEnd: function(e) {
- if (this.dragging) {
- this.fireEvent('beforedragend', this, e);
-
- if (this.revert && !this.cancelRevert) {
- this.setOffset(this.startOffset, true);
- } else {
- this.setDragging(false);
- }
-
- this.fireEvent('dragend', this, e);
- }
-
- // This 'return true' here is to let sub-classes determine whether
- // there's an interuption return before that
- return true;
- },
-
- /**
- * Fires when the orientation changes. Internal use only.
- * @private
- */
- onOrientationChange: function() {
- this.updateBoundary();
- },
-
- /**
- * Sets the dragging flag and adds a dragging class to the element.
- * @param {Boolean} dragging True to enable dragging, false to disable.
- * @private
- */
- setDragging: function(dragging) {
- if (dragging) {
- if (!this.dragging) {
- this.dragging = true;
- this.getProxyEl().addCls(this.draggingCls);
- }
- } else {
- if (this.dragging) {
- this.dragging = false;
- this.getProxyEl().removeCls(this.draggingCls);
- }
- }
-
- return this;
- },
-
- /**
- * Returns the element thats is being visually dragged.
- * @returns {Ext.Element} proxy The proxy element.
- */
- getProxyEl: function() {
- return this.proxy || this.el;
- },
-
- /**
- * Destroys this Draggable instance.
- */
- destroy: function() {
- this.el.removeCls(this.baseCls);
- this.getProxyEl().removeCls(this.proxyCls);
- this.clearListeners();
- this.disable();
- },
-
- /**
- * This method will reset the initial region of the Draggable.
- * @private
- */
- reset: function() {
- this.startOffset = new Ext.util.Offset(0, 0);
- this.setOffset(this.startOffset);
-
- var oldInitialRegion = this.initialRegion.copy();
-
- this.updateBoundary();
- this.initialRegion = this.region = this.getProxyEl().getPageBox(true);
- this.startTouchPoint.x += this.initialRegion.left - oldInitialRegion.left;
- this.startTouchPoint.y += this.initialRegion.top - oldInitialRegion.top;
- },
-
- /**
- * Use this to move the draggable to a coordinate on the screen.
- * @param {Number} x the vertical coordinate in pixels
- * @param {Number} y the horizontal coordinate in pixels
- * @return {Ext.util.Draggable} this This Draggable instance
- */
- moveTo: function(x, y) {
- this.setOffset(new Ext.util.Offset(x - this.initialRegion.left, y - this.initialRegion.top));
- return this;
- },
-
- /**
- * Method to determine whether this Draggable is currently dragging.
- * @return {Boolean}
- */
- isDragging: function() {
- return this.dragging;
- },
-
- /**
- * Method to determine whether this Draggable can be dragged on the y-axis
- * @return {Boolean}
- */
- isVertical : function() {
- return this.vertical;
- },
-
- /**
- * Method to determine whether this Draggable can be dragged on the x-axis
- * @return {Boolean}
- */
- isHorizontal : function() {
- return this.horizontal;
- }
-});
-
-Ext.util.Draggable.Animation = {};
-
-/**
- * @class Ext.util.Draggable.Animation.Abstract
- * @extends Object
- *
- * Provides the abstract methods for a Draggable animation.
- * @private
- * @ignore
- */
-Ext.util.Draggable.Animation.Abstract = Ext.extend(Object, {
- /**
- * @cfg {Number} startTime The time the Animation started
- * @private
- */
- startTime: null,
-
- /**
- * @cfg {Object/Ext.util.Offset} startOffset Object containing the x and y coordinates the
- * Draggable had when the Animation started.
- * @private
- */
- startOffset: 0,
-
- /**
- * The constructor for an Abstract animation. Applies the config to the Animation.
- * @param {Object} config Object containing the configuration for this Animation.
- * @private
- */
- constructor: function(config) {
- config = config || {};
-
- this.set(config);
-
- if (!this.startTime)
- this.startTime = Date.now();
- },
-
- /**
- * Sets a config value for this Animation.
- * @param {String} name The name of this configuration
- * @param {Mixed} value The value for this configuration
- */
- set: function(name, value) {
- if (Ext.isObject(name)) {
- Ext.apply(this, name);
- }
- else {
- this[name] = value;
- }
-
- return this;
- },
-
- /**
- * This method will return the offset of the coordinate that is being animated for any
- * given offset in time based on a different set of variables. Usually these variables are
- * a combination of the startOffset, endOffset, startTime and duration.
- * @return {Number} The offset for the coordinate that is being animated
- */
- getOffset: Ext.emptyFn
-});
-
-/**
- * @class Ext.util.Draggable.Animation.Linear
- * @extends Ext.util.Draggable.Animation.Abstract
- *
- * A linear animation that is being used by Draggable's setOffset by default.
- * @private
- * @ignore
- */
-Ext.util.Draggable.Animation.Linear = Ext.extend(Ext.util.Draggable.Animation.Abstract, {
- /**
- * @cfg {Number} duration The duration of this animation in milliseconds.
- */
- duration: 0,
-
- /**
- * @cfg {Object/Ext.util.Offset} endOffset Object containing the x and y coordinates the
- * Draggable is animating to.
- * @private
- */
- endOffset: 0,
-
- getOffset : function() {
- var distance = this.endOffset - this.startOffset,
- deltaTime = Date.now() - this.startTime,
- omegaTime = Math.min(1, (deltaTime / this.duration));
-
- return this.startOffset + (omegaTime * distance);
- }
-});
-/**
- * @class Ext.util.Droppable
- * @extends Ext.util.Observable
- *
- * @constructor
- */
-Ext.util.Droppable = Ext.extend(Ext.util.Observable, {
- baseCls: 'x-droppable',
- /**
- * @cfg {String} activeCls
- * The CSS added to a Droppable when a Draggable in the same group is being
- * dragged. Defaults to 'x-drop-active'.
- */
- activeCls: 'x-drop-active',
- /**
- * @cfg {String} invalidCls
- * The CSS class to add to the droppable when dragging a draggable that is
- * not in the same group. Defaults to 'x-drop-invalid'.
- */
- invalidCls: 'x-drop-invalid',
- /**
- * @cfg {String} hoverCls
- * The CSS class to add to the droppable when hovering over a valid drop. (Defaults to 'x-drop-hover')
- */
- hoverCls: 'x-drop-hover',
-
- /**
- * @cfg {String} validDropMode
- * Determines when a drop is considered 'valid' whether it simply need to
- * intersect the region or if it needs to be contained within the region.
- * Valid values are: 'intersects' or 'contains'
- */
- validDropMode: 'intersect',
-
- /**
- * @cfg {Boolean} disabled
- */
- disabled: false,
-
- /**
- * @cfg {String} group
- * Draggable and Droppable objects can participate in a group which are
- * capable of interacting. Defaults to 'base'
- */
- group: 'base',
-
- // not yet implemented
- tolerance: null,
-
-
- // @private
- monitoring: false,
-
- /**
- * @constructor
- * @param el {Mixed} String, HtmlElement or Ext.Element representing an
- * element on the page.
- * @param config {Object} Configuration options for this class.
- */
- constructor : function(el, config) {
- config = config || {};
- Ext.apply(this, config);
-
- this.addEvents(
- /**
- * @event dropactivate
- * @param {Ext.Droppable} this
- * @param {Ext.Draggable} draggable
- * @param {Ext.EventObject} e
- */
- 'dropactivate',
-
- /**
- * @event dropdeactivate
- * @param {Ext.Droppable} this
- * @param {Ext.Draggable} draggable
- * @param {Ext.EventObject} e
- */
- 'dropdeactivate',
-
- /**
- * @event dropenter
- * @param {Ext.Droppable} this
- * @param {Ext.Draggable} draggable
- * @param {Ext.EventObject} e
- */
- 'dropenter',
-
- /**
- * @event dropleave
- * @param {Ext.Droppable} this
- * @param {Ext.Draggable} draggable
- * @param {Ext.EventObject} e
- */
- 'dropleave',
-
- /**
- * @event drop
- * @param {Ext.Droppable} this
- * @param {Ext.Draggable} draggable
- * @param {Ext.EventObject} e
- */
- 'drop'
- );
-
- this.el = Ext.get(el);
- Ext.util.Droppable.superclass.constructor.call(this);
-
- if (!this.disabled) {
- this.enable();
- }
-
- this.el.addCls(this.baseCls);
- },
-
- // @private
- onDragStart : function(draggable, e) {
- if (draggable.group === this.group) {
- this.monitoring = true;
- this.el.addCls(this.activeCls);
- this.region = this.el.getPageBox(true);
-
- draggable.on({
- drag: this.onDrag,
- beforedragend: this.onBeforeDragEnd,
- dragend: this.onDragEnd,
- scope: this
- });
-
- if (this.isDragOver(draggable)) {
- this.setCanDrop(true, draggable, e);
- }
-
- this.fireEvent('dropactivate', this, draggable, e);
- }
- else {
- draggable.on({
- dragend: function() {
- this.el.removeCls(this.invalidCls);
- },
- scope: this,
- single: true
- });
- this.el.addCls(this.invalidCls);
- }
- },
-
- // @private
- isDragOver : function(draggable, region) {
- return this.region[this.validDropMode](draggable.region);
- },
-
- // @private
- onDrag : function(draggable, e) {
- this.setCanDrop(this.isDragOver(draggable), draggable, e);
- },
-
- // @private
- setCanDrop : function(canDrop, draggable, e) {
- if (canDrop && !this.canDrop) {
- this.canDrop = true;
- this.el.addCls(this.hoverCls);
- this.fireEvent('dropenter', this, draggable, e);
- }
- else if (!canDrop && this.canDrop) {
- this.canDrop = false;
- this.el.removeCls(this.hoverCls);
- this.fireEvent('dropleave', this, draggable, e);
- }
- },
-
- // @private
- onBeforeDragEnd: function(draggable, e) {
- draggable.cancelRevert = this.canDrop;
- },
-
- // @private
- onDragEnd : function(draggable, e) {
- this.monitoring = false;
- this.el.removeCls(this.activeCls);
-
- draggable.un({
- drag: this.onDrag,
- beforedragend: this.onBeforeDragEnd,
- dragend: this.onDragEnd,
- scope: this
- });
-
-
- if (this.canDrop) {
- this.canDrop = false;
- this.el.removeCls(this.hoverCls);
- this.fireEvent('drop', this, draggable, e);
- }
-
- this.fireEvent('dropdeactivate', this, draggable, e);
- },
-
- /**
- * Enable the Droppable target.
- * This is invoked immediately after constructing a Droppable if the
- * disabled parameter is NOT set to true.
- */
- enable : function() {
- if (!this.mgr) {
- this.mgr = Ext.util.Observable.observe(Ext.util.Draggable);
- }
- this.mgr.on({
- dragstart: this.onDragStart,
- scope: this
- });
- this.disabled = false;
- },
-
- /**
- * Disable the Droppable target.
- */
- disable : function() {
- this.mgr.un({
- dragstart: this.onDragStart,
- scope: this
- });
- this.disabled = true;
- },
-
- /**
- * Method to determine whether this Component is currently disabled.
- * @return {Boolean} the disabled state of this Component.
- */
- isDisabled : function() {
- return this.disabled;
- },
-
- /**
- * Method to determine whether this Droppable is currently monitoring drag operations of Draggables.
- * @return {Boolean} the monitoring state of this Droppable
- */
- isMonitoring : function() {
- return this.monitoring;
- }
-});
-
-(function(){
-
-Ext.ScrollManager = new Ext.AbstractManager();
-
-/**
- * @class Ext.util.ScrollView
- * @extends Ext.util.Observable
- *
- * A wrapper class around {@link Ext.util.Scroller Ext.util.Scroller} and {@link Ext.util.Scroller.Indicator Ext.util.Scroller.Indicator}
- * that listens to scroll events and control the scroll indicators
- */
-Ext.util.ScrollView = Ext.extend(Ext.util.Observable, {
-
- /**
- * @cfg {Boolean/String} useIndicators
- * Whether or not to use indicators. Can be either: <ul>
- * <li>{Boolean} true to display both directions, false otherwise</li>
- * <li>{String} 'vertical' or 'horizontal' to display for that specific direction only</li>
- * Defaults to true
- */
- useIndicators: true,
-
- /**
- * @cfg {Object} indicatorConfig
- * A valid config object for {@link Ext.util.Scroller.Indicator Ext.util.Scroller.Indicator}
- */
- indicatorConfig: {},
-
- /**
- * @cfg {Number} indicatorMargin
- * The margin value for the indicator relatively to the container.
- * Defaults to <tt>4</tt>
- */
- indicatorMargin: 4,
-
- constructor: function(el, config) {
- var indicators = [],
- directions = ['vertical', 'horizontal'];
-
- Ext.util.ScrollView.superclass.constructor.call(this);
-
- ['useIndicators', 'indicatorConfig', 'indicatorMargin'].forEach(function(c) {
- if (config.hasOwnProperty(c)) {
- this[c] = config[c];
- delete config[c];
- }
- }, this);
-
- config.scrollView = this;
- this.scroller = new Ext.util.Scroller(el, config);
-
- if (this.useIndicators === true) {
- directions.forEach(function(d) {
- if (this.scroller[d]) {
- indicators.push(d);
- }
- }, this);
- } else if (directions.indexOf(this.useIndicators) !== -1) {
- indicators.push(this.useIndicators);
- }
-
- this.indicators = {};
-
- indicators.forEach(function(i) {
- this.indicators[i] = new Ext.util.Scroller.Indicator(this.scroller.container, Ext.apply({}, this.indicatorConfig, {type: i}));
- }, this);
-
- this.mon(this.scroller, {
- scrollstart: this.onScrollStart,
- scrollend: this.onScrollEnd,
- scroll: this.onScroll,
- scope: this
- });
- },
-
- // @private
- onScrollStart: function() {
- this.showIndicators();
- },
-
- // @private
- onScrollEnd: function() {
- this.hideIndicators();
- },
-
- // @private
- onScroll: function(scroller) {
- if (scroller.offsetBoundary == null || (!this.indicators.vertical && !this.indicators.horizontal))
- return;
-
- var sizeAxis,
- offsetAxis,
- offsetMark,
- boundary = scroller.offsetBoundary,
- offset = scroller.offset;
-
- this.containerSize = scroller.containerBox;
- this.scrollerSize = scroller.size;
- this.outOfBoundOffset = boundary.getOutOfBoundOffset(offset);
- this.restrictedOffset = boundary.restrict(offset);
- this.boundarySize = boundary.getSize();
-
- if (!this.indicatorSizes) {
- this.indicatorSizes = {vertical: 0, horizontal: 0};
- }
-
- if (!this.indicatorOffsets) {
- this.indicatorOffsets = {vertical: 0, horizontal: 0};
- }
-
- Ext.iterate(this.indicators, function(axis, indicator) {
- sizeAxis = (axis == 'vertical') ? 'height' : 'width';
- offsetAxis = (axis == 'vertical') ? 'y' : 'x';
- offsetMark = (axis == 'vertical') ? 'bottom' : 'right';
-
- if (this.scrollerSize[sizeAxis] < this.containerSize[sizeAxis]) {
- this.indicatorSizes[axis] = this.containerSize[sizeAxis] * (this.scrollerSize[sizeAxis] / this.containerSize[sizeAxis]);
- }
- else {
- this.indicatorSizes[axis] = this.containerSize[sizeAxis] * (this.containerSize[sizeAxis] / this.scrollerSize[sizeAxis]);
- }
- this.indicatorSizes[axis] -= Math.abs(this.outOfBoundOffset[offsetAxis]);
- this.indicatorSizes[axis] = Math.max(this.indicatorMargin * 4, this.indicatorSizes[axis]);
-
- if (this.boundarySize[sizeAxis] != 0) {
- this.indicatorOffsets[axis] = (((boundary[offsetMark] - this.restrictedOffset[offsetAxis]) / this.boundarySize[sizeAxis])
- * (this.containerSize[sizeAxis] - this.indicatorSizes[axis]));
- } else if (offset[offsetAxis] < boundary[offsetMark]) {
- this.indicatorOffsets[axis] = this.containerSize[sizeAxis] - this.indicatorSizes[axis];
- } else {
- this.indicatorOffsets[axis] = 0;
- }
-
- indicator.setOffset(this.indicatorOffsets[axis] + this.indicatorMargin);
- indicator.setSize(this.indicatorSizes[axis] - (this.indicatorMargin * 2));
- }, this);
- },
-
- /*
- * Show the indicators if they are enabled; called automatically when the Scroller starts moving
- * @return {Ext.util.ScrollView} this This ScrollView
- */
- showIndicators : function() {
- Ext.iterate(this.indicators, function(axis, indicator) {
- indicator.show();
- }, this);
-
- return this;
- },
-
- /*
- * Hide the indicators if they are enabled; called automatically when the scrolling ends
- * @return {Ext.util.ScrollView} this This ScrollView
- */
- hideIndicators : function() {
- Ext.iterate(this.indicators, function(axis, indicator) {
- indicator.hide();
- }, this);
- },
-
- // Inherited docs
- destroy: function() {
- this.scroller.destroy();
-
- if (this.indicators) {
- Ext.iterate(this.indicators, function(axis, indicator) {
- indicator.destroy();
- }, this);
- }
-
- return Ext.util.ScrollView.superclass.destroy.apply(this, arguments);
- }
-});
-
-/**
- * @class Ext.util.Scroller
- * @extends Ext.util.Draggable
- *
- * Provide the native scrolling experience on iDevices for any DOM element
- */
-Ext.util.Scroller = Ext.extend(Ext.util.Draggable, {
- // Inherited
- baseCls: '',
-
- // Inherited
- draggingCls: '',
-
- // Inherited
- direction: 'both',
-
- // Inherited
- constrain: 'parent',
-
- /**
- * @cfg {Number} outOfBoundRestrictFactor
- * Determines the offset ratio when the scroller is pulled / pushed out of bound (when it's not decelerating)
- * A value of 0.5 means 1px allowed for every 2px. Defaults to 0.5
- */
- outOfBoundRestrictFactor: 0.5,
-
- /**
- * @cfg {Number} acceleration
- * A higher acceleration gives the scroller more initial velocity. Defaults to 30
- */
- acceleration: 30,
-
- /**
- * @cfg {Number} fps
- * The desired fps of the deceleration. Defaults to 80.
- */
- fps: Ext.is.Blackberry ? 22 : 80,
-
- autoAdjustFps: !Ext.is.Blackberry,
-
- /**
- * @cfg {Number} friction
- * The friction of the scroller.
- * By raising this value the length that momentum scrolls becomes shorter. This value is best kept
- * between 0 and 1. The default value is 0.5
- */
- friction: 0.5,
-
- /**
- * @cfg {Number} startMomentumResetTime
- * The time duration in ms to reset the start time of momentum
- * Defaults to 350
- */
- startMomentumResetTime: 350,
-
- /**
- * @cfg {Number} springTension
- * The tension of the spring that is attached to the scroller when it bounces.
- * By raising this value the bounce becomes shorter. This value is best kept
- * between 0 and 1. The default value is 0.3
- */
- springTension: 0.3,
-
- /**
- * @cfg {Number} minVelocityForAnimation
- * The minimum velocity to keep animating. Defaults to 1 (1px per second)
- */
- minVelocityForAnimation: 1,
-
- /**
- * @cfg {Boolean/String} bounces
- * Enable bouncing during scrolling past the bounds. Defaults to true. (Which is 'both').
- * You can also specify 'vertical', 'horizontal', or 'both'
- */
- bounces: true,
-
- /**
- * @cfg {Boolean} momentum
- * Whether or not to enable scrolling momentum. Defaults to true
- */
- momentum: true,
-
- cancelRevert: true,
-
- threshold: 5,
-
- constructor: function(el, config) {
- el = Ext.get(el);
-
- var scroller = Ext.ScrollManager.get(el.id);
- if (scroller) {
- return Ext.apply(scroller, config);
- }
-
- Ext.util.Scroller.superclass.constructor.apply(this, arguments);
-
- this.addEvents(
- /**
- * @event scrollstart
- * @param {Ext.util.Scroller} this
- * @param {Ext.EventObject} e
- */
- 'scrollstart',
- /**
- * @event scroll
- * @param {Ext.util.Scroller} this
- * @param {Object} offsets An object containing the x and y offsets for the scroller.
- */
- 'scroll',
- /**
- * @event scrollend
- * @param {Ext.util.Scroller} this
- * @param {Object} offsets An object containing the x and y offsets for the scroller.
- */
- 'scrollend',
- /**
- * @event bouncestart
- * @param {Ext.util.Scroller} this
- * @param {Object} info Object containing information regarding the bounce
- */
- 'bouncestart',
- /**
- * @event bouncestart
- * @param {Ext.util.Scroller} this
- * @param {Object} info Object containing information regarding the bounce
- */
- 'bounceend'
- );
-
- this.on({
- dragstart: this.onDragStart,
- offsetchange: this.onOffsetChange,
- scope: this
- });
-
- Ext.ScrollManager.register(this);
-
- this.el.addCls('x-scroller');
- this.container.addCls('x-scroller-parent');
-
- if (this.bounces !== false) {
- var both = this.bounces === 'both' || this.bounces === true,
- horizontal = both || this.bounces === 'horizontal',
- vertical = both || this.bounces === 'vertical';
-
- this.bounces = {
- x: horizontal,
- y: vertical
- };
- }
-
- this.theta = Math.log(1 - (this.friction / 10));
-
- if (!this.decelerationAnimation) {
- this.decelerationAnimation = {};
- }
-
- if (!this.bouncingAnimation) {
- this.bouncingAnimation = {};
- }
-
- ['x', 'y'].forEach(function(a) {
- if (!this.decelerationAnimation[a]) {
- this.decelerationAnimation[a] = new Ext.util.Scroller.Animation.Deceleration({
- acceleration: this.acceleration,
- theta: this.theta
- });
- }
-
- if (!this.bouncingAnimation[a]) {
- this.bouncingAnimation[a] = new Ext.util.Scroller.Animation.Bouncing({
- acceleration: this.acceleration,
- springTension: this.springTension
- });
- }
- }, this);
-
- return this;
- },
-
- getFrameDuration: function() {
- return 1000 / this.fps;
- },
-
- // Inherited docs
- updateBoundary: function() {
- Ext.util.Scroller.superclass.updateBoundary.apply(this, arguments);
-
- this.snapToBoundary();
-
- return this;
- },
-
- // Inherited docs
- onOffsetChange: function(scroller, offset) {
- this.fireEvent('scroll', scroller, {
- x: -offset.x,
- y: -offset.y
- });
- },
-
- // @private
- onTouchStart: function(e) {
- Ext.util.Scroller.superclass.onTouchStart.apply(this, arguments);
-
- this.stopMomentumAnimation();
- },
-
- // @private
- onDragStart: function(e) {
- this.fireEvent('scrollstart', this, e);
- },
-
- // @private
- setStartTime: function(e) {
- this.startTime = e.time;
- this.originalStartTime = (e.event.originalTimeStamp) ? e.event.originalTimeStamp : e.time;
- },
-
- // @private
- onStart: function(e) {
- if (Ext.util.Scroller.superclass.onStart.apply(this, arguments) !== true)
- return;
-
- this.setStartTime(e);
- this.lastEventTime = e.time;
- this.startTimeOffset = this.offset.copy();
- this.isScrolling = true;
-
- this.momentumAnimationFramesHandled = 0;
- },
-
- // @private
- onDrag: function(e) {
- if (Ext.util.Scroller.superclass.onDrag.apply(this, arguments) !== true)
- return;
-
- this.lastEventTime = e.time;
-
- if (this.lastEventTime - this.startTime > this.startMomentumResetTime) {
- this.setStartTime(e);
- this.startTimeOffset = this.offset.copy();
- }
- },
-
- // @private
- onDragEnd: function(e) {
- if (Ext.util.Scroller.superclass.onDragEnd.apply(this, arguments) !== true)
- return;
-
- if (!this.startMomentumAnimation(e)) {
- this.fireScrollEndEvent();
- }
- },
-
- // @private
- onOrientationChange: function() {
- Ext.util.Scroller.superclass.onOrientationChange.apply(this, arguments);
-
- this.snapToBoundary();
- },
-
- // @private
- fireScrollEndEvent: function() {
- this.isScrolling = false;
- this.isMomentumAnimating = false;
- this.snapToBoundary();
- this.fireEvent('scrollend', this, this.getOffset());
-
- this.snapToSlot();
- },
-
-
- /*
- * Get the last actual fps performed by this Scroller. Useful for benchmarking
- * @return {Number} The actual fps
- */
- getLastActualFps: function() {
- var duration = (this.momentumAnimationEndTime - this.momentumAnimationStartTime - this.momentumAnimationProcessingTime) / 1000;
- return this.momentumAnimationFramesHandled / duration;
- },
-
- /*
- * Similar to {@link Ext.util.Scroller#setOffset setOffset}, but will stop any existing animation
- * @param {Object} pos The new scroll position, e.g {x: 100, y: 200}
- * @param {Number/Boolean} animate Whether or not to animate while changing the scroll position.
- * If it's a number, will be treated as the duration in ms
- * @return {Ext.util.Scroller} this This Scroller
- */
- scrollTo: function(pos, animate) {
- this.stopMomentumAnimation();
-
- var newOffset = this.offsetBoundary.restrict(new Ext.util.Offset(-pos.x, -pos.y));
-
- this.setOffset(newOffset, animate);
-
- return this;
- },
-
- /*
- * Change the scroll offset by the given amount
- * @param {Ext.util.Offset/Object} offset The amount to scroll by, e.g {x: 100, y: 200}
- * @param {Number/Boolean} animate Whether or not to animate while changing the scroll position.
- * If it's a number, will be treated as the duration in ms
- * @return {Ext.util.Scroller} this This Scroller
- */
- scrollBy: function(offset, animate) {
- this.stopMomentumAnimation();
-
- var newOffset = this.offset.copy();
- newOffset.x += offset.x;
- newOffset.y += offset.y;
-
- this.setOffset(newOffset, animate);
-
- return this;
- },
-
- // @private
- setSnap: function(snap) {
- this.snap = snap;
- },
-
- /*
- * Snap this scrollable content back to the container's boundary, if it's currently out of bound
- * @return {Ext.util.Scroller} this This Scroller
- */
- snapToBoundary: function() {
- var offset = this.offsetBoundary.restrict(this.offset);
- this.setOffset(offset);
-
- return this;
- },
-
- snapToSlot: function() {
- var offset = this.offsetBoundary.restrict(this.offset);
- offset.round();
-
- if (this.snap) {
- if (this.snap === true) {
- this.snap = {
- x: 50,
- y: 50
- };
- }
- else if (Ext.isNumber(this.snap)) {
- this.snap = {
- x: this.snap,
- y: this.snap
- };
- }
- if (this.snap.y) {
- offset.y = Math.round(offset.y / this.snap.y) * this.snap.y;
- }
- if (this.snap.x) {
- offset.x = Math.round(offset.x / this.snap.x) * this.snap.x;
- }
-
- if (!this.offset.equals(offset)) {
- this.scrollTo({x: -offset.x, y: -offset.y}, this.snapDuration);
- }
- }
- },
-
- // @private
- startMomentumAnimation: function(e) {
- var originalTime = (e.event.originalTimeStamp) ? e.event.originalTimeStamp : e.time,
- duration = originalTime - this.originalStartTime;
-
- if (
- (!this.momentum || !(duration <= this.startMomentumResetTime)) &&
- !this.offsetBoundary.isOutOfBound(this.offset)
- ) {
- return false;
- }
-
- // Determine the duration of the momentum
- var minVelocity = this.minVelocityForAnimation,
- currentVelocity,
- currentOffset = this.offset.copy(),
- restrictedOffset;
-
- this.isBouncing = {x: false, y: false};
- this.isDecelerating = {x: false, y: false};
- this.momentumAnimationStartTime = e.time;
- this.momentumAnimationProcessingTime = 0;
-
- // Determine the deceleration velocity
- this.momentumAnimationStartVelocity = {
- x: (this.offset.x - this.startTimeOffset.x) / (duration / this.acceleration),
- y: (this.offset.y - this.startTimeOffset.y) / (duration / this.acceleration)
- };
-
- this.momentumAnimationStartOffset = currentOffset;
-
- ['x', 'y'].forEach(function(axis) {
-
- this.isDecelerating[axis] = (Math.abs(this.momentumAnimationStartVelocity[axis]) > minVelocity);
-
- if (this.bounces && this.bounces[axis]) {
- restrictedOffset = this.offsetBoundary.restrict(axis, currentOffset[axis]);
-
- if (restrictedOffset != currentOffset[axis]) {
- currentVelocity = (currentOffset[axis] - restrictedOffset) * this.springTension * Math.E;
- this.bouncingAnimation[axis].set({
- startTime: this.momentumAnimationStartTime - ((1 / this.springTension) * this.acceleration),
- startOffset: restrictedOffset,
- startVelocity: currentVelocity
- });
- this.isBouncing[axis] = true;
- this.fireEvent('bouncestart', this, {
- axis: axis,
- offset: restrictedOffset,
- time: this.momentumAnimationStartTime,
- velocity: currentVelocity
- });
- this.isDecelerating[axis] = false;
- }
- }
-
- if (this.isDecelerating[axis]) {
- this.decelerationAnimation[axis].set({
- startVelocity: this.momentumAnimationStartVelocity[axis],
- startOffset: this.momentumAnimationStartOffset[axis],
- startTime: this.momentumAnimationStartTime
- });
- }
- }, this);
-
- if (this.isDecelerating.x || this.isDecelerating.y || this.isBouncing.x || this.isBouncing.y) {
- this.isMomentumAnimating = true;
- this.momentumAnimationFramesHandled = 0;
- this.fireEvent('momentumanimationstart');
- this.momentumAnimationTimer = Ext.defer(this.handleMomentumAnimationFrame, this.getFrameDuration(), this);
- return true;
- }
-
- return false;
- },
-
- // @private
- stopMomentumAnimation: function() {
- if (this.isMomentumAnimating) {
- if (this.momentumAnimationTimer) {
- clearTimeout(this.momentumAnimationTimer);
- }
- this.momentumAnimationEndTime = Date.now();
-
- var lastFps = this.getLastActualFps();
-
- if (!this.maxFps || lastFps > this.maxFps) {
- this.maxFps = lastFps;
- }
-
- if (this.autoAdjustFps) {
- this.fps = this.maxFps;
- }
-
- this.isDecelerating = {};
- this.isBouncing = {};
-
- this.fireEvent('momentumanimationend');
- this.fireScrollEndEvent();
-
- }
-
- return this;
- },
-
-// fireEvent: function(name) {
-// if (['scroll', 'offsetchange'].indexOf(name) == -1) {
-// console.log(name);
-// }
-// return Ext.util.Scroller.superclass.fireEvent.apply(this, arguments);
-// },
-
- /**
- * @private
- */
- handleMomentumAnimationFrame : function() {
- if (!this.isMomentumAnimating) {
- return;
- }
-
- this.momentumAnimationTimer = Ext.defer(this.handleMomentumAnimationFrame, this.getFrameDuration(), this);
-
- var currentTime = Date.now(),
- newOffset = this.offset.copy(),
- currentVelocity,
- restrictedOffset,
- outOfBoundDistance;
-
- ['x', 'y'].forEach(function(axis) {
- if (this.isDecelerating[axis]) {
- newOffset[axis] = this.decelerationAnimation[axis].getOffset();
- currentVelocity = this.momentumAnimationStartVelocity[axis] * this.decelerationAnimation[axis].getFrictionFactor();
- outOfBoundDistance = this.offsetBoundary.getOutOfBoundOffset(axis, newOffset[axis]);
-
- // If the new offset is out of boundary, we are going to start a bounce
- if (outOfBoundDistance != 0) {
- restrictedOffset = this.offsetBoundary.restrict(axis, newOffset[axis]);
- if (this.bounces && this.bounces[axis]) {
- this.bouncingAnimation[axis].set({
- startTime: currentTime,
- startOffset: restrictedOffset,
- startVelocity: currentVelocity
- });
- this.isBouncing[axis] = true;
- this.fireEvent('bouncestart', this, {
- axis: axis,
- offset: restrictedOffset,
- time: currentTime,
- velocity: currentVelocity
- });
- }
- this.isDecelerating[axis] = false;
- }
- else if (Math.abs(currentVelocity) <= 1) {
- this.isDecelerating[axis] = false;
- }
- }
- else if (this.isBouncing[axis]) {
- newOffset[axis] = this.bouncingAnimation[axis].getOffset();
- restrictedOffset = this.offsetBoundary.restrict(axis, newOffset[axis]);
-
- if (Math.abs(newOffset[axis] - restrictedOffset) <= 1) {
- this.isBouncing[axis] = false;
- this.fireEvent('bounceend', this, {
- axis: axis,
- offset: restrictedOffset,
- time: currentTime,
- velocity: 0
- });
- newOffset[axis] = restrictedOffset;
- }
- }
- }, this);
-
- if (!this.isDecelerating.x && !this.isDecelerating.y && !this.isBouncing.x && !this.isBouncing.y) {
- this.stopMomentumAnimation();
- return;
- }
-
- this.momentumAnimationFramesHandled++;
- this.momentumAnimationProcessingTime += Date.now() - currentTime;
-
- this.setOffset(newOffset);
- },
-
- // Inherited docs
- destroy: function() {
- return Ext.util.Scroller.superclass.destroy.apply(this, arguments);
- }
-});
-
-Ext.util.Scroller.Animation = {};
-
-Ext.util.Scroller.Animation.Deceleration = Ext.extend(Ext.util.Draggable.Animation.Abstract, {
- acceleration: 30,
- theta: null,
- startVelocity: null,
-
- getOffset: function() {
- return this.startOffset - this.startVelocity * (1 - this.getFrictionFactor()) / this.theta;
- },
-
- getFrictionFactor : function() {
- var deltaTime = Date.now() - this.startTime;
-
- return Math.exp(deltaTime / this.acceleration * this.theta);
- }
-});
-
-Ext.util.Scroller.Animation.Bouncing = Ext.extend(Ext.util.Draggable.Animation.Abstract, {
- springTension: 0.3,
- acceleration: 30,
- startVelocity: null,
-
- getOffset: function() {
- var deltaTime = (Date.now() - this.startTime),
- powTime = (deltaTime / this.acceleration) * Math.pow(Math.E, -this.springTension * (deltaTime / this.acceleration));
-
- return this.startOffset + (this.startVelocity * powTime);
- }
-});
-
-/**
- * @class Ext.util.Indicator
- * @extends Object
- *
- * Represent the Scroll Indicator to be used in a {@link Ext.util.ScrollView ScrollView}
- */
-Ext.util.Scroller.Indicator = Ext.extend(Object, {
- baseCls: 'x-scrollbar',
-
- ui: 'dark',
-
- /**
- * @cfg {String} type
- * The type of this Indicator, valid values are 'vertical' or 'horizontal'
- */
- type: 'horizontal',
-
- constructor: function(container, config) {
- this.container = container;
-
- Ext.apply(this, config);
-
- this.el = this.container.createChild({
- cls: [this.baseCls, this.baseCls + '-' + this.type, this.baseCls + '-' + this.ui].join(' ')
- });
-
- this.offset = new Ext.util.Offset();
-
- this.hide();
- },
-
- /*
- * Hide this Indicator
- * @return {Ext.util.Scroller.Indicator} this This Indicator
- */
- hide: function() {
- var me = this;
-
- if (this.hideTimer) {
- clearTimeout(this.hideTimer);
- }
-
- this.hideTimer = setTimeout(function() {
- me.el.setStyle('opacity', 0);
- }, 100);
-
- return this;
- },
-
- /*
- * Show this Indicator
- * @return {Ext.util.Scroller.Indicator} this This Indicator
- */
- show: function() {
- if (this.hideTimer) {
- clearTimeout(this.hideTimer);
- }
-
- this.el.setStyle('opacity', 1);
-
- return this;
- },
-
- /*
- * Set the visibility of this Indicator, a wrapper function for
- * {@link Ext.util.Scroller.Indicator#show show} and {@link Ext.util.Scroller.Indicator#show hide}
- * @param {Boolean} isVisible True to show this Indicator, false to hide
- * @return {Ext.util.Scroller.Indicator} this This Indicator
- */
- setVisibility: function(isVisible) {
- return this[isVisible ? 'show' : 'hide']();
- },
-
- /*
- * Adjust the size of this Indicator, will change the height if {@link Ext.util.Scroller.Indicator#type type}
- * is 'vertical', and width for 'horizontal'
- * @param {Number} size The new size to change to
- * @return {Ext.util.Scroller.Indicator} this This Indicator
- */
- setSize: function(size) {
- if (this.size && size > this.size) {
- size = Math.round(size);
- }
-
- // this.el.setStyle(height) is cleaner here but let's save some little performance...
- this.el.dom.style[(this.type == 'horizontal') ? 'width' : 'height'] = size + 'px';
-
- this.size = size;
-
- return this;
- },
-
- /*
- * Set the offset position of this Indicator, relative to its container
- * @param {Number} offset The new offset
- * @return {Ext.util.Scroller.Indicator} this This Indicator
- */
- setOffset: function(offset) {
- if (this.type == 'vertical') {
- this.offset.y = offset;
- } else {
- this.offset.x = offset;
- }
-
- if (!Ext.is.iOS && !Ext.is.Desktop) {
- if (this.type == 'vertical') {
- this.el.dom.style.top = this.offset.y + 'px';
- } else {
- this.el.dom.style.left = this.offset.x + 'px';
- }
- } else {
- Ext.Element.cssTranslate(this.el, this.offset);
- }
-
- return this;
- }
-
-});
-
-})();
-
-/**
- * @class Ext.util.Sortable
- * @extends Ext.util.Observable
- * @constructor
- * @param {Mixed} el
- * @param {Object} config
- */
-Ext.util.Sortable = Ext.extend(Ext.util.Observable, {
- baseCls: 'x-sortable',
-
- /**
- * @cfg {String} direction
- * Possible values: 'vertical', 'horizontal'
- * Defaults to 'vertical'
- */
- direction: 'vertical',
-
- /**
- * @cfg {String} cancelSelector
- * A simple CSS selector that represents elements within the draggable
- * that should NOT initiate a drag.
- */
- cancelSelector: null,
-
- // not yet implemented
- //indicator: true,
- //proxy: true,
- //tolerance: null,
-
- /**
- * @cfg {Element/Boolean} constrain
- * An Element to constrain the Sortable dragging to. Defaults to <tt>window</tt>.
- * If <tt>true</tt> is specified, the dragging will be constrained to the element
- * of the sortable.
- */
- constrain: window,
- /**
- * @cfg {String} group
- * Draggable and Droppable objects can participate in a group which are
- * capable of interacting. Defaults to 'base'
- */
- group: 'base',
-
- /**
- * @cfg {Boolean} revert
- * This should NOT be changed.
- * @private
- */
- revert: true,
-
- /**
- * @cfg {String} itemSelector
- * A simple CSS selector that represents individual items within the Sortable.
- */
- itemSelector: null,
-
- /**
- * @cfg {String} handleSelector
- * A simple CSS selector to indicate what is the handle to drag the Sortable.
- */
- handleSelector: null,
-
- /**
- * @cfg {Boolean} disabled
- * Passing in true will disable this Sortable.
- */
- disabled: false,
-
- /**
- * @cfg {Number} delay
- * How many milliseconds a user must hold the draggable before starting a
- * drag operation. Defaults to 0 or immediate.
- * @private
- */
- delay: 0,
-
- // Properties
-
- /**
- * Read-only property that indicates whether a Sortable is currently sorting.
- * @type Boolean
- * @private
- */
- sorting: false,
-
- /**
- * Read-only value representing whether the Draggable can be moved vertically.
- * This is automatically calculated by Draggable by the direction configuration.
- * @type Boolean
- * @private
- */
- vertical: false,
-
- /**
- * Read-only value representing whether the Draggable can be moved horizontally.
- * This is automatically calculated by Draggable by the direction configuration.
- * @type Boolean
- * @private
- */
- vertical: false,
-
- constructor : function(el, config) {
- config = config || {};
- Ext.apply(this, config);
-
- this.addEvents(
- /**
- * @event sortstart
- * @param {Ext.Sortable} this
- * @param {Ext.EventObject} e
- */
- 'sortstart',
- /**
- * @event sortend
- * @param {Ext.Sortable} this
- * @param {Ext.EventObject} e
- */
- 'sortend',
- /**
- * @event sortchange
- * @param {Ext.Sortable} this
- * @param {Ext.Element} el The Element being dragged.
- * @param {Number} index The index of the element after the sort change.
- */
- 'sortchange'
-
- // not yet implemented.
- // 'sortupdate',
- // 'sortreceive',
- // 'sortremove',
- // 'sortenter',
- // 'sortleave',
- // 'sortactivate',
- // 'sortdeactivate'
- );
-
- this.el = Ext.get(el);
- Ext.util.Sortable.superclass.constructor.call(this);
-
- if (this.direction == 'horizontal') {
- this.horizontal = true;
- }
- else if (this.direction == 'vertical') {
- this.vertical = true;
- }
- else {
- this.horizontal = this.vertical = true;
- }
-
- this.el.addCls(this.baseCls);
- //this.tapEvent = (this.delay > 0) ? 'taphold' : 'tapstart';
- if (!this.disabled) {
- this.enable();
- }
- },
-
- // @private
- onTouchStart : function(e, t) {
- if (this.cancelSelector && e.getTarget(this.cancelSelector)) {
- return;
- }
- if (this.handleSelector && !e.getTarget(this.handleSelector)) {
- return;
- }
-
- if (!this.sorting) {
- this.onSortStart(e, t);
- }
- },
-
- // @private
- onSortStart : function(e, t) {
- this.sorting = true;
- var draggable = new Ext.util.Draggable(t, {
- delay: this.delay,
- revert: this.revert,
- direction: this.direction,
- constrain: this.constrain === true ? this.el : this.constrain,
- animationDuration: 100
- });
- draggable.on({
- dragThreshold: 0,
- drag: this.onDrag,
- dragend: this.onDragEnd,
- scope: this
- });
-
- this.dragEl = t;
- this.calculateBoxes();
-
- if (!draggable.dragging) {
- draggable.onStart(e);
- }
-
- this.fireEvent('sortstart', this, e);
- },
-
- // @private
- calculateBoxes : function() {
- this.items = [];
- var els = this.el.select(this.itemSelector, false),
- ln = els.length, i, item, el, box;
-
- for (i = 0; i < ln; i++) {
- el = els[i];
- if (el != this.dragEl) {
- item = Ext.fly(el).getPageBox(true);
- item.el = el;
- this.items.push(item);
- }
- }
- },
-
- // @private
- onDrag : function(draggable, e) {
- var items = this.items,
- ln = items.length,
- region = draggable.region,
- sortChange = false,
- i, intersect, overlap, item;
-
- for (i = 0; i < ln; i++) {
- item = items[i];
- intersect = region.intersect(item);
- if (intersect) {
- if (this.vertical && Math.abs(intersect.top - intersect.bottom) > (region.bottom - region.top) / 2) {
- if (region.bottom > item.top && item.top > region.top) {
- draggable.el.insertAfter(item.el);
- }
- else {
- draggable.el.insertBefore(item.el);
- }
- sortChange = true;
- }
- else if (this.horizontal && Math.abs(intersect.left - intersect.right) > (region.right - region.left) / 2) {
- if (region.right > item.left && item.left > region.left) {
- draggable.el.insertAfter(item.el);
- }
- else {
- draggable.el.insertBefore(item.el);
- }
- sortChange = true;
- }
-
- if (sortChange) {
- // We reset the draggable (initializes all the new start values)
- draggable.reset();
-
- // Move the draggable to its current location (since the transform is now
- // different)
- draggable.moveTo(region.left, region.top);
-
- // Finally lets recalculate all the items boxes
- this.calculateBoxes();
- this.fireEvent('sortchange', this, draggable.el, this.el.select(this.itemSelector, false).indexOf(draggable.el.dom));
- return;
- }
- }
- }
- },
-
- // @private
- onDragEnd : function(draggable, e) {
- draggable.destroy();
- this.sorting = false;
- this.fireEvent('sortend', this, draggable, e);
- },
-
- /**
- * Enables sorting for this Sortable.
- * This method is invoked immediately after construction of a Sortable unless
- * the disabled configuration is set to true.
- */
- enable : function() {
- this.el.on('touchstart', this.onTouchStart, this, {delegate: this.itemSelector});
- this.disabled = false;
- },
-
- /**
- * Disables sorting for this Sortable.
- */
- disable : function() {
- this.el.un('touchstart', this.onTouchStart, this);
- this.disabled = true;
- },
-
- /**
- * Method to determine whether this Sortable is currently disabled.
- * @return {Boolean} the disabled state of this Sortable.
- */
- isDisabled : function() {
- return this.disabled;
- },
-
- /**
- * Method to determine whether this Sortable is currently sorting.
- * @return {Boolean} the sorting state of this Sortable.
- */
- isSorting : function() {
- return this.sorting;
- },
-
- /**
- * Method to determine whether this Sortable is currently disabled.
- * @return {Boolean} the disabled state of this Sortable.
- */
- isVertical : function() {
- return this.vertical;
- },
-
- /**
- * Method to determine whether this Sortable is currently sorting.
- * @return {Boolean} the sorting state of this Sortable.
- */
- isHorizontal : function() {
- return this.horizontal;
- }
-});
-
-/**
- * @class Date
- *
- * The date parsing and formatting syntax contains a subset of
- * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
- * supported will provide results equivalent to their PHP versions.
- *
- * The following is a list of all currently supported formats:
- * <pre>
-Format Description Example returned values
------- ----------------------------------------------------------------------- -----------------------
- d Day of the month, 2 digits with leading zeros 01 to 31
- D A short textual representation of the day of the week Mon to Sun
- j Day of the month without leading zeros 1 to 31
- l A full textual representation of the day of the week Sunday to Saturday
- N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
- S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
- w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
- z The day of the year (starting from 0) 0 to 364 (365 in leap years)
- W ISO-8601 week number of year, weeks starting on Monday 01 to 53
- F A full textual representation of a month, such as January or March January to December
- m Numeric representation of a month, with leading zeros 01 to 12
- M A short textual representation of a month Jan to Dec
- n Numeric representation of a month, without leading zeros 1 to 12
- t Number of days in the given month 28 to 31
- L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
- o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
- belongs to the previous or next year, that year is used instead)
- Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
- y A two digit representation of a year Examples: 99 or 03
- a Lowercase Ante meridiem and Post meridiem am or pm
- A Uppercase Ante meridiem and Post meridiem AM or PM
- g 12-hour format of an hour without leading zeros 1 to 12
- G 24-hour format of an hour without leading zeros 0 to 23
- h 12-hour format of an hour with leading zeros 01 to 12
- H 24-hour format of an hour with leading zeros 00 to 23
- i Minutes, with leading zeros 00 to 59
- s Seconds, with leading zeros 00 to 59
- u Decimal fraction of a second Examples:
- (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
- 100 (i.e. 0.100s) or
- 999 (i.e. 0.999s) or
- 999876543210 (i.e. 0.999876543210s)
- O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
- P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
- T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
- Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
- c ISO 8601 date
- Notes: Examples:
- 1) If unspecified, the month / day defaults to the current month / day, 1991 or
- the time defaults to midnight, while the timezone defaults to the 1992-10 or
- browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
- and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
- are optional. 1995-07-18T17:21:28-02:00 or
- 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
- least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
- of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
- Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
- date-time granularity which are supported, or see 2000-02-13T21:25:33
- http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
- U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
- M$ Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
- \/Date(1238606590509+0800)\/
-</pre>
- *
- * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
- * <pre><code>
-// Sample date:
-// 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
-
-var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
-document.write(dt.format('Y-m-d')); // 2007-01-10
-document.write(dt.format('F j, Y, g:i a')); // January 10, 2007, 3:05 pm
-document.write(dt.format('l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
-</code></pre>
- *
- * Here are some standard date/time patterns that you might find helpful. They
- * are not part of the source of Date.js, but to use them you can simply copy this
- * block of code into any script that is included after Date.js and they will also become
- * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
- * <pre><code>
-Date.patterns = {
- ISO8601Long:"Y-m-d H:i:s",
- ISO8601Short:"Y-m-d",
- ShortDate: "n/j/Y",
- LongDate: "l, F d, Y",
- FullDateTime: "l, F d, Y g:i:s A",
- MonthDay: "F d",
- ShortTime: "g:i A",
- LongTime: "g:i:s A",
- SortableDateTime: "Y-m-d\\TH:i:s",
- UniversalSortableDateTime: "Y-m-d H:i:sO",
- YearMonth: "F, Y"
-};
-</code></pre>
- *
- * Example usage:
- * <pre><code>
-var dt = new Date();
-document.write(dt.format(Date.patterns.ShortDate));
-</code></pre>
- * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
- * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
- */
-
-/*
- * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
- * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
- * They generate precompiled functions from format patterns instead of parsing and
- * processing each pattern every time a date is formatted. These functions are available
- * on every Date object.
- */
-
- (function() {
-
- /**
- * Global flag which determines if strict date parsing should be used.
- * Strict date parsing will not roll-over invalid dates, which is the
- * default behaviour of javascript Date objects.
- * (see {@link #parseDate} for more information)
- * Defaults to <tt>false</tt>.
- * @static
- * @type Boolean
-*/
- Date.useStrict = false;
-
-
- // create private copy of Ext's Ext.util.Format.format() method
- // - to remove unnecessary dependency
- // - to resolve namespace conflict with M$-Ajax's implementation
- function xf(format) {
- var args = Array.prototype.slice.call(arguments, 1);
- return format.replace(/\{(\d+)\}/g,
- function(m, i) {
- return args[i];
- });
- }
-
-
- // private
- Date.formatCodeToRegex = function(character, currentGroup) {
- // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
- var p = Date.parseCodes[character];
-
- if (p) {
- p = typeof p == 'function' ? p() : p;
- Date.parseCodes[character] = p;
- // reassign function result to prevent repeated execution
- }
-
- return p ? Ext.applyIf({
- c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
- },
- p) : {
- g: 0,
- c: null,
- s: Ext.util.Format.escapeRegex(character)
- // treat unrecognised characters as literals
- };
- };
-
- // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
- var $f = Date.formatCodeToRegex;
-
- Ext.apply(Date, {
- /**
- * <p>An object hash in which each property is a date parsing function. The property name is the
- * format string which that function parses.</p>
- * <p>This object is automatically populated with date parsing functions as
- * date formats are requested for Ext standard formatting strings.</p>
- * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
- * may be used as a format string to {@link #parseDate}.<p>
- * <p>Example:</p><pre><code>
-Date.parseFunctions['x-date-format'] = myDateParser;
-</code></pre>
- * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
- * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
- * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
- * (i.e. prevent javascript Date "rollover") (The default must be false).
- * Invalid date strings should return null when parsed.</div></li>
- * </ul></div></p>
- * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
- * formatting function must be placed into the {@link #formatFunctions} property.
- * @property parseFunctions
- * @static
- * @type Object
- */
- parseFunctions: {
- "M$": function(input, strict) {
- // note: the timezone offset is ignored since the M$ Ajax server sends
- // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
- var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
- var r = (input || '').match(re);
- return r ? new Date(((r[1] || '') + r[2]) * 1) : null;
- }
- },
- parseRegexes: [],
-
- /**
- * <p>An object hash in which each property is a date formatting function. The property name is the
- * format string which corresponds to the produced formatted date string.</p>
- * <p>This object is automatically populated with date formatting functions as
- * date formats are requested for Ext standard formatting strings.</p>
- * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
- * may be used as a format string to {@link #format}. Example:</p><pre><code>
-Date.formatFunctions['x-date-format'] = myDateFormatter;
-</code></pre>
- * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
- * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
- * </ul></div></p>
- * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
- * parsing function must be placed into the {@link #parseFunctions} property.
- * @property formatFunctions
- * @static
- * @type Object
- */
- formatFunctions: {
- "M$": function() {
- // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
- return '\\/Date(' + this.getTime() + ')\\/';
- }
- },
-
- y2kYear: 50,
-
- /**
- * Date interval constant
- * @static
- * @type String
- */
- MILLI: "ms",
-
- /**
- * Date interval constant
- * @static
- * @type String
- */
- SECOND: "s",
-
- /**
- * Date interval constant
- * @static
- * @type String
- */
- MINUTE: "mi",
-
- /** Date interval constant
- * @static
- * @type String
- */
- HOUR: "h",
-
- /**
- * Date interval constant
- * @static
- * @type String
- */
- DAY: "d",
-
- /**
- * Date interval constant
- * @static
- * @type String
- */
- MONTH: "mo",
-
- /**
- * Date interval constant
- * @static
- * @type String
- */
- YEAR: "y",
-
- /**
- * <p>An object hash containing default date values used during date parsing.</p>
- * <p>The following properties are available:<div class="mdetail-params"><ul>
- * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
- * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
- * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
- * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
- * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
- * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
- * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
- * </ul></div></p>
- * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
- * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
- * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
- * It is the responsiblity of the developer to account for this.</b></p>
- * Example Usage:
- * <pre><code>
-// set default day value to the first day of the month
-Date.defaults.d = 1;
-
-// parse a February date string containing only year and month values.
-// setting the default day value to 1 prevents weird date rollover issues
-// when attempting to parse the following date string on, for example, March 31st 2009.
-Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
-</code></pre>
- * @property defaults
- * @static
- * @type Object
- */
- defaults: {},
-
- /**
- * An array of textual day names.
- * Override these values for international dates.
- * Example:
- * <pre><code>
-Date.dayNames = [
- 'SundayInYourLang',
- 'MondayInYourLang',
- ...
-];
-</code></pre>
- * @type Array
- * @static
- */
- dayNames: [
- "Sunday",
- "Monday",
- "Tuesday",
- "Wednesday",
- "Thursday",
- "Friday",
- "Saturday"
- ],
-
- /**
- * An array of textual month names.
- * Override these values for international dates.
- * Example:
- * <pre><code>
-Date.monthNames = [
- 'JanInYourLang',
- 'FebInYourLang',
- ...
-];
-</code></pre>
- * @type Array
- * @static
- */
- monthNames: [
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December"
- ],
-
- /**
- * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
- * Override these values for international dates.
- * Example:
- * <pre><code>
-Date.monthNumbers = {
- 'ShortJanNameInYourLang':0,
- 'ShortFebNameInYourLang':1,
- ...
-};
-</code></pre>
- * @type Object
- * @static
- */
- monthNumbers: {
- Jan: 0,
- Feb: 1,
- Mar: 2,
- Apr: 3,
- May: 4,
- Jun: 5,
- Jul: 6,
- Aug: 7,
- Sep: 8,
- Oct: 9,
- Nov: 10,
- Dec: 11
- },
-
- /**
- * Get the short month name for the given month number.
- * Override this function for international dates.
- * @param {Number} month A zero-based javascript month number.
- * @return {String} The short month name.
- * @static
- */
- getShortMonthName: function(month) {
- return Date.monthNames[month].substring(0, 3);
- },
-
- /**
- * Get the short day name for the given day number.
- * Override this function for international dates.
- * @param {Number} day A zero-based javascript day number.
- * @return {String} The short day name.
- * @static
- */
- getShortDayName: function(day) {
- return Date.dayNames[day].substring(0, 3);
- },
-
- /**
- * Get the zero-based javascript month number for the given short/full month name.
- * Override this function for international dates.
- * @param {String} name The short/full month name.
- * @return {Number} The zero-based javascript month number.
- * @static
- */
- getMonthNumber: function(name) {
- // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
- return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
- },
-
- /**
- * The base format-code to formatting-function hashmap used by the {@link #format} method.
- * Formatting functions are strings (or functions which return strings) which
- * will return the appropriate value when evaluated in the context of the Date object
- * from which the {@link #format} method is called.
- * Add to / override these mappings for custom date formatting.
- * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
- * Example:
- * <pre><code>
-Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
-(new Date()).format("X"); // returns the current day of the month
-</code></pre>
- * @type Object
- * @static
- */
- formatCodes: {
- d: "Ext.util.Format.leftPad(this.getDate(), 2, '0')",
- D: "Date.getShortDayName(this.getDay())",
- // get localised short day name
- j: "this.getDate()",
- l: "Date.dayNames[this.getDay()]",
- N: "(this.getDay() ? this.getDay() : 7)",
- S: "this.getSuffix()",
- w: "this.getDay()",
- z: "this.getDayOfYear()",
- W: "Ext.util.Format.leftPad(this.getWeekOfYear(), 2, '0')",
- F: "Date.monthNames[this.getMonth()]",
- m: "Ext.util.Format.leftPad(this.getMonth() + 1, 2, '0')",
- M: "Date.getShortMonthName(this.getMonth())",
- // get localised short month name
- n: "(this.getMonth() + 1)",
- 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: "('' + this.getFullYear()).substring(2, 4)",
- a: "(this.getHours() < 12 ? 'am' : 'pm')",
- A: "(this.getHours() < 12 ? 'AM' : 'PM')",
- g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
- G: "this.getHours()",
- h: "Ext.util.Format.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
- H: "Ext.util.Format.leftPad(this.getHours(), 2, '0')",
- i: "Ext.util.Format.leftPad(this.getMinutes(), 2, '0')",
- s: "Ext.util.Format.leftPad(this.getSeconds(), 2, '0')",
- u: "Ext.util.Format.leftPad(this.getMilliseconds(), 3, '0')",
- O: "this.getGMTOffset()",
- P: "this.getGMTOffset(true)",
- T: "this.getTimezone()",
- Z: "(this.getTimezoneOffset() * -60)",
-
- c: function() {
- // ISO-8601 -- GMT format
- for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
- var e = c.charAt(i);
- code.push(e == "T" ? "'T'": Date.getFormatCode(e));
- // treat T as a character literal
- }
- return code.join(" + ");
- },
- /*
- c: function() { // ISO-8601 -- UTC format
- return [
- "this.getUTCFullYear()", "'-'",
- "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
- "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
- "'T'",
- "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
- "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
- "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
- "'Z'"
- ].join(" + ");
- },
- */
-
- U: "Math.round(this.getTime() / 1000)"
- },
-
- /**
- * Checks if the passed Date parameters will cause a javascript Date "rollover".
- * @param {Number} year 4-digit year
- * @param {Number} month 1-based month-of-year
- * @param {Number} day Day of month
- * @param {Number} hour (optional) Hour
- * @param {Number} minute (optional) Minute
- * @param {Number} second (optional) Second
- * @param {Number} millisecond (optional) Millisecond
- * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
- * @static
- */
- isValid: function(y, m, d, h, i, s, ms) {
- // setup defaults
- h = h || 0;
- i = i || 0;
- s = s || 0;
- ms = ms || 0;
-
- var dt = new Date(y, m - 1, d, h, i, s, ms);
-
- return y == dt.getFullYear() &&
- m == dt.getMonth() + 1 &&
- d == dt.getDate() &&
- h == dt.getHours() &&
- i == dt.getMinutes() &&
- s == dt.getSeconds() &&
- ms == dt.getMilliseconds();
- },
-
- /**
- * Parses the passed string using the specified date format.
- * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
- * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
- * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
- * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
- * Keep in mind that the input date string must precisely match the specified format string
- * in order for the parse operation to be successful (failed parse operations return a null value).
- * <p>Example:</p><pre><code>
-//dt = Fri May 25 2007 (current date)
-var dt = new Date();
-
-//dt = Thu May 25 2006 (today's month/day in 2006)
-dt = Date.parseDate("2006", "Y");
-
-//dt = Sun Jan 15 2006 (all date parts specified)
-dt = Date.parseDate("2006-01-15", "Y-m-d");
-
-//dt = Sun Jan 15 2006 15:20:01
-dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
-
-// attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
-dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
-</code></pre>
- * @param {String} input The raw date string.
- * @param {String} format The expected date string format.
- * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
- (defaults to false). Invalid date strings will return null when parsed.
- * @return {Date} The parsed Date.
- * @static
- */
- parseDate: function(input, format, strict) {
- var p = Date.parseFunctions;
- if (p[format] == null) {
- Date.createParser(format);
- }
- return p[format](input, Ext.isDefined(strict) ? strict: Date.useStrict);
- },
-
- // private
- getFormatCode: function(character) {
- var f = Date.formatCodes[character];
-
- if (f) {
- f = typeof f == 'function' ? f() : f;
- Date.formatCodes[character] = f;
- // reassign function result to prevent repeated execution
- }
-
- // note: unknown characters are treated as literals
- return f || ("'" + Ext.util.Format.escape(character) + "'");
- },
-
- // private
- createFormat: function(format) {
- var code = [],
- special = false,
- ch = '';
-
- for (var i = 0; i < format.length; ++i) {
- ch = format.charAt(i);
- if (!special && ch == "\\") {
- special = true;
- } else if (special) {
- special = false;
- code.push("'" + Ext.util.Format.escape(ch) + "'");
- } else {
- code.push(Date.getFormatCode(ch));
- }
- }
- Date.formatFunctions[format] = new Function("return " + code.join('+'));
- },
-
- // private
- createParser: function() {
- var code = [
- "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
- "def = Date.defaults,",
- "results = String(input).match(Date.parseRegexes[{0}]);",
- // either null, or an array of matched strings
- "if(results){",
- "{1}",
-
- "if(u != null){",
- // i.e. unix time is defined
- "v = new Date(u * 1000);",
- // give top priority to UNIX time
- "}else{",
- // create Date object representing midnight of the current day;
- // this will provide us with our date defaults
- // (note: clearTime() handles Daylight Saving Time automatically)
- "dt = (new Date()).clearTime();",
-
- // date calculations (note: these calculations create a dependency on Ext.num())
- "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
- "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
- "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
-
- // time calculations (note: these calculations create a dependency on Ext.num())
- "h = Ext.num(h, Ext.num(def.h, dt.getHours()));",
- "i = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
- "s = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
- "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
-
- "if(z >= 0 && y >= 0){",
- // both the year and zero-based day of year are defined and >= 0.
- // 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);",
-
- // 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);",
- "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){",
- // check for Date "rollover"
- "v = null;",
- // invalid date, so return null
- "}else{",
- // plain old Date object
- "v = new Date(y, m, d, h, i, s, ms);",
- "}",
- "}",
- "}",
-
- "if(v){",
- // favour UTC offset over GMT offset
- "if(zz != null){",
- // reset to UTC, then add offset
- "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
- "}else if(o){",
- // reset to GMT, then add offset
- "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
- "}",
- "}",
-
- "return v;"
- ].join('\n');
-
- return function(format) {
- var regexNum = Date.parseRegexes.length,
- currentGroup = 1,
- calc = [],
- regex = [],
- special = false,
- ch = "",
- i = 0,
- obj,
- last;
-
- for (; i < format.length; ++i) {
- ch = format.charAt(i);
- if (!special && ch == "\\") {
- special = true;
- } else if (special) {
- special = false;
- regex.push(Ext.util.Format.escape(ch));
- } else {
- obj = $f(ch, currentGroup);
- currentGroup += obj.g;
- regex.push(obj.s);
- if (obj.g && obj.c) {
- if (obj.last) {
- last = obj;
- } else {
- calc.push(obj.c);
- }
- }
- }
- }
-
- if (last) {
- calc.push(last);
- }
-
- Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
- Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
- };
- }(),
-
- // private
- parseCodes: {
- /*
- * Notes:
- * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
- * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
- * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
- */
- d: {
- g: 1,
- c: "d = parseInt(results[{0}], 10);\n",
- s: "(\\d{2})"
- // day of month with leading zeroes (01 - 31)
- },
- j: {
- g: 1,
- c: "d = parseInt(results[{0}], 10);\n",
- s: "(\\d{1,2})"
- // day of month without leading zeroes (1 - 31)
- },
- D: function() {
- for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i);
- // get localised short day names
- return {
- g: 0,
- c: null,
- s: "(?:" + a.join("|") + ")"
- };
- },
- l: function() {
- return {
- g: 0,
- c: null,
- s: "(?:" + Date.dayNames.join("|") + ")"
- };
- },
- N: {
- g: 0,
- c: null,
- s: "[1-7]"
- // ISO-8601 day number (1 (monday) - 7 (sunday))
- },
- S: {
- g: 0,
- c: null,
- s: "(?:st|nd|rd|th)"
- },
- w: {
- g: 0,
- c: null,
- s: "[0-6]"
- // javascript day number (0 (sunday) - 6 (saturday))
- },
- z: {
- g: 1,
- c: "z = parseInt(results[{0}], 10);\n",
- s: "(\\d{1,3})"
- // day of the year (0 - 364 (365 in leap years))
- },
- W: {
- g: 0,
- c: null,
- s: "(?:\\d{2})"
- // ISO-8601 week number (with leading zero)
- },
- F: function() {
- return {
- 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
- return Ext.applyIf({
- s: "(" + a.join("|") + ")"
- },
- $f("F"));
- },
- m: {
- g: 1,
- c: "m = parseInt(results[{0}], 10) - 1;\n",
- s: "(\\d{2})"
- // month number with leading zeros (01 - 12)
- },
- n: {
- g: 1,
- c: "m = parseInt(results[{0}], 10) - 1;\n",
- s: "(\\d{1,2})"
- // month number without leading zeros (1 - 12)
- },
- t: {
- g: 0,
- c: null,
- s: "(?:\\d{2})"
- // no. of days in the month (28 - 31)
- },
- L: {
- g: 0,
- c: null,
- s: "(?:1|0)"
- },
- o: function() {
- return $f("Y");
- },
- Y: {
- g: 1,
- c: "y = parseInt(results[{0}], 10);\n",
- s: "(\\d{4})"
- // 4-digit year
- },
- y: {
- g: 1,
- c: "var ty = parseInt(results[{0}], 10);\n"
- + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
- // 2-digit year
- s: "(\\d{1,2})"
- },
- a: function(){
- return $f("A");
- },
- A: {
- // We need to calculate the hour before we apply AM/PM when parsing
- calcLast: true,
- g: 1,
- c: "if (results[{0}] == 'AM') {\n"
- + "if (!h || h == 12) { h = 0; }\n"
- + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
- s: "(AM|PM)"
- },
- g: function() {
- return $f("G");
- },
- G: {
- g: 1,
- c: "h = parseInt(results[{0}], 10);\n",
- s: "(\\d{1,2})"
- // 24-hr format of an hour without leading zeroes (0 - 23)
- },
- h: function() {
- return $f("H");
- },
- H: {
- g: 1,
- c: "h = parseInt(results[{0}], 10);\n",
- s: "(\\d{2})"
- // 24-hr format of an hour with leading zeroes (00 - 23)
- },
- i: {
- g: 1,
- c: "i = parseInt(results[{0}], 10);\n",
- s: "(\\d{2})"
- // minutes with leading zeros (00 - 59)
- },
- s: {
- g: 1,
- c: "s = parseInt(results[{0}], 10);\n",
- s: "(\\d{2})"
- // seconds with leading zeros (00 - 59)
- },
- u: {
- g: 1,
- c: "ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
- s: "(\\d+)"
- // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
- },
- O: {
- g: 1,
- c: [
- "o = results[{0}];",
- "var sn = o.substring(0,1),",
- // get + / - sign
- "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),",
- // get hours (performs minutes-to-hour conversion also, just in case)
- "mn = o.substring(3,5) % 60;",
- // get minutes
- "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.util.Format.leftPad(hr, 2, '0') + Ext.util.Format.leftPad(mn, 2, '0')) : null;\n"
- // -12hrs <= GMT offset <= 14hrs
- ].join("\n"),
- s: "([+\-]\\d{4})"
- // GMT offset in hrs and mins
- },
- P: {
- g: 1,
- c: [
- "o = results[{0}];",
- "var sn = o.substring(0,1),",
- // get + / - sign
- "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),",
- // get hours (performs minutes-to-hour conversion also, just in case)
- "mn = o.substring(4,6) % 60;",
- // get minutes
- "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.util.Format.leftPad(hr, 2, '0') + Ext.util.Format.leftPad(mn, 2, '0')) : null;\n"
- // -12hrs <= GMT offset <= 14hrs
- ].join("\n"),
- s: "([+\-]\\d{2}:\\d{2})"
- // GMT offset in hrs and mins (with colon separator)
- },
- T: {
- g: 0,
- c: null,
- s: "[A-Z]{1,4}"
- // timezone abbrev. may be between 1 - 4 chars
- },
- Z: {
- g: 1,
- c: "zz = results[{0}] * 1;\n"
- // -43200 <= UTC offset <= 50400
- + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
- s: "([+\-]?\\d{1,5})"
- // leading '+' sign is optional for UTC offset
- },
- c: function() {
- var calc = [],
- arr = [
- $f("Y", 1),
- // year
- $f("m", 2),
- // month
- $f("d", 3),
- // day
- $f("h", 4),
- // hour
- $f("i", 5),
- // minute
- $f("s", 6),
- // second
- {
- c: "ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"
- },
- // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
- {
- c: [
- // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
- "if(results[8]) {",
- // timezone specified
- "if(results[8] == 'Z'){",
- "zz = 0;",
- // UTC
- "}else if (results[8].indexOf(':') > -1){",
- $f("P", 8).c,
- // timezone offset with colon separator
- "}else{",
- $f("O", 8).c,
- // timezone offset without colon separator
- "}",
- "}"
- ].join('\n')
- }
- ];
-
- for (var i = 0, l = arr.length; i < l; ++i) {
- calc.push(arr[i].c);
- }
-
- return {
- g: 1,
- c: calc.join(""),
- s: [
- arr[0].s,
- // year (required)
- "(?:", "-", arr[1].s,
- // month (optional)
- "(?:", "-", arr[2].s,
- // day (optional)
- "(?:",
- "(?:T| )?",
- // time delimiter -- either a "T" or a single blank space
- arr[3].s, ":", arr[4].s,
- // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
- "(?::", arr[5].s, ")?",
- // seconds (optional)
- "(?:(?:\\.|,)(\\d+))?",
- // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
- "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?",
- // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
- ")?",
- ")?",
- ")?"
- ].join("")
- };
- },
- U: {
- g: 1,
- c: "u = parseInt(results[{0}], 10);\n",
- s: "(-?\\d+)"
- // leading minus sign indicates seconds before UNIX epoch
- }
- }
- });
-
-} ());
-
-Ext.apply(Date.prototype, {
- // private
- dateFormat: function(format) {
- if (Date.formatFunctions[format] == null) {
- Date.createFormat(format);
- }
- return Date.formatFunctions[format].call(this);
- },
-
- /**
- * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
- *
- * Note: The date string returned by the javascript Date object's toString() method varies
- * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
- * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
- * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
- * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
- * from the GMT offset portion of the date string.
- * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
- */
- getTimezone: function() {
- // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
- //
- // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
- // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
- // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
- // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
- // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
- //
- // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
- // step 1: (?:\((.*)\) -- find timezone in parentheses
- // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
- // step 3: remove all non uppercase characters found in step 1 and 2
- return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
- },
-
- /**
- * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
- * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
- * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
- */
- getGMTOffset: function(colon) {
- return (this.getTimezoneOffset() > 0 ? "-": "+")
- + Ext.util.Format.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
- + (colon ? ":": "")
- + Ext.util.Format.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
- },
-
- /**
- * Get the numeric day number of the year, adjusted for leap year.
- * @return {Number} 0 to 364 (365 in leap years).
- */
- getDayOfYear: function() {
- var num = 0,
- d = this.clone(),
- m = this.getMonth(),
- i;
-
- for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
- num += d.getDaysInMonth();
- }
- return num + this.getDate() - 1;
- },
-
- /**
- * Get the numeric ISO-8601 week number of the year.
- * (equivalent to the format specifier 'W', but without a leading zero).
- * @return {Number} 1 to 53
- */
- getWeekOfYear: function() {
- // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
- var ms1d = 864e5,
- // milliseconds in a day
- ms7d = 7 * ms1d;
- // milliseconds in a week
- return function() {
- // return a closure so constants get calculated only once
- var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d,
- // an Absolute Day Number
- AWN = Math.floor(DC3 / 7),
- // an Absolute Week Number
- Wyr = new Date(AWN * ms7d).getUTCFullYear();
-
- return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
- };
- }(),
-
- /**
- * Checks if the current date falls within a leap year.
- * @return {Boolean} True if the current date falls within a leap year, false otherwise.
- */
- isLeapYear: function() {
- var year = this.getFullYear();
- return !! ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
- },
-
- /**
- * Get the first day of the current month, adjusted for leap year. The returned value
- * is the numeric day index within the week (0-6) which can be used in conjunction with
- * the {@link #monthNames} array to retrieve the textual day name.
- * Example:
- * <pre><code>
-var dt = new Date('1/10/2007');
-document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
-</code></pre>
- * @return {Number} The day number (0-6).
- */
- getFirstDayOfMonth: function() {
- var day = (this.getDay() - (this.getDate() - 1)) % 7;
- return (day < 0) ? (day + 7) : day;
- },
-
- /**
- * Get the last day of the current month, adjusted for leap year. The returned value
- * is the numeric day index within the week (0-6) which can be used in conjunction with
- * the {@link #monthNames} array to retrieve the textual day name.
- * Example:
- * <pre><code>
-var dt = new Date('1/10/2007');
-document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
-</code></pre>
- * @return {Number} The day number (0-6).
- */
- getLastDayOfMonth: function() {
- return this.getLastDateOfMonth().getDay();
- },
-
-
- /**
- * Get the date of the first day of the month in which this date resides.
- * @return {Date}
- */
- getFirstDateOfMonth: function() {
- return new Date(this.getFullYear(), this.getMonth(), 1);
- },
-
- /**
- * Get the date of the last day of the month in which this date resides.
- * @return {Date}
- */
- getLastDateOfMonth: function() {
- return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
- },
-
- /**
- * Get the number of days in the current month, adjusted for leap year.
- * @return {Number} The number of days in the month.
- */
- getDaysInMonth: function() {
- var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
-
- return function() {
- // return a closure for efficiency
- var m = this.getMonth();
-
- return m == 1 && this.isLeapYear() ? 29: daysInMonth[m];
- };
- }(),
-
- /**
- * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
- * @return {String} 'st, 'nd', 'rd' or 'th'.
- */
- getSuffix: function() {
- switch (this.getDate()) {
- case 1:
- case 21:
- case 31:
- return "st";
- case 2:
- case 22:
- return "nd";
- case 3:
- case 23:
- return "rd";
- default:
- return "th";
- }
- },
-
- /**
- * Creates and returns a new Date instance with the exact same date value as the called instance.
- * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
- * variable will also be changed. When the intention is to create a new variable that will not
- * modify the original instance, you should create a clone.
- *
- * Example of correctly cloning a date:
- * <pre><code>
-//wrong way:
-var orig = new Date('10/1/2006');
-var copy = orig;
-copy.setDate(5);
-document.write(orig); //returns 'Thu Oct 05 2006'!
-
-//correct way:
-var orig = new Date('10/1/2006');
-var copy = orig.clone();
-copy.setDate(5);
-document.write(orig); //returns 'Thu Oct 01 2006'
-</code></pre>
- * @return {Date} The new Date instance.
- */
- clone: function() {
- return new Date(this.getTime());
- },
-
- /**
- * Checks if the current date is affected by Daylight Saving Time (DST).
- * @return {Boolean} True if the current date is affected by DST.
- */
- isDST: function() {
- // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
- // courtesy of @geoffrey.mcgill
- return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
- },
-
- /**
- * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
- * automatically adjusting for Daylight Saving Time (DST) where applicable.
- * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
- * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
- * @return {Date} this or the clone.
- */
- clearTime: function(clone) {
- if (clone) {
- return this.clone().clearTime();
- }
-
- // get current date before clearing time
- var d = this.getDate();
-
- // clear time
- this.setHours(0);
- this.setMinutes(0);
- this.setSeconds(0);
- this.setMilliseconds(0);
-
- if (this.getDate() != d) {
- // account for DST (i.e. day of month changed when setting hour = 0)
- // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
- // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
- // increment hour until cloned date == current date
- for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
-
- this.setDate(d);
- this.setHours(c.getHours());
- }
-
- return this;
- },
-
- /**
- * Provides a convenient method for performing basic date arithmetic. This method
- * does not modify the Date instance being called - it creates and returns
- * a new Date instance containing the resulting date value.
- *
- * Examples:
- * <pre><code>
-// Basic usage:
-var dt = new Date('10/29/2006').add(Date.DAY, 5);
-document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
-
-// Negative values will be subtracted:
-var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
-document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
-
-// You can even chain several calls together in one line:
-var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
-document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
-</code></pre>
- *
- * @param {String} interval A valid date interval enum value.
- * @param {Number} value The amount to add to the current date.
- * @return {Date} The new Date instance.
- */
- add: function(interval, value) {
- var d = this.clone();
- if (!interval || value === 0) return d;
-
- switch (interval.toLowerCase()) {
- case Date.MILLI:
- d.setMilliseconds(this.getMilliseconds() + value);
- break;
- case Date.SECOND:
- d.setSeconds(this.getSeconds() + value);
- break;
- case Date.MINUTE:
- d.setMinutes(this.getMinutes() + value);
- break;
- case Date.HOUR:
- d.setHours(this.getHours() + value);
- break;
- case Date.DAY:
- d.setDate(this.getDate() + value);
- break;
- case Date.MONTH:
- var day = this.getDate();
- if (day > 28) {
- day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
- }
- d.setDate(day);
- d.setMonth(this.getMonth() + value);
- break;
- case Date.YEAR:
- d.setFullYear(this.getFullYear() + value);
- break;
- }
- return d;
- },
-
- /**
- * Checks if this date falls on or between the given start and end dates.
- * @param {Date} start Start date
- * @param {Date} end End date
- * @return {Boolean} true if this date falls on or between the given start and end dates.
- */
- between: function(start, end) {
- var t = this.getTime();
- return start.getTime() <= t && t <= end.getTime();
- }
-});
-
-
-/**
- * Formats a date given the supplied format string.
- * @param {String} format The format string.
- * @return {String} The formatted date.
- * @method format
- */
-Date.prototype.format = Date.prototype.dateFormat;
-
-/* Some basic Date tests... (requires Firebug)
-
-Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
-console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
-
-// standard tests
-console.group('Standard Date.parseDate() Tests');
- console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
- console.log('Date.parseDate("2009-02-04T12:37:55.001000", "c") = %o', Date.parseDate("2009-02-04T12:37:55.001000", "c")); // assumes browser's timezone setting
- console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
- console.log('Date.parseDate("2009-04-02T14:35:53.901000-0530", "c") = %o', Date.parseDate("2009-04-02T14:35:53.901000-0530", "c")); // GMT-0530
- console.log('Date.parseDate("2009-05-01T15:34:52,9876000+08:00", "c") = %o', Date.parseDate("2009-05-01T15:34:52,987600+08:00", "c")); // GMT+08:00
-console.groupEnd();
-
-// ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
-// -- accepts ALL 6 levels of date-time granularity
-console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
- console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
- console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
- console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
- console.log('Date.parseDate("1997-07-16T19:20+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20+01:00", "c")); // YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00)
- console.log('Date.parseDate("1997-07-16T19:20:30+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20:30+01:00", "c")); // YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:30+01:00)
- console.log('Date.parseDate("1997-07-16T19:20:30.45+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20:30.45+01:00", "c")); // YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
- console.log('Date.parseDate("1997-07-16 19:20:30.45+01:00", "c") = %o', Date.parseDate("1997-07-16 19:20:30.45+01:00", "c")); // YYYY-MM-DD hh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
- 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.data.Connection
- * @extends Ext.util.Observable
- */
-Ext.data.Connection = Ext.extend(Ext.util.Observable, {
- method: 'post',
- url: null,
-
- /**
- * @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',
-
- /**
- * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
- */
- timeout : 30000,
-
- useDefaultHeader : true,
- defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
- useDefaultXhrHeader : true,
- defaultXhrHeader : 'XMLHttpRequest',
-
- constructor : function(config) {
- config = config || {};
- Ext.apply(this, config);
-
- this.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 <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
- * 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 <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
- * for details of HTTP status codes.
- * @param {Connection} conn This Connection object.
- * @param {Object} response The XHR object containing the response data.
- * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
- * for details.
- * @param {Object} options The options config object passed to the {@link #request} method.
- */
- 'requestexception'
- );
- this.requests = {};
- Ext.data.Connection.superclass.constructor.call(this);
- },
-
- /**
- * <p>Sends an HTTP request to a remote server.</p>
- * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
- * return before the response has been received. Process any returned data
- * in a callback function.</p>
- * <pre><code>
-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);
-}
-});
- * </code></pre>
- * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
- * @param {Object} options An object which may contain the following properties:<ul>
- * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">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 <tt>scope</tt> option. Defaults to the configured
- * <tt>{@link #url}</tt>.</div></li>
- * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
- * 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 <tt>scope</tt> option.</div></li>
- * <li><b>method</b> : String (Optional)<div class="sub-desc">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.</div></li>
- * <li><b>callback</b> : Function (Optional)<div class="sub-desc">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:<ul>
- * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
- * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
- * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
- * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
- * accessing elements of the response.</div></li>
- * </ul></div></li>
- * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
- * to be called upon success of the request. The callback is passed the following
- * parameters:<ul>
- * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
- * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
- * </ul></div></li>
- * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
- * to be called upon failure of the request. The callback is passed the
- * following parameters:<ul>
- * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
- * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
- * </ul></div></li>
- * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
- * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> 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.</div></li>
- * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>
- * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt><form></tt>
- * Element or the id of the <tt><form></tt> to pull parameters from.</div></li>
- * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
- * with the <tt>form</tt> option</b>.
- * <p>True if the form object is a file upload (will be set automatically if the form was
- * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
- * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
- * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
- * DOM <tt><form></tt> element temporarily modified to have its
- * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
- * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
- * but removed after the return data has been gathered.</p>
- * <p>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
- * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
- * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
- * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
- * is created containing a <tt>responseText</tt> property in order to conform to the
- * requirements of event handlers and callbacks.</p>
- * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
- * and some server technologies (notably JEE) may require some custom processing in order to
- * retrieve parameter names and parameter values from the packet content.</p>
- * </div></li>
- * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
- * headers to set for the request.</div></li>
- * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">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.</div></li>
- * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">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.</div></li>
- * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
- * to add a unique cache-buster param to GET requests.</div></li>
- * </ul></p>
- * <p>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.</p>
- * @return {Object} request The request object. This may be used
- * to cancel the request.
- */
- request : function(o) {
- var me = this;
- if (me.fireEvent('beforerequest', me, o) !== false) {
- var params = o.params,
- url = o.url || me.url,
- urlParams = o.urlParams,
- extraParams = me.extraParams,
- request, data, headers,
- method, key, xhr;
-
- // allow params to be a method that returns the params object
- if (Ext.isFunction(params)) {
- params = params.call(o.scope || window, o);
- }
-
- // allow url to be a method that returns the actual url
- if (Ext.isFunction(url)) {
- url = url.call(o.scope || window, o);
- }
-
- // check for xml or json data, and make sure json data is encoded
- data = o.rawData || o.xmlData || o.jsonData || null;
- if (o.jsonData && !Ext.isPrimitive(o.jsonData)) {
- data = Ext.encode(data);
- }
-
- // make sure params are a url encoded string and include any extraParams if specified
- params = Ext.urlEncode(extraParams, Ext.isObject(params) ? Ext.urlEncode(params) : params);
-
- urlParams = Ext.isObject(urlParams) ? Ext.urlEncode(urlParams) : urlParams;
-
- // decide the proper method for this request
- method = (o.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
-
- // if the method is get append date to prevent caching
- if (method === 'GET' && o.disableCaching !== false && me.disableCaching) {
- url = Ext.urlAppend(url, o.disableCachingParam || me.disableCachingParam + '=' + (new Date().getTime()));
- }
-
- // if the method is get or there is json/xml data append the params to the url
- if ((method == 'GET' || data) && params){
- url = Ext.urlAppend(url, params);
- params = null;
- }
-
- // allow params to be forced into the url
- if (urlParams) {
- url = Ext.urlAppend(url, urlParams);
- }
-
- // if autoabort is set, cancel the current transactions
- if (o.autoAbort === true || me.autoAbort) {
- me.abort();
- }
-
- // create a connection object
- xhr = this.getXhrInstance();
-
- // open the request
- xhr.open(method.toUpperCase(), url, true);
-
- // create all the headers
- headers = Ext.apply({}, o.headers || {}, me.defaultHeaders || {});
- if (!headers['Content-Type'] && (data || params)) {
- var contentType = me.defaultPostHeader,
- jsonData = o.jsonData,
- xmlData = o.xmlData;
-
- if (data) {
- if (o.rawData) {
- contentType = 'text/plain';
- } else {
- if (xmlData && Ext.isDefined(xmlData)) {
- contentType = 'text/xml';
- } else if (jsonData && Ext.isDefined(jsonData)) {
- contentType = 'application/json';
- }
- }
- }
- headers['Content-Type'] = contentType;
- }
- if (me.useDefaultXhrHeader && !headers['X-Requested-With']) {
- headers['X-Requested-With'] = me.defaultXhrHeader;
- }
- // set up all the request headers on the xhr object
- for (key in headers) {
- if (headers.hasOwnProperty(key)) {
- try {
- xhr.setRequestHeader(key, headers[key]);
- }
- catch(e) {
- me.fireEvent('exception', key, headers[key]);
- }
- }
- }
-
- // create the transaction object
- request = {
- id: ++Ext.data.Connection.requestId,
- xhr: xhr,
- headers: headers,
- options: o,
- timeout: setTimeout(function() {
- request.timedout = true;
- me.abort(request);
- }, o.timeout || me.timeout)
- };
- me.requests[request.id] = request;
-
- // bind our statechange listener
- xhr.onreadystatechange = Ext.createDelegate(me.onStateChange, me, [request]);
-
- // start the request!
- xhr.send(data || params || null);
- return request;
- } else {
- return o.callback ? o.callback.apply(o.scope, [o, undefined, undefined]) : null;
- }
- },
-
- getXhrInstance : function() {
- return new XMLHttpRequest();
- },
-
- /**
- * Determine whether this object has a request outstanding.
- * @param {Object} request (Optional) defaults to the last transaction
- * @return {Boolean} True if there is an outstanding request.
- */
- isLoading : function(r) {
- // if there is a connection and readyState is not 0 or 4
- return r && !{0:true, 4:true}[r.xhr.readyState];
- },
-
- /**
- * Aborts any outstanding request.
- * @param {Object} request (Optional) defaults to the last request
- */
- abort : function(r) {
- if (r && this.isLoading(r)) {
- r.xhr.abort();
- clearTimeout(r.timeout);
- delete(r.timeout);
- r.aborted = true;
- this.onComplete(r);
- }
- else if (!r) {
- var id;
- for(id in this.requests) {
- if (!this.requests.hasOwnProperty(id)) {
- continue;
- }
- this.abort(this.requests[id]);
- }
- }
- },
-
- // private
- onStateChange : function(r) {
- if (r.xhr.readyState == 4) {
- clearTimeout(r.timeout);
- delete r.timeout;
- this.onComplete(r);
- }
- },
-
- // private
- onComplete : function(r) {
- var status = r.xhr.status,
- options = r.options,
- success = true,
- response;
-
- if ((status >= 200 && status < 300) || status == 304) {
- response = this.createResponse(r);
- this.fireEvent('requestcomplete', this, response, options);
- if (options.success) {
- if (!options.scope) {
- options.success(response, options);
- }
- else {
- options.success.call(options.scope, response, options);
- }
- }
- }
- else {
- success = false;
- switch (status) {
- case 12002:
- case 12029:
- case 12030:
- case 12031:
- case 12152:
- case 13030:
- response = this.createException(r);
- break;
- default:
- response = this.createResponse(r);
- }
- this.fireEvent('requestexception', this, response, options);
- if (options.failure) {
- if (!options.scope) {
- options.failure(response, options);
- }
- else {
- options.failure.call(options.scope, response, options);
- }
- }
- }
-
- if (options.callback) {
- if (!options.scope) {
- options.callback(options, success, response);
- }
- else {
- options.callback.call(options.scope, options, success, response);
- }
- }
-
- delete this.requests[r.id];
- },
-
- // private
- createResponse : function(r) {
- var xhr = r.xhr,
- headers = {},
- lines = xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'),
- count = lines.length,
- line, index, key, value;
-
- while (count--) {
- line = lines[count];
- index = line.indexOf(':');
- if(index >= 0) {
- key = line.substr(0, index).toLowerCase();
- if (line.charAt(index + 1) == ' ') {
- ++index;
- }
- headers[key] = line.substr(index + 1);
- }
- }
-
- delete r.xhr;
-
- return {
- request: r,
- requestId : r.id,
- status : xhr.status,
- statusText : xhr.statusText,
- getResponseHeader : function(header){ return headers[header.toLowerCase()]; },
- getAllResponseHeaders : function(){ return headers; },
- responseText : xhr.responseText,
- responseXML : xhr.responseXML
- };
- },
-
- // private
- createException : function(r) {
- return {
- request : r,
- requestId : r.id,
- status : r.aborted ? -1 : 0,
- statusText : r.aborted ? 'transaction aborted' : 'communication failure',
- aborted: r.aborted,
- timedout: r.timedout
- };
- }
-});
-
-Ext.data.Connection.requestId = 0;
-
-/**
- * @class Ext.Ajax
- * @extends Ext.data.Connection
- * A singleton instance of an {@link Ext.data.Connection}.
- * @singleton
- */
-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
- */
- /**
- * @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
- */
- /**
- * @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
- * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
- * @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
-});
-
-// This class is still experimental, docs will be added at a later time
-Ext.util.EventSimulator = Ext.extend(Object, {
-
- supportedEvents: {
- touch: ['touchstart', 'touchmove', 'touchend', 'gesturestart', 'gesturechange', 'gestureend'],
- mouse: ['mousedown', 'mousemove', 'mouseup', 'click']
- },
-
- getEventTypeByName: function(name) {
- var ret = null;
-
- Ext.iterate(this.supportedEvents, function(type, events) {
- if (events.indexOf(name) != -1)
- ret = type;
- });
-
- return ret;
- },
-
- fire: function(type, target, options) {
- type = type.toLowerCase();
-
- if (arguments.length == 2) {
- options = target;
- target = document;
- }
-
- switch(this.getEventTypeByName(type)) {
- case 'touch':
- this.fireTouchEvent.call(this, type, target, options);
- break;
-
- case 'mouse':
- this.fireMouseEvent.call(this, type, target, options);
- break;
-
- default:
- throw new Error("Event type " + type + " is currently not supported");
- }
-
- return this;
- },
-
- createEvent: function(data, serializable) {
-
- },
-
- createEventData: function(event, serializable) {
- switch (this.getEventTypeByName(event.type)) {
- case 'touch':
- return this.createTouchEventData(event.type, event.target, event, serializable);
- break;
-
- case 'mouse':
- return this.createMouseEventData(event.type, event.target, event, serializable);
- break;
-
- default:
- throw new Error("Event type " + event.type + " is currently not supported");
- }
- },
-
- /* Touch events ========================================================================== */
-
- fireTouchEvent: function(type, target, options) {
- var touchEventData = this.createTouchEventData(type, target, options);
-
- var touchEvent = this.createTouchEvent(type, touchEventData);
- touchEvent.isSimulated = true;
-
- return target.dispatchEvent(touchEvent);
- },
-
- createTouchEventData: function(type, target, options, serializable) {
- var touchEventData = {
- type: type,
- timeStamp: Date.now(),
- bubbles: true,
- cancelable: true,
- detail: 1, // Not sure what this does in "touch" event.
- screenX: 0,
- screenY: 0,
- pageX: 0,
- pageY: 0,
- clientX: 0,
- clientY: 0,
- ctrlKey: false,
- altKey: false,
- shiftKey: false,
- metaKey: false,
- scale: 1,
- rotation: 0
- };
-
- if (!serializable) {
- touchEventData.target = target;
- touchEventData.view = document.defaultView;
- }
-
- if (options) {
- Ext.iterate(touchEventData, function(key, value) {
- if (options.hasOwnProperty(key)) {
- touchEventData[key] = options[key];
- }
- });
- }
-
- ['touches', 'targetTouches', 'changedTouches'].forEach(function(touchListName) {
- if (options.hasOwnProperty(touchListName)) {
- touchEventData[touchListName] = this.createTouchList(options[touchListName], target, serializable);
- }
- else {
- touchEventData[touchListName] = this.createTouchList(touchEventData, target, serializable);
- }
- }, this);
-
- return touchEventData;
- },
-
- createTouchEvent: function(type, data) {
- if (typeof type != 'string') {
- data = type;
- type = type.type;
- }
-
- var touchEvent = document.createEvent('TouchEvent');
-
- if (touchEvent.initTouchEvent.length == 9) {
- touchEvent.initTouchEvent(data.touches, data.targetTouches, data.changedTouches,
- type, data.view, data.screenX, data.screenY, data.clientX, data.clientY);
-
- } else {
- touchEvent.initTouchEvent(type, data.bubbles, data.cancelable, data.view,
- data.detail, data.screenX, data.screenY, data.pageX, data.pageY, data.ctrlKey,
- data.altKey, data.shiftKey, data.metaKey, data.touches, data.targetTouches,
- data.changedTouches, data.scale, data.rotation);
- }
-
- return touchEvent;
- },
-
- createTouch: function(target, options, serializable) {
- if (!document.createTouch || serializable) {
- return {
- pageX: options.pageX,
- pageY: options.pageY,
- clientX: options.pageX,
- clientY: options.pageY,
- screenX: options.pageX,
- screenY: options.pageY,
- identifier: +options.identifier || 0
- };
- }
-
- return document.createTouch(
- document.defaultView,
- target,
- +options.identifier || 0,
- +options.pageX || 0,
- +options.pageY || 0,
- +options.screenX || 0,
- +options.screenY || 0);
- },
-
- createTouchList: function(data, target, serializable) {
- var touch,
- touches = [];
-
- if (Ext.isObject(data) && typeof data.target != 'undefined') {
- data = [data];
- }
-
- if (data) {
- for (var i = 0; i < data.length; i++) {
- if (!serializable && !data[i].target) {
- data[i].target = target;
- }
-
- touch = this.createTouch(data[i].target, data[i], serializable);
- touches.push(touch);
- }
- }
-
- if (!document.createTouchList || serializable) {
- return touches;
- }
-
- return document.createTouchList.apply(document, touches);
- },
-
- /* Mouse events ======================================================================================= */
-
- fireMouseEvent: function(type, target, options) {
- var eventData = this.createMouseEventData(type, target, options);
-
- var event = this.createMouseEvent(type, eventData);
- event.isSimulated = true;
- event.originalTimeStamp = eventData.timeStamp;
-
- return target.dispatchEvent(event);
- },
-
- createMouseEventData: function(type, target, options, serializable) {
- var mouseEventData = {
- type: type,
- timeStamp: Date.now(),
- bubbles: true, // all mouse events bubble
- cancelable: (type != 'mousemove'), // mousemove is the only event type that can't be cancelled
- detail: 1, // number of mouse clicks must be at least one
- screenX: 0,
- screenY: 0,
- pageX: 0,
- pageY: 0,
- clientX: 0,
- clientY: 0,
- ctrlKey: false,
- altKey: false,
- shiftKey: false,
- metaKey: false,
- button: 0,
- relatedTarget: null
- },
- coordProperties = ['screen', 'client', 'page'],
- coords = {
- x: 0,
- y: 0
- };
-
- if (!serializable) {
- mouseEventData.target = target;
- mouseEventData.view = window;
- }
-
- if (options) {
- Ext.iterate(mouseEventData, function(key, value) {
- if (options.hasOwnProperty(key)) {
- mouseEventData[key] = options[key];
- }
- });
- }
-
- coordProperties.forEach(function(p) {
- if (mouseEventData[p + 'X'] != 0) {
- coords.x = mouseEventData[p + 'X']
- }
-
- if (mouseEventData[p + 'Y'] != 0) {
- coords.y = mouseEventData[p + 'Y']
- }
- });
-
- coordProperties.forEach(function(p) {
- if (mouseEventData[p + 'X'] == 0 && coords.x != 0) {
- mouseEventData[p + 'X'] = coords.x;
- }
-
- if (mouseEventData[p + 'Y'] == 0 && coords.y != 0) {
- mouseEventData[p + 'Y'] = coords.y;
- }
- });
-
- return mouseEventData;
- },
-
- createMouseEvent: function(type, data) {
- var mouseEvent = document.createEvent('MouseEvents');
-
- mouseEvent.initMouseEvent(
- type, data.bubbles, data.cancelable, data.view, data.detail,
- data.screenX, data.screenY, data.clientX, data.clientY,
- data.ctrlKey, data.altKey, data.shiftKey, data.metaKey,
- data.button, data.relatedTarget);
-
- return mouseEvent;
- }
-
-});
-// This class is still experimental, docs will be added at a later time
-Ext.util.EventRecorder = Ext.extend(Ext.util.Observable, {
-
- eventCollection: null,
-
- currentEventSetName: null,
-
- constructor: function() {
- this.addEvents(
- 'replaystart',
- 'beforecalculatetarget',
- 'beforefire',
- 'afterfire',
- 'aftercalculatetarget',
- 'replayend',
- 'interrupted'
- );
-
- this.eventSets = {};
- this.interruptedIndexes = {};
-
- return this;
- },
-
- getEventSet: function(name) {
- if (typeof name != 'string') {
- if (this.currentEventSetName == null)
- throw new Error('No EventSet is currently used');
-
- name = this.currentEventSetName;
- }
-
- if (typeof this.eventSets[name] == 'undefined') {
- this.eventSets[name] = [];
- }
- return this.eventSets[name];
- },
-
- start: function(name) {
- this.currentEventSetName = name;
- },
-
- record: function(name, event) {
- if (typeof name != 'string') {
- // Not being recorded since either it's not started or is already ended
- if (this.currentEventSetName == null)
- return;
-
- event = name;
- name = this.currentEventSetName;
- }
-
- var eventData = this.getEventSimulator().createEventData(event, true);
-
- this.getEventSet(name).push(eventData);
- },
-
- setEventSet: function(name, events) {
- this.eventSets[name] = events;
- },
-
- erase: function(name) {
- // Empty the array
- this.getEventSet(name).length = 0;
- },
-
- stopReplay: function() {
- this.interruptFlag = true;
- },
-
- resumeReplay: function(name) {
- var index = this.interruptedIndexes[name] || 0;
- this.replay(name, index);
- },
-
- replay: function(name, startIndex) {
- var simulator = this.getEventSimulator(),
- events = this.getEventSet(name),
- numEvents,
- delay = 0,
- index = 0,
- event,
- point,
- target,
- reserveTargetEvents = ['touchmove', 'touchend', 'mousemove', 'mouseup'],
- me = this;
-
- if (typeof startIndex == 'undefined') {
- startIndex = 0;
- }
-
- index = startIndex;
-
- numEvents = events.length;
-
- this.interruptFlag = false;
-
- if (numEvents > 0) {
- this.fireEvent('replaystart', name, startIndex);
- setTimeout(function() {
- event = events[index];
-
- if (event) {
-
- if (reserveTargetEvents.indexOf(event.type) === -1) {
- me.fireEvent('beforecalculatetarget', event.type, event);
- point = Ext.util.Point.fromEvent(event);
- target = document.elementFromPoint(point.x, point.y);
- me.fireEvent('aftercalculatetarget', event.type, target, event);
- }
-
- if (target) {
- if (me.interruptFlag === true) {
- me.interruptFlag = false;
- me.interruptedIndexes[name] = index;
- me.fireEvent('interrupted', index);
- me.fireEvent('replayend', name, true);
- return;
- }
- me.fireEvent('beforefire', event.type, target, event);
- simulator.fire(event.type, target, event);
- me.fireEvent('afterfire', event.type, target, event);
- }
-
- if (++index < numEvents) {
- setTimeout(arguments.callee, events[index].timeStamp - event.timeStamp);
- } else {
- me.fireEvent('replayend', name, false);
- }
- }
- }, delay);
- }
- },
-
- end: function() {
- this.currentEventSetName = null;
- },
-
- getEventSimulator: function() {
- if (!this._eventSimulator) {
- this._eventSimulator = new Ext.util.EventSimulator();
- }
-
- return this._eventSimulator;
- },
-
- setEventSimulator: function(eventSimulator) {
- if (!(eventSimulator instanceof Ext.util.EventSimulator))
- throw new Error('eventSimulator must be an instance of Ext.util.EventSimulator');
-
- this._eventSimulator = eventSimulator;
- },
-
- // TODO
- save: function(name) {
-
- }
-});
-
-Ext.gesture.Manager = new Ext.AbstractManager({
- eventNames: {
- start: 'touchstart',
- move: 'touchmove',
- end: 'touchend'
- },
-
- defaultPreventedMouseEvents: ['click'],
-
- clickMoveThreshold: 5,
-
- init: function() {
- this.targets = [];
-
- this.followTouches = [];
- this.currentGestures = [];
- this.currentTargets = [];
-
- if (!Ext.supports.Touch) {
- Ext.apply(this.eventNames, {
- start: 'mousedown',
- move: 'mousemove',
- end: 'mouseup'
- });
- }
-
- this.listenerWrappers = {
- start: Ext.createDelegate(this.onTouchStart, this),
- move: Ext.createDelegate(this.onTouchMove, this),
- end: Ext.createDelegate(this.onTouchEnd, this),
- mouse: Ext.createDelegate(this.onMouseEvent, this)
- };
-
- this.attachListeners();
- },
-
- freeze: function() {
- this.isFrozen = true;
- },
-
- thaw: function() {
- this.isFrozen = false;
- },
-
- getEventSimulator: function() {
- if (!this.eventSimulator) {
- this.eventSimulator = new Ext.util.EventSimulator();
- }
-
- return this.eventSimulator;
- },
-
- attachListeners: function() {
- Ext.iterate(this.eventNames, function(key, name) {
- document.body.addEventListener(name, this.listenerWrappers[key], false);
- }, this);
-
- if (Ext.supports.Touch) {
- this.defaultPreventedMouseEvents.forEach(function(name) {
- document.body.addEventListener(name, this.listenerWrappers['mouse'], true);
- }, this);
- }
- },
-
- detachListeners: function() {
- Ext.iterate(this.eventNames, function(key, name) {
- document.body.removeEventListener(name, this.listenerWrappers[key], false);
- }, this);
-
- if (Ext.supports.Touch) {
- this.defaultPreventedMouseEvents.forEach(function(name) {
- document.body.removeEventListener(name, this.listenerWrappers['mouse'], true);
- }, this);
- }
- },
-
- onMouseEvent: function(e) {
- if (!e.isSimulated) {
- e.preventDefault();
- e.stopPropagation();
- }
- },
-
- onTouchStart: function(e) {
- var targets = [],
- target = e.target,
- me = this;
-
- if (e.stopped === true) {
- return;
- }
-
- if (!Ext.is.iOS) {
- if (!(target.tagName && ['input', 'textarea', 'select'].indexOf(target.tagName.toLowerCase()) !== -1)) {
- e.preventDefault();
- }
- }
-
- if (this.isFrozen) {
- return;
- }
-
- // There's already a touchstart without any touchend!
- // This used to happen on HTC Desire and HTC Incredible
- // We have to clean it up
- if (this.startEvent) {
- this.onTouchEnd(e);
- }
-
- this.locks = {};
-
- this.currentTargets = [target];
-
- while (target) {
- if (this.targets.indexOf(target) !== -1) {
- targets.unshift(target);
- }
-
- target = target.parentNode;
- this.currentTargets.push(target);
- }
-
- this.startEvent = e;
- this.startPoint = Ext.util.Point.fromEvent(e);
- this.lastMovePoint = null;
- this.isClick = true;
- this.handleTargets(targets, e);
- },
-
- onTouchMove: function(e) {
- if (!this.startEvent) {
- return;
- }
-
- if (Ext.is.Desktop) {
- e.target = this.startEvent.target;
- }
-
- if (Ext.is.iOS) {
- e.preventDefault();
- }
-
- if (this.isFrozen) {
- return;
- }
-
- var gestures = this.currentGestures,
- gesture,
- touch = e.changedTouches ? e.changedTouches[0] : e;
-
- this.lastMovePoint = Ext.util.Point.fromEvent(e);
-
- if (Ext.supports.Touch && this.isClick && !this.lastMovePoint.isWithin(this.startPoint, this.clickMoveThreshold)) {
- this.isClick = false;
- }
-
- for (var i = 0; i < gestures.length; i++) {
- if (e.stopped) {
- break;
- }
-
- gesture = gestures[i];
-
- if (gesture.listenForMove) {
- gesture.onTouchMove(e, touch);
- }
- }
- },
-
- /**
- * This listener is here to always ensure we stop all current gestures
- * @private
- */
- onTouchEnd: function(e) {
- if (this.isFrozen) {
- return;
- }
-
- var gestures = this.currentGestures.slice(0),
- ln = gestures.length,
- i, gesture, endPoint,
- needsAnotherMove = false,
- touch = e.changedTouches ? e.changedTouches[0] : e;
-
- if (this.startPoint) {
- endPoint = Ext.util.Point.fromEvent(e);
- if (!(this.lastMovePoint || this.startPoint)['equals'](endPoint)) {
- needsAnotherMove = true;
- }
- }
-
- for (i = 0; i < ln; i++) {
- gesture = gestures[i];
-
- if (!e.stopped && gesture.listenForEnd) {
- // The point has changed, we should execute another onTouchMove before onTouchEnd
- // to deal with the problem of missing events on Androids and alike
- // This significantly improves scrolling experience on Androids!
- if (needsAnotherMove) {
- gesture.onTouchMove(e, touch);
- }
-
- gesture.onTouchEnd(e, touch);
- }
-
- this.stopGesture(gesture);
- }
-
-
- if (Ext.supports.Touch && this.isClick) {
- this.isClick = false;
- this.getEventSimulator().fire('click', this.startEvent.target, touch);
- }
-
- this.lastMovePoint = null;
- this.followTouches = [];
- this.startedChangedTouch = false;
- this.currentTargets = [];
- this.startEvent = null;
- this.startPoint = null;
- },
-
- handleTargets: function(targets, e) {
- // In handle targets we have to first handle all the capture targets,
- // then all the bubble targets.
- var ln = targets.length,
- i;
-
- this.startedChangedTouch = false;
- this.startedTouches = Ext.supports.Touch ? e.touches : [e];
-
- for (i = 0; i < ln; i++) {
- if (e.stopped) {
- break;
- }
-
- this.handleTarget(targets[i], e, true);
- }
-
- for (i = ln - 1; i >= 0; i--) {
- if (e.stopped) {
- break;
- }
-
- this.handleTarget(targets[i], e, false);
- }
-
- if (this.startedChangedTouch) {
- this.followTouches = this.followTouches.concat((Ext.supports.Touch && e.targetTouches) ? Ext.toArray(e.targetTouches) : [e]);
- }
- },
-
- handleTarget: function(target, e, capture) {
- var gestures = Ext.Element.data(target, 'x-gestures') || [],
- ln = gestures.length,
- i, gesture;
-
- for (i = 0; i < ln; i++) {
- gesture = gestures[i];
- if (
- (!!gesture.capture === !!capture) &&
- (this.followTouches.length < gesture.touches) &&
- ((Ext.supports.Touch && e.targetTouches) ? (e.targetTouches.length === gesture.touches) : true)
- ) {
- this.startedChangedTouch = true;
- this.startGesture(gesture);
-
- if (gesture.listenForStart) {
- gesture.onTouchStart(e, e.changedTouches ? e.changedTouches[0] : e);
- }
-
- if (e.stopped) {
- break;
- }
- }
- }
- },
-
- startGesture: function(gesture) {
- gesture.started = true;
- this.currentGestures.push(gesture);
- },
-
- stopGesture: function(gesture) {
- gesture.started = false;
- this.currentGestures.remove(gesture);
- },
-
- addEventListener: function(target, eventName, listener, options) {
- target = Ext.getDom(target);
-
- var targets = this.targets,
- name = this.getGestureName(eventName),
- gestures = Ext.Element.data(target, 'x-gestures'),
- gesture;
-
- if (!gestures) {
- gestures = [];
- Ext.Element.data(target, 'x-gestures', gestures);
- }
-
- // <debug>
- if (!name) {
- throw new Error('Trying to subscribe to unknown event ' + eventName);
- }
- // </debug>
-
- if (targets.indexOf(target) === -1) {
- this.targets.push(target);
- }
-
- gesture = this.get(target.id + '-' + name);
-
- if (!gesture) {
- gesture = this.create(Ext.apply({}, options || {}, {
- target: target,
- type: name
- }));
-
- gestures.push(gesture);
- // The line below is not needed, Ext.Element.data(target, 'x-gestures') still reference gestures
- // Ext.Element.data(target, 'x-gestures', gestures);
- }
-
- gesture.addListener(eventName, listener);
-
- // If there is already a finger down, then instantly start the gesture
- if (this.startedChangedTouch && this.currentTargets.contains(target) && !gesture.started && !options.subsequent) {
- this.startGesture(gesture);
- if (gesture.listenForStart) {
- gesture.onTouchStart(this.startEvent, this.startedTouches[0]);
- }
- }
- },
-
- removeEventListener: function(target, eventName, listener) {
- target = Ext.getDom(target);
-
- var name = this.getGestureName(eventName),
- gestures = Ext.Element.data(target, 'x-gestures') || [],
- gesture;
-
- gesture = this.get(target.id + '-' + name);
-
- if (gesture) {
- gesture.removeListener(eventName, listener);
-
- for (name in gesture.listeners) {
- return;
- }
-
- gesture.destroy();
- gestures.remove(gesture);
- Ext.Element.data(target, 'x-gestures', gestures);
- }
- },
-
- getGestureName: function(ename) {
- return this.names && this.names[ename];
- },
-
- registerType: function(type, cls) {
- var handles = cls.prototype.handles,
- i, ln;
-
- this.types[type] = cls;
-
- cls[this.typeName] = type;
-
- if (!handles) {
- handles = cls.prototype.handles = [type];
- }
-
- this.names = this.names || {};
-
- for (i = 0, ln = handles.length; i < ln; i++) {
- this.names[handles[i]] = type;
- }
- }
-});
-
-Ext.regGesture = function() {
- return Ext.gesture.Manager.registerType.apply(Ext.gesture.Manager, arguments);
-};
-Ext.TouchEventObjectImpl = Ext.extend(Object, {
- constructor : function(e, args) {
- if (e) {
- this.setEvent(e, args);
- }
- },
-
- setEvent : function(e, args) {
- Ext.apply(this, {
- event: e,
- time: e.timeStamp
- });
-
- this.touches = e.touches || [e];
- this.changedTouches = e.changedTouches || [e];
- this.targetTouches = e.targetTouches || [e];
-
- if (args) {
- this.target = args.target;
- Ext.apply(this, args);
- }
- else {
- this.target = e.target;
- }
- return this;
- },
-
- stopEvent : function() {
- this.stopPropagation();
- this.preventDefault();
- },
-
- stopPropagation : function() {
- this.event.stopped = true;
- },
-
- preventDefault : function() {
- this.event.preventDefault();
- },
-
- getTarget : function(selector, maxDepth, returnEl) {
- if (selector) {
- return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
- }
- else {
- return returnEl ? Ext.get(this.target) : this.target;
- }
- }
-});
-
-Ext.TouchEventObject = new Ext.TouchEventObjectImpl();
-Ext.gesture.Gesture = Ext.extend(Object, {
- listenForStart: true,
- listenForEnd: true,
- listenForMove: true,
-
- disableLocking: false,
-
- touches: 1,
-
- constructor: function(config) {
- config = config || {};
- Ext.apply(this, config);
-
- this.target = Ext.getDom(this.target);
- this.listeners = {};
-
- // <debug>
- if (!this.target) {
- throw new Error('Trying to bind a ' + this.type + ' event to element that does\'nt exist: ' + this.target);
- }
- // </debug>
-
- this.id = this.target.id + '-' + this.type;
-
- Ext.gesture.Gesture.superclass.constructor.call(this);
- Ext.gesture.Manager.register(this);
- },
-
- addListener: function(name, listener) {
- this.listeners[name] = this.listeners[name] || [];
- this.listeners[name].push(listener);
- },
-
- removeListener: function(name, listener) {
- var listeners = this.listeners[name];
-
- if (listeners) {
- listeners.remove(listener);
-
- if (listeners.length == 0) {
- delete this.listeners[name];
- }
-
- for (name in this.listeners) {
- if (this.listeners.hasOwnProperty(name)) {
- return;
- }
- }
-
- this.listeners = {};
- }
- },
-
- fire: function(type, e, args) {
- var listeners = this.listeners[type],
- ln = listeners && listeners.length,
- i;
-
- if (!this.disableLocking && this.isLocked(type)) {
- return false;
- }
-
- if (ln) {
- args = Ext.apply(args || {}, {
- time: e.timeStamp,
- type: type,
- gesture: this,
- target: (e.target.nodeType == 3) ? e.target.parentNode: e.target
- });
-
- for (i = 0; i < ln; i++) {
- listeners[i](e, args);
- }
- }
-
- return true;
- },
-
- stop: function() {
- Ext.gesture.Manager.stopGesture(this);
- },
-
- lock: function() {
- if (!this.disableLocking) {
- var args = arguments,
- ln = args.length,
- i;
-
- for (i = 0; i < ln; i++) {
- Ext.gesture.Manager.locks[args[i]] = this.id;
- }
- }
- },
-
- unlock: function() {
- if (!this.disableLocking) {
- var args = arguments,
- ln = args.length,
- i;
-
- for (i = 0; i < ln; i++) {
- if (Ext.gesture.Manager.locks[args[i]] == this.id) {
- delete Ext.gesture.Manager.locks[args[i]];
- }
- }
- }
- },
-
- isLocked : function(type) {
- var lock = Ext.gesture.Manager.locks[type];
- return !!(lock && lock !== this.id);
- },
-
- getLockingGesture : function(type) {
- var lock = Ext.gesture.Manager.locks[type];
- if (lock) {
- return Ext.gesture.Manager.get(lock) || null;
- }
- return null;
- },
-
- onTouchStart: Ext.emptyFn,
- onTouchMove: Ext.emptyFn,
- onTouchEnd: Ext.emptyFn,
-
- destroy: function() {
- this.stop();
- this.listeners = null;
- Ext.gesture.Manager.unregister(this);
- }
-});
-Ext.gesture.Touch = Ext.extend(Ext.gesture.Gesture, {
- handles: ['touchstart', 'touchmove', 'touchend', 'touchdown'],
-
- touchDownInterval: 500,
-
- onTouchStart: function(e, touch) {
- this.startX = this.previousX = touch.pageX;
- this.startY = this.previousY = touch.pageY;
- this.startTime = this.previousTime = e.timeStamp;
-
- this.fire('touchstart', e);
- this.lastEvent = e;
-
- if (this.listeners && this.listeners.touchdown) {
- this.touchDownIntervalId = setInterval(Ext.createDelegate(this.touchDownHandler, this), this.touchDownInterval);
- }
- },
-
- onTouchMove: function(e, touch) {
- this.fire('touchmove', e, this.getInfo(touch));
- this.lastEvent = e;
- },
-
- onTouchEnd: function(e) {
- this.fire('touchend', e, this.lastInfo);
- clearInterval(this.touchDownIntervalId);
- },
-
- touchDownHandler : function() {
- this.fire('touchdown', this.lastEvent, this.lastInfo);
- },
-
- getInfo : function(touch) {
- var time = Date.now(),
- deltaX = touch.pageX - this.startX,
- deltaY = touch.pageY - this.startY,
- info = {
- startX: this.startX,
- startY: this.startY,
- previousX: this.previousX,
- previousY: this.previousY,
- pageX: touch.pageX,
- pageY: touch.pageY,
- deltaX: deltaX,
- deltaY: deltaY,
- absDeltaX: Math.abs(deltaX),
- absDeltaY: Math.abs(deltaY),
- previousDeltaX: touch.pageX - this.previousX,
- previousDeltaY: touch.pageY - this.previousY,
- time: time,
- startTime: this.startTime,
- previousTime: this.previousTime,
- deltaTime: time - this.startTime,
- previousDeltaTime: time - this.previousTime
- };
-
- this.previousTime = info.time;
- this.previousX = info.pageX;
- this.previousY = info.pageY;
- this.lastInfo = info;
-
- return info;
- }
-});
-
-Ext.regGesture('touch', Ext.gesture.Touch);
-Ext.gesture.Tap = Ext.extend(Ext.gesture.Gesture, {
- handles: [
- 'tapstart',
- 'tapcancel',
- 'tap',
- 'doubletap',
- 'taphold',
- 'singletap'
- ],
-
- cancelThreshold: 10,
-
- doubleTapThreshold: 800,
-
- singleTapThreshold: 400,
-
- holdThreshold: 1000,
-
- fireClickEvent: false,
-
- onTouchStart : function(e, touch) {
- var me = this;
-
- me.startX = touch.pageX;
- me.startY = touch.pageY;
- me.fire('tapstart', e, me.getInfo(touch));
-
- if (this.listeners.taphold) {
- me.timeout = setTimeout(function() {
- me.fire('taphold', e, me.getInfo(touch));
- delete me.timeout;
- }, me.holdThreshold);
- }
-
- me.lastTouch = touch;
- },
-
- onTouchMove : function(e, touch) {
- var me = this;
- if (me.isCancel(touch)) {
- me.fire('tapcancel', e, me.getInfo(touch));
- if (me.timeout) {
- clearTimeout(me.timeout);
- delete me.timeout;
- }
- me.stop();
- }
-
- me.lastTouch = touch;
- },
-
- onTouchEnd : function(e) {
- var me = this,
- info = me.getInfo(me.lastTouch);
-
- this.fireTapEvent(e, info);
-
- if (me.lastTapTime && e.timeStamp - me.lastTapTime <= me.doubleTapThreshold) {
- me.lastTapTime = null;
- e.preventDefault();
- me.fire('doubletap', e, info);
- }
- else {
- me.lastTapTime = e.timeStamp;
- }
-
- if (me.listeners && me.listeners.singletap && me.singleTapThreshold && !me.preventSingleTap) {
- me.fire('singletap', e, info);
- me.preventSingleTap = true;
- setTimeout(function() {
- me.preventSingleTap = false;
- }, me.singleTapThreshold);
- }
-
- if (me.timeout) {
- clearTimeout(me.timeout);
- delete me.timeout;
- }
- },
-
- fireTapEvent: function(e, info) {
- this.fire('tap', e, info);
-
- if (e.event)
- e = e.event;
-
- var target = (e.changedTouches ? e.changedTouches[0] : e).target;
-
- if (!target.disabled && this.fireClickEvent) {
- var clickEvent = document.createEvent("MouseEvent");
- clickEvent.initMouseEvent('click', e.bubbles, e.cancelable, document.defaultView, e.detail, e.screenX, e.screenY, e.clientX,
- e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.metaKey, e.button, e.relatedTarget);
- clickEvent.isSimulated = true;
-
-
- target.dispatchEvent(clickEvent);
- }
- },
-
- getInfo : function(touch) {
- return {
- pageX: touch.pageX,
- pageY: touch.pageY
- };
- },
-
- isCancel : function(touch) {
- var me = this;
- return (
- Math.abs(touch.pageX - me.startX) >= me.cancelThreshold ||
- Math.abs(touch.pageY - me.startY) >= me.cancelThreshold
- );
- }
-});
-Ext.regGesture('tap', Ext.gesture.Tap);
-Ext.gesture.Swipe = Ext.extend(Ext.gesture.Gesture, {
- listenForEnd: false,
-
- swipeThreshold: 35,
- swipeTime: 1000,
-
- onTouchStart : function(e, touch) {
- this.startTime = e.timeStamp;
- this.startX = touch.pageX;
- this.startY = touch.pageY;
- this.lock('scroll', 'scrollstart', 'scrollend');
- },
-
- onTouchMove : function(e, touch) {
- var deltaY = touch.pageY - this.startY,
- deltaX = touch.pageX - this.startX,
- absDeltaY = Math.abs(deltaY),
- absDeltaX = Math.abs(deltaX),
- deltaTime = e.timeStamp - this.startTime;
-
- // If the swipeTime is over, we are not gonna check for it again
- if (absDeltaY - absDeltaX > 3 || deltaTime > this.swipeTime) {
- this.unlock('scroll', 'scrollstart', 'scrollend');
- this.stop();
- }
- else if (absDeltaX > this.swipeThreshold && absDeltaX > absDeltaY) {
- // If this is a swipe, a scroll is not possible anymore
- this.fire('swipe', e, {
- direction: (deltaX < 0) ? 'left' : 'right',
- distance: absDeltaX,
- deltaTime: deltaTime,
- deltaX: deltaX
- });
-
- this.stop();
- }
- }
-});
-Ext.regGesture('swipe', Ext.gesture.Swipe);
-Ext.gesture.Drag = Ext.extend(Ext.gesture.Touch, {
- handles: ['dragstart', 'drag', 'dragend'],
-
- dragThreshold: 5,
-
- direction: 'both',
-
- horizontal: false,
- vertical: false,
-
- constructor: function() {
- Ext.gesture.Drag.superclass.constructor.apply(this, arguments);
-
- if (this.direction == 'both') {
- this.horizontal = true;
- this.vertical = true;
- }
- else if (this.direction == 'horizontal') {
- this.horizontal = true;
- }
- else {
- this.vertical = true;
- }
-
- return this;
- },
-
- onTouchStart: function(e, touch) {
- this.startX = this.previousX = touch.pageX;
- this.startY = this.previousY = touch.pageY;
- this.startTime = this.previousTime = e.timeStamp;
-
- this.dragging = false;
- },
-
- onTouchMove: function(e, touch) {
- if (this.isLocked('drag')) {
- return;
- }
-
- var info = this.getInfo(touch);
-
- if (!this.dragging) {
- if (this.isDragging(info) && this.fire('dragstart', e, info)) {
- this.dragging = true;
- this.lock('drag', 'dragstart', 'dragend');
- }
- }
- else {
- this.fire('drag', e, info);
- }
- },
-
- onTouchEnd: function(e) {
- if (this.dragging) {
- this.fire('dragend', e, this.lastInfo);
- }
-
- this.dragging = false;
- },
-
- isDragging: function(info) {
- return (
- (this.horizontal && info.absDeltaX >= this.dragThreshold) ||
- (this.vertical && info.absDeltaY >= this.dragThreshold)
- );
- },
-
- /**
- * Method to determine whether this Sortable is currently disabled.
- * @return {Boolean} the disabled state of this Sortable.
- */
- isVertical: function() {
- return this.vertical;
- },
-
- /**
- * Method to determine whether this Sortable is currently sorting.
- * @return {Boolean} the sorting state of this Sortable.
- */
- isHorizontal: function() {
- return this.horizontal;
- }
-});
-
-Ext.regGesture('drag', Ext.gesture.Drag);
-Ext.gesture.Pinch = Ext.extend(Ext.gesture.Gesture, {
- handles: [
- 'pinchstart',
- 'pinch',
- 'pinchend'
- ],
-
- touches: 2,
-
- onTouchStart : function(e) {
- var me = this;
-
- if (Ext.supports.Touch && e.targetTouches.length >= 2) {
- me.lock('swipe', 'scroll', 'scrollstart', 'scrollend', 'touchmove', 'touchend', 'touchstart', 'tap', 'tapstart', 'taphold', 'tapcancel', 'doubletap');
- me.pinching = true;
-
- var targetTouches = e.targetTouches,
- firstTouch = me.firstTouch = targetTouches[0],
- secondTouch = me.secondTouch = targetTouches[1];
-
- me.previousDistance = me.startDistance = me.getDistance();
- me.previousScale = 1;
-
- me.fire('pinchstart', e, {
- distance: me.startDistance,
- scale: me.previousScale
- });
- }
- else if (me.pinching) {
- me.unlock('swipe', 'scroll', 'scrollstart', 'scrollend', 'touchmove', 'touchend', 'touchstart', 'tap', 'tapstart', 'taphold', 'tapcancel', 'doubletap');
- me.pinching = false;
- }
- },
-
- onTouchMove : function(e) {
- if (this.pinching) {
- this.fire('pinch', e, this.getPinchInfo());
- }
- },
-
- onTouchEnd : function(e) {
- if (this.pinching) {
- this.fire('pinchend', e, this.getPinchInfo());
- }
- },
-
- getPinchInfo : function() {
- var me = this,
- distance = me.getDistance(),
- scale = distance / me.startDistance,
- firstTouch = me.firstTouch,
- secondTouch = me.secondTouch,
- info = {
- scale: scale,
- deltaScale: scale - 1,
- previousScale: me.previousScale,
- previousDeltaScale: scale - me.previousScale,
- distance: distance,
- deltaDistance: distance - me.startDistance,
- startDistance: me.startDistance,
- previousDistance: me.previousDistance,
- previousDeltaDistance: distance - me.previousDistance,
- firstTouch: firstTouch,
- secondTouch: secondTouch,
- firstPageX: firstTouch.pageX,
- firstPageY: firstTouch.pageY,
- secondPageX: secondTouch.pageX,
- secondPageY: secondTouch.pageY,
- // The midpoint between the touches is (x1 + x2) / 2, (y1 + y2) / 2
- midPointX: (firstTouch.pageX + secondTouch.pageX) / 2,
- midPointY: (firstTouch.pageY + secondTouch.pageY) / 2
- };
-
- me.previousScale = scale;
- me.previousDistance = distance;
-
- return info;
- },
-
- getDistance : function() {
- var me = this;
- return Math.sqrt(
- Math.pow(Math.abs(me.firstTouch.pageX - me.secondTouch.pageX), 2) +
- Math.pow(Math.abs(me.firstTouch.pageY - me.secondTouch.pageY), 2)
- );
- }
-});
-
-Ext.regGesture('pinch', Ext.gesture.Pinch);
-
-/**
- * @class Ext.lib.Component
- * @extends Ext.util.Observable
- * Shared Component class
- */
-Ext.lib.Component = Ext.extend(Ext.util.Observable, {
- isComponent: true,
-
- /**
- * @cfg {Mixed} renderTpl
- * <p>An {@link Ext.XTemplate XTemplate} used to create the internal structure inside this Component's
- * encapsulating {@link #getEl Element}.</p>
- * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}
- * and {@link Ext.Container}, this defaults to <b><code>null</code></b> which means that they will be initially rendered
- * with no internal structure; they render their {@link #getEl Element} empty. The more specialized ExtJS and Touch classes
- * which use a more complex DOM structure, provide their own template definitions.</p>
- * <p>This is intended to allow the developer to create application-specific utility Components with customized
- * internal structure.</p>
- * <p>Upon rendering, any created child elements may be automatically imported into object properties using the
- * {@link #renderSelectors} option.</p>
- */
- renderTpl: null,
-
- /**
- * @cfg {Object} renderSelectors.
- * <p>An object containing properties specifying {@link Ext.DomQuery DomQuery} selectors which identify child elements
- * created by the render process.</p>
- * <p>After the Component's internal structure is rendered according to the {@link renderTpl}, this object is iterated through,
- * and the found Elements are added as properties to the Component using the <code>renderSelector</code> property name.</p>
- * <p>For example, a Component which rendered an image, and description into its element might use the following properties
- * coded into its prototype:<pre><code>
-renderTpl: '<img src="{imageUrl}" class="x-image-component-img"><div class="x-image-component-desc">{description}</div>',
-
-renderSelectors: {
- image: 'img.x-image-component-img',
- descEl: 'div.x-image-component-desc'
-}
-</code></pre>
- * <p>After rendering, the Component would have a property <code>image</code> referencing its child <code>img</code> Element,
- * and a property <code>descEl</code> referencing the <code>div</code> Element which contains the description.</p>
- */
-
- /**
- * @cfg {Mixed} renderTo
- * <p>Specify the id of the element, a DOM element or an existing Element that this component
- * will be rendered into.</p><div><ul>
- * <li><b>Notes</b> : <ul>
- * <div class="sub-desc">Do <u>not</u> use this option if the Component is to be a child item of
- * a {@link Ext.Container Container}. It is the responsibility of the
- * {@link Ext.Container Container}'s {@link Ext.Container#layout layout manager}
- * to render and manage its child items.</div>
- * <div class="sub-desc">When using this config, a call to render() is not required.</div>
- * </ul></li>
- * </ul></div>
- * <p>See <code>{@link #render}</code> also.</p>
- */
-
- /**
- * @cfg {String/Object} componentLayout
- * <br><p>The sizing and positioning of the component Elements is the responsibility of
- * the Component's layout manager which creates and manages the type of layout specific to the component.
- * <p>If the {@link #layout} configuration is not explicitly specified for
- * a general purpose compopnent the
- * {@link Ext.layout.AutoComponentLayout default layout manager} will be used.
- */
-
- /**
- * @cfg {Mixed} tpl
- * An <bold>{@link Ext.Template}</bold>, <bold>{@link Ext.XTemplate}</bold>
- * or an array of strings to form an Ext.XTemplate.
- * Used in conjunction with the <code>{@link #data}</code> and
- * <code>{@link #tplWriteMode}</code> configurations.
- */
-
- /**
- * @cfg {Mixed} data
- * The initial set of data to apply to the <code>{@link #tpl}</code> to
- * update the content area of the Component.
- */
-
- /**
- * @cfg {String} tplWriteMode The Ext.(X)Template method to use when
- * updating the content area of the Component. Defaults to <code>'overwrite'</code>
- * (see <code>{@link Ext.XTemplate#overwrite}</code>).
- */
- tplWriteMode: 'overwrite',
-
- /**
- * @cfg {String} baseCls
- * The base CSS class to apply to this components's element. This will also be prepended to
- * elements within this component like Panel's body will get a class x-panel-body. This means
- * that if you create a subclass of Panel, and you want it to get all the Panels styling for the
- * element and the body, you leave the baseCls x-panel and use componentCls to add specific styling for this
- * component.
- */
- baseCls: 'x-component',
-
- /**
- * @cfg {String} componentCls
- * CSS Class to be added to a components root level element to give distinction to it
- * via styling.
- */
-
- /**
- * @cfg {String} cls
- * An optional extra CSS class that will be added to this component's Element (defaults to ''). This can be
- * useful for adding customized styles to the component or any of its children using standard CSS rules.
- */
-
- /**
- * @cfg {String} disabledCls
- * CSS class to add when the Component is disabled. Defaults to 'x-item-disabled'.
- */
- disabledCls: 'x-item-disabled',
-
- /**
- * @cfg {String} ui
- * A set of predefined ui styles for individual components.
- *
- * Most components support 'light' and 'dark'.
- *
- * Extra string added to the baseCls with an extra '-'.
- * <pre><code>
- new Ext.Panel({
- title: 'Some Title',
- baseCls: 'x-component'
- ui: 'green'
- });
- </code></pre>
- * <p>The ui configuration in this example would add 'x-component-green' as an additional class.</p>
- */
-
- /**
- * @cfg {String} style
- * A custom style specification to be applied to this component's Element. Should be a valid argument to
- * {@link Ext.Element#applyStyles}.
- * <pre><code>
- new Ext.Panel({
- title: 'Some Title',
- renderTo: Ext.getBody(),
- width: 400, height: 300,
- layout: 'form',
- items: [{
- xtype: 'textareafield',
- style: {
- width: '95%',
- marginBottom: '10px'
- }
- },
- new Ext.Button({
- text: 'Send',
- minWidth: '100',
- style: {
- marginBottom: '10px'
- }
- })
- ]
- });
- </code></pre>
- */
-
- /**
- * @cfg {Number} width
- * The width of this component in pixels.
- */
-
- /**
- * @cfg {Number} height
- * The height of this component in pixels.
- */
-
- /**
- * @cfg {Number/String} border
- * Specifies the border for this component. The border can be a single numeric value to apply to all sides or
- * it can be a CSS style specification for each style, for example: '10 5 3 10'.
- */
-
- /**
- * @cfg {Number/String} padding
- * Specifies the padding for this component. The padding can be a single numeric value to apply to all sides or
- * it can be a CSS style specification for each style, for example: '10 5 3 10'.
- */
-
- /**
- * @cfg {Number/String} margin
- * Specifies the margin for this component. The margin can be a single numeric value to apply to all sides or
- * it can be a CSS style specification for each style, for example: '10 5 3 10'.
- */
-
- /**
- * @cfg {Boolean} hidden
- * Defaults to false.
- */
- hidden: false,
-
- /**
- * @cfg {Boolean} disabled
- * Defaults to false.
- */
- disabled: false,
-
- /**
- * @cfg {Boolean} draggable
- * Allows the component to be dragged via the touch event.
- */
-
- /**
- * Read-only property indicating whether or not the component can be dragged
- * @property draggable
- * @type {Boolean}
- */
- draggable: false,
-
- /**
- * @cfg {Boolean} floating
- * Create the Component as a floating and use absolute positioning.
- * Defaults to false.
- */
- floating: false,
-
- /**
- * @cfg {String} contentEl
- * <p>Optional. Specify an existing HTML element, or the <code>id</code> of an existing HTML element to use as the content
- * for this component.</p>
- * <ul>
- * <li><b>Description</b> :
- * <div class="sub-desc">This config option is used to take an existing HTML element and place it in the layout element
- * of a new component (it simply moves the specified DOM element <i>after the Component is rendered</i> to use as the content.</div></li>
- * <li><b>Notes</b> :
- * <div class="sub-desc">The specified HTML element is appended to the layout element of the component <i>after any configured
- * {@link #html HTML} has been inserted</i>, and so the document will not contain this element at the time the {@link #render} event is fired.</div>
- * <div class="sub-desc">The specified HTML element used will not participate in any <code><b>{@link Ext.Container#layout layout}</b></code>
- * scheme that the Component may use. It is just HTML. Layouts operate on child <code><b>{@link Ext.Container#items items}</b></code>.</div>
- * <div class="sub-desc">Add either the <code>x-hidden</code> or the <code>x-hide-display</code> CSS class to
- * prevent a brief flicker of the content before it is rendered to the panel.</div></li>
- * </ul>
- */
-
- /**
- * @cfg {String/Object} html
- * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element
- * content (defaults to ''). The HTML content is added after the component is rendered,
- * so the document will not contain this HTML at the time the {@link #render} event is fired.
- * This content is inserted into the body <i>before</i> any configured {@link #contentEl} is appended.
- */
-
- /**
- * @cfg {String} styleHtmlContent
- * True to automatically style the html inside the content target of this component (body for panels).
- * Defaults to false.
- */
- styleHtmlContent: false,
-
- /**
- * @cfg {String} styleHtmlCls
- * The class that is added to the content target when you set styleHtmlContent to true.
- * Defaults to 'x-html'
- */
- styleHtmlCls: 'x-html',
-
- /**
- * @cfg {Number} minHeight
- * <p>The minimum value in pixels which this Component will set its height to.</p>
- * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
- */
- /**
- * @cfg {Number} minWidth
- * <p>The minimum value in pixels which this Component will set its width to.</p>
- * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
- */
- /**
- * @cfg {Number} maxHeight
- * <p>The maximum value in pixels which this Component will set its height to.</p>
- * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
- */
- /**
- * @cfg {Number} maxWidth
- * <p>The maximum value in pixels which this Component will set its width to.</p>
- * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
- */
-
- // @private
- allowDomMove: true,
- autoShow: false,
-
- autoRender: false,
-
- needsLayout: false,
-
- /**
- * @cfg {Object/Array} plugins
- * An object or array of objects that will provide custom functionality for this component. The only
- * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
- * When a component is created, if any plugins are available, the component will call the init method on each
- * plugin, passing a reference to itself. Each plugin can then call methods or respond to events on the
- * component as needed to provide its functionality.
- */
-
- /**
- * Read-only property indicating whether or not the component has been rendered.
- * @property rendered
- * @type {Boolean}
- */
- rendered: false,
-
- constructor : function(config) {
- config = config || {};
- this.initialConfig = config;
- Ext.apply(this, config);
-
- this.addEvents(
- /**
- * @event beforeactivate
- * Fires before a Component has been visually activated.
- * Returning false from an event listener can prevent the activate
- * from occurring.
- * @param {Ext.Component} this
- */
- 'beforeactivate',
- /**
- * @event activate
- * Fires after a Component has been visually activated.
- * @param {Ext.Component} this
- */
- 'activate',
- /**
- * @event beforedeactivate
- * Fires before a Component has been visually deactivated.
- * Returning false from an event listener can prevent the deactivate
- * from occurring.
- * @param {Ext.Component} this
- */
- 'beforedeactivate',
- /**
- * @event deactivate
- * Fires after a Component has been visually deactivated.
- * @param {Ext.Component} this
- */
- 'deactivate',
- /**
- * @event added
- * Fires after a Component had been added to a Container.
- * @param {Ext.Component} this
- * @param {Ext.Container} container Parent Container
- * @param {Number} pos position of Component
- */
- 'added',
- /**
- * @event disable
- * Fires after the component is disabled.
- * @param {Ext.Component} this
- */
- 'disable',
- /**
- * @event enable
- * Fires after the component is enabled.
- * @param {Ext.Component} this
- */
- 'enable',
- /**
- * @event beforeshow
- * Fires before the component is shown when calling the {@link #show} method.
- * Return false from an event handler to stop the show.
- * @param {Ext.Component} this
- */
- 'beforeshow',
- /**
- * @event show
- * Fires after the component is shown when calling the {@link #show} method.
- * @param {Ext.Component} this
- */
- 'show',
- /**
- * @event beforehide
- * Fires before the component is hidden when calling the {@link #hide} method.
- * Return false from an event handler to stop the hide.
- * @param {Ext.Component} this
- */
- 'beforehide',
- /**
- * @event hide
- * Fires after the component is hidden.
- * Fires after the component is hidden when calling the {@link #hide} method.
- * @param {Ext.Component} this
- */
- 'hide',
- /**
- * @event removed
- * Fires when a component is removed from an Ext.Container
- * @param {Ext.Component} this
- * @param {Ext.Container} ownerCt Container which holds the component
- */
- 'removed',
- /**
- * @event beforerender
- * Fires before the component is {@link #rendered}. Return false from an
- * event handler to stop the {@link #render}.
- * @param {Ext.Component} this
- */
- 'beforerender',
- /**
- * @event render
- * Fires after the component markup is {@link #rendered}.
- * @param {Ext.Component} this
- */
- 'render',
- /**
- * @event afterrender
- * <p>Fires after the component rendering is finished.</p>
- * <p>The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed
- * by any afterRender method defined for the Component, and, if {@link #stateful}, after state
- * has been restored.</p>
- * @param {Ext.Component} this
- */
- 'afterrender',
- /**
- * @event beforedestroy
- * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}.
- * @param {Ext.Component} this
- */
- 'beforedestroy',
- /**
- * @event destroy
- * Fires after the component is {@link #destroy}ed.
- * @param {Ext.Component} this
- */
- 'destroy',
- /**
- * @event resize
- * Fires after the component is resized.
- * @param {Ext.Component} this
- * @param {Number} adjWidth The box-adjusted width that was set
- * @param {Number} adjHeight The box-adjusted height that was set
- * @param {Number} rawWidth The width that was originally specified
- * @param {Number} rawHeight The height that was originally specified
- */
- 'resize',
- /**
- * @event move
- * Fires after the component is moved.
- * @param {Ext.Component} this
- * @param {Number} x The new x position
- * @param {Number} y The new y position
- */
- 'move',
-
- 'beforestaterestore',
- 'staterestore',
- 'beforestatesave',
- 'statesave'
- );
-
- this.getId();
-
- this.mons = [];
- this.additionalCls = [];
- this.renderData = this.renderData || {};
- this.renderSelectors = this.renderSelectors || {};
-
- this.initComponent();
-
- // ititComponent gets a chance to change the id property before registering
- Ext.ComponentMgr.register(this);
-
- // Dont pass the config so that it is not applied to 'this' again
- Ext.lib.Component.superclass.constructor.call(this);
-
- // Move this into Observable?
- if (this.plugins) {
- this.plugins = [].concat(this.plugins);
- for (var i = 0, len = this.plugins.length; i < len; i++) {
- this.plugins[i] = this.initPlugin(this.plugins[i]);
- }
- }
-
- // This won't work in Touch
- if (this.applyTo) {
- this.applyToMarkup(this.applyTo);
- delete this.applyTo;
- }
- else if (this.renderTo) {
- this.render(this.renderTo);
- delete this.renderTo;
- }
-
- if (Ext.isDefined(this.disabledClass)) {
- throw "Component: disabledClass has been deprecated. Please use disabledCls.";
- }
- },
-
- initComponent: Ext.emptyFn,
- applyToMarkup: Ext.emptyFn,
-
- show: Ext.emptyFn,
-
- onShow : function() {
- // Layout if needed
- var needsLayout = this.needsLayout;
- if (Ext.isObject(needsLayout)) {
- this.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize);
- }
- },
-
- // @private
- initPlugin : function(plugin) {
- if (plugin.ptype && typeof plugin.init != 'function') {
- plugin = Ext.PluginMgr.create(plugin);
- }
- else if (typeof plugin == 'string') {
- plugin = Ext.PluginMgr.create({
- ptype: plugin
- });
- }
-
- plugin.init(this);
-
- return plugin;
- },
-
- // @private
- render : function(container, position) {
- if (!this.rendered && this.fireEvent('beforerender', this) !== false) {
- // If this.el is defined, we want to make sure we are dealing with
- // an Ext Element.
- if (this.el) {
- this.el = Ext.get(this.el);
- }
-
- container = this.initContainer(container);
-
- this.onRender(container, position);
- this.fireEvent('render', this);
-
- this.initContent();
-
- this.afterRender(container);
- this.fireEvent('afterrender', this);
-
- this.initEvents();
-
- if (this.autoShow) {
- this.show();
- }
-
- if (this.hidden) {
- // call this so we don't fire initial hide events.
- this.onHide(false); // no animation after render
- }
-
- if (this.disabled) {
- // pass silent so the event doesn't fire the first time.
- this.disable(true);
- }
- }
-
- return this;
- },
-
- // @private
- onRender : function(container, position) {
- var el = this.el,
- renderTpl,
- renderData;
-
- position = this.getInsertPosition(position);
-
- if (!el) {
- if (position) {
- el = Ext.DomHelper.insertBefore(position, this.getElConfig(), true);
- }
- else {
- el = Ext.DomHelper.append(container, this.getElConfig(), true);
- }
- }
- else if (this.allowDomMove !== false) {
- container.dom.insertBefore(el.dom, position);
- }
-
- el.addCls(this.initCls());
- el.setStyle(this.initStyles());
-
- // Here we check if the component has a height set through style or css.
- // If it does then we set the this.height to that value and it won't be
- // considered an auto height component
- // if (this.height === undefined) {
- // var height = el.getHeight();
- // // This hopefully means that the panel has an explicit height set in style or css
- // if (height - el.getPadding('tb') - el.getBorderWidth('tb') > 0) {
- // this.height = height;
- // }
- // }
-
- renderTpl = this.initRenderTpl();
- if (renderTpl) {
- renderData = this.initRenderData();
- renderTpl.append(el, renderData);
- }
-
- this.el = el;
- this.applyRenderSelectors();
- this.rendered = true;
- },
-
- /**
- * <p>Creates an array of class names from the configurations to add to this Component's <code>el</code> on render.</p>
- * <p>Private, but (possibly) used by ComponentQuery for selection by class name if Component is not rendered.</p>
- * @return {Array} An array of class names with which the Component's element will be rendered.
- * @private
- */
- initCls: function() {
- var cls = [ this.baseCls ];
-
- if (this.componentCls) {
- cls.push(this.componentCls);
- }
- else {
- this.componentCls = this.baseCls;
- }
- if (this.cls) {
- cls.push(this.cls);
- delete this.cls;
- }
- if (this.ui) {
- cls.push(this.componentCls + '-' + this.ui);
- }
- return cls.concat(this.additionalCls);
- },
-
- // @private
- afterRender : function() {
- this.getComponentLayout();
-
- if (this.x || this.y) {
- this.setPosition(this.x, this.y);
- }
-
- if (this.styleHtmlContent) {
- this.getTargetEl().addCls(this.styleHtmlCls);
- }
-
- // If there is a width or height set on this component we will call
- // which will trigger the component layout
- if (!this.ownerCt) {
- this.setSize(this.width, this.height);
- }
- },
-
- getElConfig : function() {
- return {tag: 'div', id: this.id};
- },
-
- /**
- * This function takes the position argument passed to onRender and returns a
- * DOM element that you can use in the insertBefore.
- * @param {String/Number/Element/HTMLElement} position Index, element id or element you want
- * to put this component before.
- * @return {HTMLElement} DOM element that you can use in the insertBefore
- */
- getInsertPosition: function(position) {
- // Convert the position to an element to insert before
- if (position !== undefined) {
- if (Ext.isNumber(position)) {
- position = this.container.dom.childNodes[position];
- }
- else {
- position = Ext.getDom(position);
- }
- }
-
- return position;
- },
-
- /**
- * Adds ctCls to container.
- * @return {Ext.Element} The initialized container
- * @private
- */
- initContainer: function(container) {
- // If you render a component specifying the el, we get the container
- // of the el, and make sure we dont move the el around in the dom
- // during the render
- if (!container && this.el) {
- container = this.el.dom.parentNode;
- this.allowDomMove = false;
- }
-
- this.container = Ext.get(container);
-
- if (this.ctCls) {
- this.container.addCls(this.ctCls);
- }
-
- return this.container;
- },
-
- /**
- * Initialized the renderData to be used when rendering the renderTpl.
- * @return {Object} Object with keys and values that are going to be applied to the renderTpl
- * @private
- */
- initRenderData: function() {
- return Ext.applyIf(this.renderData, {
- baseCls: this.baseCls,
- componentCls: this.componentCls
- });
- },
-
- /**
- * Initializes the renderTpl.
- * @return {Ext.XTemplate} The renderTpl XTemplate instance.
- * @private
- */
- initRenderTpl: function() {
- var renderTpl = this.renderTpl;
- if (renderTpl) {
- if (this.proto.renderTpl !== renderTpl) {
- if (Ext.isArray(renderTpl) || typeof renderTpl === "string") {
- renderTpl = new Ext.XTemplate(renderTpl);
- }
- }
- else if (Ext.isArray(this.proto.renderTpl)){
- renderTpl = this.proto.renderTpl = new Ext.XTemplate(renderTpl);
- }
- }
- return renderTpl;
- },
-
- /**
- * Function description
- * @return {String} A CSS style string with style, padding, margin and border.
- * @private
- */
- initStyles: function() {
- var style = {},
- Element = Ext.Element,
- i, ln, split, prop;
-
- if (Ext.isString(this.style)) {
- split = this.style.split(';');
- for (i = 0, ln = split.length; i < ln; i++) {
- if (!Ext.isEmpty(split[i])) {
- prop = split[i].split(':');
- style[Ext.util.Format.trim(prop[0])] = Ext.util.Format.trim(prop[1]);
- }
- }
- } else {
- style = Ext.apply({}, this.style);
- }
-
- // Convert the padding, margin and border properties from a space seperated string
- // into a proper style string
- if (this.padding != undefined) {
- style.padding = Element.unitizeBox((this.padding === true) ? 5 : this.padding);
- }
-
- if (this.margin != undefined) {
- style.margin = Element.unitizeBox((this.margin === true) ? 5 : this.margin);
- }
-
- if (this.border != undefined) {
- style.borderWidth = Element.unitizeBox((this.border === true) ? 1 : this.border);
- }
-
- delete this.style;
- return style;
- },
-
- /**
- * Initializes this components contents. It checks for the properties
- * html, contentEl and tpl/data.
- * @private
- */
- initContent: function() {
- var target = this.getTargetEl();
-
- if (this.html) {
- target.update(Ext.DomHelper.markup(this.html));
- delete this.html;
- }
-
- if (this.contentEl) {
- var contentEl = Ext.get(this.contentEl);
- contentEl.show();
- target.appendChild(contentEl.dom);
- }
-
- if (this.tpl) {
- // Make sure this.tpl is an instantiated XTemplate
- if (!this.tpl.isTemplate) {
- this.tpl = new Ext.XTemplate(this.tpl);
- }
-
- if (this.data) {
- this.tpl[this.tplWriteMode](target, this.data);
- delete this.data;
- }
- }
- },
-
- // @private
- initEvents : function() {
- var afterRenderEvents = this.afterRenderEvents,
- property, listeners;
- if (afterRenderEvents) {
- for (property in afterRenderEvents) {
- if (!afterRenderEvents.hasOwnProperty(property)) {
- continue;
- }
- listeners = afterRenderEvents[property];
- if (this[property] && this[property].on) {
- this.mon(this[property], listeners);
- }
- }
- }
- },
-
- /**
- * Sets references to elements inside the component. E.g body -> x-panel-body
- * @private
- */
- applyRenderSelectors: function() {
- var selectors = this.renderSelectors || {},
- el = this.el.dom,
- selector;
-
- for (selector in selectors) {
- if (!selectors.hasOwnProperty(selector)) {
- continue;
- }
- this[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], el));
- }
- },
-
- is: function(selector) {
- return Ext.ComponentQuery.is(this, selector);
- },
-
- /**
- * <p>Walks up the <code>ownerCt</code> axis looking for an ancestor Container which matches
- * the passed simple selector.</p>
- * <p>Example:<pre><code>
-var owningTabContainer = grid.up('tabcontainer');
-</code></pre>
- * @param {String} selector Optional. The simple selector to test.
- * @return {Ext.Container} The matching ancestor Container (or <code>undefined</code> if no match was found).
- */
- up: function(selector) {
- var result = this.ownerCt;
- if (selector) {
- for (; result; result = result.ownerCt) {
- if (Ext.ComponentQuery.is(result, selector)) {
- return result;
- }
- }
- }
- return result;
- },
-
- /**
- * <p>Returns the next sibling of this Component.</p>
- * <p>Optionally selects the next sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery} selector.</p>
- * <p>May also be refered to as <code><b>prev()</b></code></p>
- * @param selector Optional. A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following items.
- * @returns The next sibling (or the next sibling which matches the selector). Returns null if there is no matching sibling.
- */
- nextSibling: function(selector) {
- var o = this.ownerCt, it, last, idx, c;
- if (o) {
- it = o.items;
- idx = it.indexOf(this) + 1;
- if (idx) {
- if (selector) {
- for (last = it.getCount(); idx < last; idx++) {
- if ((c = it.getAt(idx)).is(selector)) {
- return c;
- }
- }
- } else {
- if (idx < it.getCount()) {
- return it.getAt(idx);
- }
- }
- }
- }
- return null;
- },
-
- /**
- * <p>Returns the previous sibling of this Component.</p>
- * <p>Optionally selects the previous sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery} selector.</p>
- * <p>May also be refered to as <code><b>prev()</b></code></p>
- * @param selector Optional. A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding items.
- * @returns The previous sibling (or the previous sibling which matches the selector). Returns null if there is no matching sibling.
- */
- previousSibling: function(selector) {
- var o = this.ownerCt, it, idx, c;
- if (o) {
- it = o.items;
- idx = it.indexOf(this);
- if (idx != -1) {
- if (selector) {
- for (--idx; idx >= 0; idx--) {
- if ((c = it.getAt(idx)).is(selector)) {
- return c;
- }
- }
- } else {
- if (idx) {
- return it.getAt(--idx);
- }
- }
- }
- }
- return null;
- },
-
- /**
- * Retrieves the id of this component.
- * Will autogenerate an id if one has not already been set.
- */
- getId : function() {
- return this.id || (this.id = 'ext-comp-' + (++Ext.lib.Component.AUTO_ID));
- },
-
- getItemId : function() {
- return this.itemId || this.id;
- },
-
- /**
- * Retrieves the top level element representing this component.
- */
- getEl : function() {
- return this.el;
- },
-
- /**
- * This is used to determine where to insert the 'html', 'contentEl' and 'items' in this component.
- * @private
- */
- getTargetEl: function() {
- return this.el;
- },
-
- /**
- * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
- * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
- * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
- * to participate in determination of inherited xtypes.</b></p>
- * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
- * <p>Example usage:</p>
- * <pre><code>
-var t = new Ext.form.Text();
-var isText = t.isXType('textfield'); // true
-var isBoxSubclass = t.isXType('field'); // true, descended from Ext.form.Field
-var isBoxInstance = t.isXType('field', true); // false, not a direct Ext.form.Field instance
-</code></pre>
- * @param {String} xtype The xtype to check for this 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.
- */
- isXType: function(xtype, shallow) {
- //assume a string by default
- if (Ext.isFunction(xtype)) {
- xtype = xtype.xtype;
- //handle being passed the class, e.g. Ext.Component
- } else if (Ext.isObject(xtype)) {
- xtype = xtype.constructor.xtype;
- //handle being passed an instance
- }
-
- return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1: this.constructor.xtype == xtype;
- },
-
- /**
- * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
- * available xtypes, see the {@link Ext.Component} header.</p>
- * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
- * to participate in determination of inherited xtypes.</b></p>
- * <p>Example usage:</p>
- * <pre><code>
-var t = new Ext.form.Text();
-alert(t.getXTypes()); // alerts 'component/field/textfield'
-</code></pre>
- * @return {String} The xtype hierarchy string
- */
- getXTypes: function() {
- var constructor = this.constructor,
- xtypes = [],
- superclass = this,
- xtype;
-
- if (!constructor.xtypes) {
- while (superclass) {
- xtype = superclass.constructor.xtype;
-
- if (xtype != undefined) {
- xtypes.unshift(xtype);
- }
-
- superclass = superclass.constructor.superclass;
- }
-
- constructor.xtypeChain = xtypes;
- constructor.xtypes = xtypes.join('/');
- }
-
- return constructor.xtypes;
- },
-
- /**
- * Update the content area of a component.
- * @param {Mixed} htmlOrData
- * If this component has been configured with a template via the tpl config
- * then it will use this argument as data to populate the template.
- * If this component was not configured with a template, the components
- * content area will be updated via Ext.Element update
- * @param {Boolean} loadScripts
- * (optional) Only legitimate when using the html configuration. Defaults to false
- * @param {Function} callback
- * (optional) Only legitimate when using the html configuration. Callback to execute when scripts have finished loading
- */
- update : function(htmlOrData, loadScripts, cb) {
- if (this.tpl && !Ext.isString(htmlOrData)) {
- this.data = htmlOrData;
- if (this.rendered) {
- this.tpl[this.tplWriteMode](this.getTargetEl(), htmlOrData || {});
- }
- }
- else {
- this.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
- if (this.rendered) {
- this.getTargetEl().update(this.html, loadScripts, cb);
- }
- }
-
- if (this.rendered) {
- this.doComponentLayout();
- }
- },
-
- /**
- * Convenience function to hide or show this component by boolean.
- * @param {Boolean} visible True to show, false to hide
- * @return {Ext.Component} this
- */
- setVisible : function(visible) {
- return this[visible ? 'show': 'hide']();
- },
-
- /**
- * Returns true if this component is visible.
- * @return {Boolean} True if this component is visible, false otherwise.
- */
- isVisible: function() {
- var visible = !this.hidden,
- cmp = this.ownerCt;
-
- // Clear hiddenOwnerCt property
- this.hiddenOwnerCt = false;
- if (this.destroyed) {
- return false;
- };
-
- if (visible && this.rendered && cmp) {
- while (cmp) {
- if (cmp.hidden || cmp.collapsed) {
- // Store hiddenOwnerCt property if needed
- this.hiddenOwnerCt = cmp;
- visible = false;
- break;
- }
- cmp = cmp.ownerCt;
- }
- }
- return visible;
- },
-
- /**
- * Enable the component
- * @param {Boolean} silent
- * Passing false will supress the 'enable' event from being fired.
- */
- enable : function(silent) {
- if (this.rendered) {
- this.el.removeCls(this.disabledCls);
- this.el.dom.disabled = false;
- this.onEnable();
- }
-
- this.disabled = false;
-
- if (silent !== true) {
- this.fireEvent('enable', this);
- }
-
- return this;
- },
-
- /**
- * Disable the component.
- * @param {Boolean} silent
- * Passing true, will supress the 'disable' event from being fired.
- */
- disable : function(silent) {
- if (this.rendered) {
- this.el.addCls(this.disabledCls);
- this.el.dom.disabled = true;
- this.onDisable();
- }
-
- this.disabled = true;
-
- if (silent !== true) {
- this.fireEvent('disable', this);
- }
-
- return this;
- },
-
- /**
- * Method to determine whether this Component is currently disabled.
- * @return {Boolean} the disabled state of this Component.
- */
- isDisabled : function() {
- return this.disabled;
- },
-
- /**
- * Enable or disable the component.
- * @param {Boolean} disabled
- */
- setDisabled : function(disabled) {
- return this[disabled ? 'disable': 'enable']();
- },
-
- /**
- * Method to determine whether this Component is currently set to hidden.
- * @return {Boolean} the hidden state of this Component.
- */
- isHidden : function() {
- return this.hidden;
- },
-
- /**
- * Adds a CSS class to the top level element representing this component.
- * @returns {Ext.Component} Returns the Component to allow method chaining.
- */
- addCls : function() {
- var me = this,
- args = Ext.toArray(arguments);
- if (me.rendered) {
- me.el.addCls(args);
- }
- else {
- me.additionalCls = me.additionalCls.concat(args);
- }
- return me;
- },
-
- addClass : function() {
- throw "Component: addClass has been deprecated. Please use addCls.";
- },
-
- /**
- * Removes a CSS class from the top level element representing this component.
- * @returns {Ext.Component} Returns the Component to allow method chaining.
- */
- removeCls : function() {
- var me = this,
- args = Ext.toArray(arguments);
- if (me.rendered) {
- me.el.removeCls(args);
- }
- else if (me.additionalCls.length) {
- Ext.each(args, function(cls) {
- me.additionalCls.remove(cls);
- });
- }
- return me;
- },
-
- removeClass : function() {
- throw "Component: removeClass has been deprecated. Please use removeCls.";
- },
-
- addListener : function(element, listeners, scope, options) {
- if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) {
- if (options.element) {
- var fn = listeners,
- option;
-
- listeners = {};
- listeners[element] = fn;
- element = options.element;
- if (scope) {
- listeners.scope = scope;
- }
-
- for (option in options) {
- if (!options.hasOwnProperty(option)) {
- continue;
- }
- if (this.eventOptionsRe.test(option)) {
- listeners[option] = options[option];
- }
- }
- }
-
- // At this point we have a variable called element,
- // and a listeners object that can be passed to on
- if (this[element] && this[element].on) {
- this.mon(this[element], listeners);
- }
- else {
- this.afterRenderEvents = this.afterRenderEvents || {};
- this.afterRenderEvents[element] = listeners;
- }
- }
-
- return Ext.lib.Component.superclass.addListener.apply(this, arguments);
- },
-
- // @TODO: implement removelistener to support the dom event stuff
-
- /**
- * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
- * @return {Ext.Container} the Container which owns this Component.
- */
- getBubbleTarget : function() {
- return this.ownerCt;
- },
-
- /**
- * Method to determine whether this Component is floating.
- * @return {Boolean} the floating state of this component.
- */
- isFloating : function() {
- return this.floating;
- },
-
- /**
- * Method to determine whether this Component is draggable.
- * @return {Boolean} the draggable state of this component.
- */
- isDraggable : function() {
- return !!this.draggable;
- },
-
- /**
- * Method to determine whether this Component is droppable.
- * @return {Boolean} the droppable state of this component.
- */
- isDroppable : function() {
- return !!this.droppable;
- },
-
- /**
- * @private
- * Method to manage awareness of when components are added to their
- * respective Container, firing an added event.
- * References are established at add time rather than at render time.
- * @param {Ext.Container} container Container which holds the component
- * @param {number} pos Position at which the component was added
- */
- onAdded : function(container, pos) {
- this.ownerCt = container;
- this.fireEvent('added', this, container, pos);
- },
-
- /**
- * @private
- * Method to manage awareness of when components are removed from their
- * respective Container, firing an removed event. References are properly
- * cleaned up after removing a component from its owning container.
- */
- onRemoved : function() {
- this.fireEvent('removed', this, this.ownerCt);
- delete this.ownerCt;
- },
-
- // @private
- onEnable : Ext.emptyFn,
- // @private
- onDisable : Ext.emptyFn,
- // @private
- beforeDestroy : Ext.emptyFn,
- // @private
- // @private
- onResize : Ext.emptyFn,
-
- /**
- * Sets the width and height of this Component. This method fires the {@link #resize} event. This method can accept
- * either width and height as separate arguments, or you can pass a size object like <code>{width:10, height:20}</code>.
- * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
- * <li>A String used to set the CSS width style.</li>
- * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
- * <li><code>undefined</code> to leave the width unchanged.</li>
- * </ul></div>
- * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg).
- * This may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
- * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
- * <li><code>undefined</code> to leave the height unchanged.</li>
- * </ul></div>
- * @return {Ext.Component} this
- */
- setSize : function(width, height) {
- // support for standard size objects
- if (Ext.isObject(width)) {
- height = width.height;
- width = width.width;
- }
- if (!this.rendered || !this.isVisible()) {
- // If an ownerCt is hidden, add my reference onto the layoutOnShow stack. Set the needsLayout flag.
- if (this.hiddenOwnerCt) {
- var layoutCollection = this.hiddenOwnerCt.layoutOnShow;
- layoutCollection.remove(this);
- layoutCollection.add(this);
- }
- this.needsLayout = {
- width: width,
- height: height,
- isSetSize: true
- };
- if (!this.rendered) {
- this.width = (width !== undefined) ? width : this.width;
- this.height = (height !== undefined) ? height : this.height;
- }
- return this;
- }
- this.doComponentLayout(width, height, true);
-
- return this;
- },
-
- setCalculatedSize : function(width, height) {
- // support for standard size objects
- if (Ext.isObject(width)) {
- height = width.height;
- width = width.width;
- }
- if (!this.rendered || !this.isVisible()) {
- // If an ownerCt is hidden, add my reference onto the layoutOnShow stack. Set the needsLayout flag.
- if (this.hiddenOwnerCt) {
- var layoutCollection = this.hiddenOwnerCt.layoutOnShow;
- layoutCollection.remove(this);
- layoutCollection.add(this);
- }
- this.needsLayout = {
- width: width,
- height: height,
- isSetSize: false
- };
- return this;
- }
- this.doComponentLayout(width, height);
-
- return this;
- },
-
- /**
- * This method needs to be called whenever you change something on this component that requires the components
- * layout to be recalculated. An example is adding, showing or hiding a docked item to a Panel, or changing the
- * label of a form field. After a component layout, the container layout will automatically be run. So you could
- * be on the safe side and always call doComponentLayout instead of doLayout.
- * @return {Ext.Container} this
- */
- doComponentLayout : function(width, height, isSetSize) {
- var componentLayout = this.getComponentLayout();
- if (this.rendered && componentLayout) {
- width = (width !== undefined || this.collapsed === true) ? width : this.width;
- height = (height !== undefined || this.collapsed === true) ? height : this.height;
- if (isSetSize) {
- this.width = width;
- this.height = height;
- }
-
- componentLayout.layout(width, height);
- }
- return this;
- },
-
- // @private
- setComponentLayout : function(layout) {
- var currentLayout = this.componentLayout;
- if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
- currentLayout.setOwner(null);
- }
- this.componentLayout = layout;
- layout.setOwner(this);
- },
-
- getComponentLayout : function() {
- if (!this.componentLayout || !this.componentLayout.isLayout) {
- this.setComponentLayout(Ext.layout.LayoutManager.create(this.componentLayout, 'autocomponent'));
- }
- return this.componentLayout;
- },
-
- afterComponentLayout: Ext.emptyFn,
-
- /**
- * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
- * This method fires the {@link #move} event.
- * @param {Number} left The new left
- * @param {Number} top The new top
- * @return {Ext.Component} this
- */
- setPosition : function(x, y) {
- if (Ext.isObject(x)) {
- y = x.y;
- x = x.x;
- }
-
- if (!this.rendered) {
-
- return this;
- }
-
- if (x !== undefined || y !== undefined) {
- this.el.setBox(x, y);
- this.onPosition(x, y);
- this.fireEvent('move', this, x, y);
- }
- return this;
- },
-
- /* @private
- * Called after the component is moved, this method is empty by default but can be implemented by any
- * subclass that needs to perform custom logic after a move occurs.
- * @param {Number} x The new x position
- * @param {Number} y The new y position
- */
- onPosition: Ext.emptyFn,
-
- /**
- * Sets the width of the component. This method fires the {@link #resize} event.
- * @param {Number} width The new width to setThis may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
- * <li>A String used to set the CSS width style.</li>
- * </ul></div>
- * @return {Ext.Component} this
- */
- setWidth : function(width) {
- return this.setSize(width);
- },
-
- /**
- * Sets the height of the component. This method fires the {@link #resize} event.
- * @param {Number} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
- * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
- * <li>A String used to set the CSS height style.</li>
- * <li><i>undefined</i> to leave the height unchanged.</li>
- * </ul></div>
- * @return {Ext.Component} this
- */
- setHeight : function(height) {
- return this.setSize(undefined, height);
- },
-
- /**
- * Gets the current size of the component's underlying element.
- * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
- */
- getSize : function() {
- return this.el.getSize();
- },
-
- /**
- * Gets the current width of the component's underlying element.
- * @return {Number}
- */
- getWidth : function() {
- return this.el.getWidth();
- },
-
- /**
- * Gets the current height of the component's underlying element.
- * @return {Number}
- */
- getHeight : function() {
- return this.el.getHeight();
- },
-
- /**
- * This method allows you to show or hide a LoadMask on top of this component.
- * @param {Boolean/Object} load True to show the default LoadMask or a config object
- * that will be passed to the LoadMask constructor. False to hide the current LoadMask.
- * @param {Boolean} targetEl True to mask the targetEl of this Component instead of the this.el.
- * For example, setting this to true on a Panel will cause only the body to be masked. (defaults to false)
- * @return {Ext.LoadMask} The LoadMask instance that has just been shown.
- */
- setLoading : function(load, targetEl) {
- if (this.rendered) {
- if (load) {
- this.loadMask = this.loadMask || new Ext.LoadMask(targetEl ? this.getTargetEl() : this.el, Ext.applyIf(Ext.isObject(load) ? load : {}));
- this.loadMask.show();
- } else {
- Ext.destroy(this.loadMask);
- this.loadMask = null;
- }
- }
-
- return this.loadMask;
- },
-
- /**
- * Sets the dock position of this component in its parent panel. Note that
- * this only has effect if this item is part of the dockedItems collection
- * of a parent that has a DockLayout (note that any Panel has a DockLayout
- * by default)
- * @return {Component} this
- */
- setDocked : function(dock, layoutParent) {
- this.dock = dock;
- if (layoutParent && this.ownerCt && this.rendered) {
- this.ownerCt.doComponentLayout();
- }
- return this;
- },
-
- onDestroy : function() {
- if (this.monitorResize && Ext.EventManager.resizeEvent) {
- Ext.EventManager.resizeEvent.removeListener(this.setSize, this);
- }
- Ext.destroy(this.componentLayout, this.loadMask);
- },
-
- /**
- * Destroys the Component.
- */
- destroy : function() {
- if (!this.isDestroyed) {
- if (this.fireEvent('beforedestroy', this) !== false) {
- this.destroying = true;
- this.beforeDestroy();
-
- if (this.ownerCt && this.ownerCt.remove) {
- this.ownerCt.remove(this, false);
- }
-
- if (this.rendered) {
- this.el.remove();
- }
-
- this.onDestroy();
-
- Ext.ComponentMgr.unregister(this);
- this.fireEvent('destroy', this);
-
- this.clearListeners();
- this.destroying = false;
- this.isDestroyed = true;
- }
- }
- }
-});
-
-Ext.lib.Component.prototype.on = Ext.lib.Component.prototype.addListener;
-Ext.lib.Component.prototype.prev = Ext.lib.Component.prototype.previousSibling;
-Ext.lib.Component.prototype.next = Ext.lib.Component.prototype.nextSibling;
-
-// @private
-Ext.lib.Component.AUTO_ID = 1000;
-
-// Declare here so we can test
-Ext.Component = Ext.extend(Ext.lib.Component, {});
-Ext.reg('component', Ext.Component);
-
-/**
- * @class Ext.Component
- * @extends Ext.lib.Component
- * <p>Base class for all Ext components. All subclasses of Component may participate in the automated
- * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.Container Container} class.
- * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created,
- * or they may be added dynamically via the {@link Ext.Container#add add} method.</p>
- * <p>The Component base class has built-in support for basic hide/show and enable/disable behavior.</p>
- * <p>All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via
- * {@link Ext#getCmp}, passing the {@link #id}.</p>
- * <p>All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or
- * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).</p>
- * <p>See the <a href="http://extjs.com/learn/Tutorial:Creating_new_UI_controls">Creating new UI controls</a> tutorial for details on how
- * and to either extend or augment ExtJs base classes to create custom Components.</p>
- * <p>Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the
- * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:</p>
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #fullscreen}</li>
- * </ul>
- * <pre>
-xtype Class
-------------- ------------------
-button {@link Ext.Button}
-component {@link Ext.Component}
-container {@link Ext.Container}
-dataview {@link Ext.DataView}
-panel {@link Ext.Panel}
-slider {@link Ext.form.Slider}
-toolbar {@link Ext.Toolbar}
-spacer {@link Ext.Spacer}
-tabpanel {@link Ext.TabPanel}
-
-Form components
----------------------------------------
-formpanel {@link Ext.form.FormPanel}
-checkboxfield {@link Ext.form.Checkbox}
-selectfield {@link Ext.form.Select}
-field {@link Ext.form.Field}
-fieldset {@link Ext.form.FieldSet}
-hiddenfield {@link Ext.form.Hidden}
-numberfield {@link Ext.form.Number}
-radiofield {@link Ext.form.Radio}
-textareafield {@link Ext.form.TextArea}
-textfield {@link Ext.form.Text}
-togglefield {@link Ext.form.Toggle}
-</pre>
- * @constructor
- * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
- * <div class="mdetail-params"><ul>
- * <li><b>an element</b> :
- * <p class="sub-desc">it is set as the internal element and its id used as the component id</p></li>
- * <li><b>a string</b> :
- * <p class="sub-desc">it is assumed to be the id of an existing element and is used as the component id</p></li>
- * <li><b>anything else</b> :
- * <p class="sub-desc">it is assumed to be a standard config object and is applied to the component</p></li>
- * </ul></div>
- * @xtype component
- */
-Ext.Component = Ext.extend(Ext.lib.Component, {
- /**
- * @cfg {Object/String/Boolean} showAnimation
- * The type of animation you want to use when this component is shown. If you set this
- * this hide animation will automatically be the opposite.
- */
- showAnimation: null,
-
- /**
- * @cfg {Boolean} monitorOrientation
- * Monitor Orientation change
- */
- monitorOrientation: false,
-
- /**
- * @cfg {Boolean} floatingCls
- * The class that is being added to this component when its floating.
- * (defaults to x-floating)
- */
- floatingCls: 'x-floating',
-
- /**
- * @cfg {Boolean} hideOnMaskTap
- * True to automatically bind a tap listener to the mask that hides the window.
- * Defaults to true. Note: if you set this property to false you have to programmaticaly
- * hide the overlay.
- */
- hideOnMaskTap: true,
-
- /**
- * @cfg {Boolean} stopMaskTapEvent
- * True to stop the event that fires when you click outside the floating component.
- * Defalts to true.
- */
- stopMaskTapEvent: true,
-
- /**
- * @cfg {Boolean} centered
- * Center the Component. Defaults to false.
- */
- centered: false,
-
- /**
- * @cfg {Boolean} modal
- * True to make the Component modal and mask everything behind it when displayed, false to display it without
- * restricting access to other UI elements (defaults to false).
- */
- modal: false,
-
- /**
- * @cfg {Mixed} scroll
- * Configure the component to be scrollable. Acceptable values are:
- * <ul>
- * <li>'horizontal', 'vertical', 'both' to enabling scrolling for that direction.</li>
- * <li>A {@link Ext.util.Scroller Scroller} configuration.</li>
- * <li>false to explicitly disable scrolling.</li>
- * </ul>
- *
- * Enabling scrolling immediately sets the monitorOrientation config to true (for {@link Ext.Panel Panel})
- */
-
- /**
- * @cfg {Boolean} fullscreen
- * Force the component to take up 100% width and height available. Defaults to false.
- * Setting this configuration immediately sets the monitorOrientation config to true.
- * Setting this to true will render the component instantly.
- */
- fullscreen: false,
-
- /**
- * @cfg {Boolean} layoutOnOrientationChange
- * Set this to true to automatically relayout this component on orientation change.
- * This property is set to true by default if a component is floating unless you specifically
- * set this to false. Also note that you dont have to set this property to true if this component
- * is a child of a fullscreen container, since fullscreen components are also laid out automatically
- * on orientation change.
- * Defaults to <tt>null</tt>
- */
- layoutOnOrientationChange: null,
-
- // @private
- initComponent : function() {
- this.addEvents(
- /**
- * @event beforeorientationchange
- * Fires before the orientation changes, if the monitorOrientation
- * configuration is set to true. Return false to stop the orientation change.
- * @param {Ext.Panel} this
- * @param {String} orientation 'landscape' or 'portrait'
- * @param {Number} width
- * @param {Number} height
- */
- 'beforeorientationchange',
- /**
- * @event orientationchange
- * Fires when the orientation changes, if the monitorOrientation
- * configuration is set to true.
- * @param {Ext.Panel} this
- * @param {String} orientation 'landscape' or 'portrait'
- * @param {Number} width
- * @param {Number} height
- */
- 'orientationchange'
- );
-
- if (this.fullscreen || this.floating) {
- this.monitorOrientation = true;
- this.autoRender = true;
- }
-
- if (this.fullscreen) {
- var viewportSize = Ext.Viewport.getSize();
- this.width = viewportSize.width;
- this.height = viewportSize.height;
- this.cls = (this.cls || '') + ' x-fullscreen';
- this.renderTo = document.body;
- }
- },
-
- onRender : function() {
- Ext.Component.superclass.onRender.apply(this, arguments);
-
- if (this.monitorOrientation) {
- this.el.addCls('x-' + Ext.Viewport.orientation);
- }
-
- if (this.floating) {
- this.setFloating(true);
- }
-
- if (this.draggable) {
- this.setDraggable(this.draggable);
- }
-
- if (this.scroll) {
- this.setScrollable(this.scroll);
- }
- },
-
- afterRender : function() {
- if (this.fullscreen) {
- this.layoutOrientation(Ext.Viewport.orientation, this.width, this.height);
- }
- Ext.Component.superclass.afterRender.call(this);
- },
-
- initEvents : function() {
- Ext.Component.superclass.initEvents.call(this);
-
- if (this.monitorOrientation) {
- Ext.EventManager.onOrientationChange(this.setOrientation, this);
- }
- },
-
- // Template method that can be overriden to perform logic after the panel has layed out itself
- // e.g. Resized the body and positioned all docked items.
- afterComponentLayout : function() {
- var scrollEl = this.scrollEl,
- scroller = this.scroller,
- parentEl;
-
- if (scrollEl) {
- parentEl = scrollEl.parent();
-
- if (scroller.horizontal) {
- scrollEl.setStyle('min-width', parentEl.getWidth(true) + 'px');
- scrollEl.setHeight(parentEl.getHeight(true) || null);
- }
- if (scroller.vertical) {
- scrollEl.setStyle('min-height', parentEl.getHeight(true) + 'px');
- scrollEl.setWidth(parentEl.getWidth(true) || null);
- }
- scroller.updateBoundary(true);
- }
-
- if (this.fullscreen && Ext.is.iPad) {
- Ext.repaint();
- }
- },
-
- layoutOrientation: Ext.emptyFn,
-
- // Inherit docs
- update: function(){
- // We override this here so we can call updateBoundary once the update happens.
- Ext.Component.superclass.update.apply(this, arguments);
- var scroller = this.scroller;
- if (scroller && scroller.updateBoundary){
- scroller.updateBoundary(true);
- }
- },
-
- /**
- * Show the component.
- * @param {Object/String/Boolean} animation (optional) Defaults to false.
- */
- show : function(animation) {
- var rendered = this.rendered;
- if ((this.hidden || !rendered) && this.fireEvent('beforeshow', this) !== false) {
- if (this.anchorEl) {
- this.anchorEl.hide();
- }
- if (!rendered && this.autoRender) {
- this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
- }
- this.hidden = false;
- if (this.rendered) {
- this.onShow(animation);
- this.fireEvent('show', this);
- }
- }
- return this;
- },
-
- /**
- * Show this component relative another component or element.
- * @param {Mixed} alignTo Element or Component
- * @param {Object/String/Boolean} animation
- * @param {Boolean} allowOnSide true to allow this element to be aligned on the left or right.
- * @returns {Ext.Component} this
- */
- showBy : function(alignTo, animation, allowSides, anchor) {
- if (!this.floating) {
- return this;
- }
-
- if (alignTo.isComponent) {
- alignTo = alignTo.el;
- }
- else {
- alignTo = Ext.get(alignTo);
- }
-
- this.x = 0;
- this.y = 0;
-
- this.show(animation);
-
- if (anchor !== false) {
- if (!this.anchorEl) {
- this.anchorEl = this.el.createChild({
- cls: 'x-anchor'
- });
- }
- this.anchorEl.show();
- }
-
- this.alignTo(alignTo, allowSides, 20);
- },
-
- alignTo : function(alignTo, allowSides, offset) {
- // We are going to try and position this component to the alignTo element.
- var alignBox = alignTo.getPageBox(),
- constrainBox = {
- width: window.innerWidth,
- height: window.innerHeight
- },
- size = this.getSize(),
- newSize = {
- width: Math.min(size.width, constrainBox.width),
- height: Math.min(size.height, constrainBox.height)
- },
- position,
- index = 2,
- positionMap = [
- 'tl-bl',
- 't-b',
- 'tr-br',
- 'l-r',
- 'l-r',
- 'r-l',
- 'bl-tl',
- 'b-t',
- 'br-tr'
- ],
- anchorEl = this.anchorEl,
- offsets = [0, offset],
- targetBox, cls,
- onSides = false,
- arrowOffsets = [0, 0],
- alignCenterX = alignBox.left + (alignBox.width / 2),
- alignCenterY = alignBox.top + (alignBox.height / 2);
-
- if (alignCenterX <= constrainBox.width * (1/3)) {
- index = 1;
- arrowOffsets[0] = 25;
- }
- else if (alignCenterX >= constrainBox.width * (2/3)) {
- index = 3;
- arrowOffsets[0] = -30;
- }
-
- if (alignCenterY >= constrainBox.height * (2/3)) {
- index += 6;
- offsets = [0, -offset];
- arrowOffsets[1] = -10;
- }
- // If the alignTo element is vertically in the middle part of the screen
- // we position it left or right.
- else if (allowSides !== false && alignCenterY >= constrainBox.height * (1/3)) {
- index += 3;
- offsets = (index <= 5) ? [offset, 0] : [-offset, 0];
- arrowOffsets = (index <= 5) ? [10, 0] : [-10, 0];
- onSides = true;
- }
- else {
- arrowOffsets[1] = 10;
- }
-
- position = positionMap[index-1];
- targetBox = this.el.getAlignToXY(alignTo, position, offsets);
-
- // If the window is going to be aligned on the left or right of the alignTo element
- // we make sure the height is smaller then the window height, and the width
- if (onSides) {
- if (targetBox[0] < 0) {
- newSize.width = alignBox.left - offset;
- }
- else if (targetBox[0] + newSize.width > constrainBox.width) {
- newSize.width = constrainBox.width - alignBox.right - offset;
- }
- }
- else {
- if (targetBox[1] < 0) {
- newSize.height = alignBox.top - offset;
- }
- else if (targetBox[1] + newSize.height > constrainBox.height) {
- newSize.height = constrainBox.height - alignBox.bottom - offset;
- }
- }
-
- if (newSize.width != size.width) {
- this.setSize(newSize.width);
- }
- else if (newSize.height != size.height) {
- this.setSize(undefined, newSize.height);
- }
-
- targetBox = this.el.getAlignToXY(alignTo, position, offsets);
- this.setPosition(targetBox[0], targetBox[1]);
-
- if (anchorEl) {
- // we are at the top
- anchorEl.removeCls(['x-anchor-bottom', 'x-anchor-left', 'x-anchor-right', 'x-anchor-top']);
- if (offsets[1] == offset) {
- cls = 'x-anchor-top';
- }
- else if (offsets[1] == -offset) {
- cls = 'x-anchor-bottom';
- }
- else if (offsets[0] == offset) {
- cls = 'x-anchor-left';
- }
- else {
- cls = 'x-anchor-right';
- }
- targetBox = anchorEl.getAlignToXY(alignTo, position, arrowOffsets);
- anchorEl.setXY(targetBox);
- anchorEl.addCls(cls);
- }
-
- return position;
- },
-
- /**
- * Show this component centered of its parent or the window
- * This only applies when the component is floating.
- * @param {Boolean} centered True to center, false to remove centering
- * @returns {Ext.Component} this
- */
- setCentered : function(centered, update) {
- this.centered = centered;
-
- if (this.rendered && update) {
- var x, y;
- if (!this.ownerCt) {
- x = (Ext.Element.getViewportWidth() / 2) - (this.getWidth() / 2);
- y = (Ext.Element.getViewportHeight() / 2) - (this.getHeight() / 2);
- }
- else {
- x = (this.ownerCt.getTargetEl().getWidth() / 2) - (this.getWidth() / 2);
- y = (this.ownerCt.getTargetEl().getHeight() / 2) - (this.getHeight() / 2);
- }
- this.setPosition(x, y);
- }
-
- return this;
- },
-
- /**
- * Hide the component
- * @param {Object/String/Boolean} animation (optional) Defaults to false.
- */
- hide : function(animation) {
- if (!this.hidden && this.fireEvent('beforehide', this) !== false) {
- this.hidden = true;
- if (this.rendered) {
- this.onHide(animation, true);
- }
- }
- return this;
- },
-
- // @private
- onShow : function(animation) {
- this.el.show();
-
- Ext.Component.superclass.onShow.call(this, animation);
-
- if (animation === undefined || animation === true) {
- animation = this.showAnimation;
- }
-
- if (this.floating) {
- this.el.dom.parentNode || this.el.appendTo(document.body);
-
- if (animation) {
- this.el.setStyle('opacity', 0.01);
- }
-
- if (this.centered) {
- this.setCentered(true, true);
- }
- else {
- this.setPosition(this.x, this.y);
- }
-
- if (this.modal) {
- this.el.parent().mask(null, 'x-mask-gray');
- }
-
- if (this.hideOnMaskTap) {
- Ext.getDoc().on('touchstart', this.onFloatingTouchStart, this, {capture: true, subsequent: true});
- }
- }
-
- if (animation) {
- //this.el.setStyle('opacity', 0.01);
-
- Ext.Anim.run(this, animation, {
- out: false,
- autoClear: true
- });
-
- this.showAnimation = animation;
- }
- },
-
- // @private
- onFloatingTouchStart: function(e) {
- if (!this.el.contains(e.target)) {
- this.hide();
- if (this.stopMaskTapEvent || Ext.fly(e.target).hasCls('x-mask')) {
- e.stopEvent();
- }
- }
- },
-
- // @private
- onHide : function(animation, fireHideEvent) {
- if (animation === undefined || animation === true) {
- animation = this.showAnimation;
- }
-
- if (this.hideOnMaskTap && this.floating) {
- Ext.getDoc().un('touchstart', this.onFloatingTouchStart, this, {capture: true, subsequent: true});
- }
-
- if (animation) {
- Ext.Anim.run(this, animation, {
- out: true,
- reverse: true,
- autoClear: true,
- scope: this,
- fireHideEvent: fireHideEvent,
- after: this.doHide
- });
- } else {
- this.doHide(null, {fireHideEvent: fireHideEvent});
- }
- },
-
- // private
- doHide : function(el, options) {
- var parent = this.el.parent();
-
- this.el.hide();
-
- if (parent && this.floating && this.modal) {
- parent.unmask();
- }
- if (options && options.fireHideEvent) {
- this.fireEvent('hide', this);
- }
- },
-
- /**
- * Sets a Component as scrollable.
- * @param {Mixed} config
- * Acceptable values are a Ext.Scroller configuration, 'horizontal', 'vertical', 'both', and false
- */
- setScrollable : function(config) {
- if (!this.rendered) {
- this.scroll = config;
- return;
- }
-
- Ext.destroy(this.scroller);
- this.scroller = null;
-
- if (config !== false) {
- var direction = Ext.isObject(config) ? config.direction: config;
- config = Ext.apply({},
- Ext.isObject(config) ? config: {}, {
-// momentum: true,
- direction: direction
- });
-
- if (!this.scrollEl) {
- this.scrollEl = this.getTargetEl().createChild();
- this.originalGetTargetEl = this.getTargetEl;
- this.getTargetEl = function() {
- return this.scrollEl;
- };
- }
-
- this.scroller = (new Ext.util.ScrollView(this.scrollEl, config)).scroller;
- }
- else {
- this.getTargetEl = this.originalGetTargetEl;
- }
- },
-
- /**
- * Sets a Component as floating.
- * @param {Boolean} floating
- * @param {Boolean} autoShow
- */
- setFloating : function(floating, autoShow) {
- this.floating = !!floating;
- this.hidden = true;
- if (this.rendered) {
- if (floating !== false) {
- this.el.addCls(this.floatingCls);
- if (autoShow) {
- this.show();
- }
- }
- else {
- this.el.removeCls(this.floatingCls);
- Ext.getDoc().un('touchstart', this.onFloatingTouchStart, this);
- }
- }
- else if (floating !== false) {
- if (this.layoutOnOrientationChange !== false) {
- this.layoutOnOrientationChange = true;
- }
- this.autoRender = true;
- }
- },
-
- /**
- * Sets a Component as draggable.
- * @param {Boolean/Mixed} draggable On first call, this can be a config object for {@link Ext.util.Draggable}.
- * Afterwards, if set to false, the existing draggable object will be disabled
- * @param {Boolean} autoShow
- */
- setDraggable : function(draggable, autoShow) {
- this.isDraggable = draggable;
-
- if (this.rendered) {
- if (draggable === false) {
- if (this.dragObj) {
- this.dragObj.disable();
- }
- } else {
- if (autoShow) {
- this.show();
- }
- if (this.dragObj) {
- this.dragObj.enable();
- } else {
- this.dragObj = new Ext.util.Draggable(this.el, Ext.apply({}, this.draggable || {}));
- this.relayEvents(this.dragObj, ['dragstart', 'beforedragend' ,'drag', 'dragend']);
- }
- }
- }
- },
-
- /**
- * Sets the orientation for the Panel.
- * @param {String} orientation 'landscape' or 'portrait'
- * @param {Number/String} width New width of the Panel.
- * @param {Number/String} height New height of the Panel.
- */
- setOrientation : function(orientation, w, h) {
- if (this.fireEvent('beforeorientationchange', this, orientation, w, h) !== false) {
- if (this.orientation != orientation) {
- this.el.removeCls('x-' + this.orientation);
- this.el.addCls('x-' + orientation);
- }
-
- this.orientation = orientation;
- this.layoutOrientation(orientation, w, h);
-
- if (this.fullscreen) {
- this.setSize(w, h);
- }
- else if (this.layoutOnOrientationChange) {
- this.doComponentLayout();
- }
-
- if (this.floating && this.centered) {
- this.setCentered(true, true);
- }
-
- this.onOrientationChange(orientation, w, h);
- this.fireEvent('orientationchange', this, orientation, w, h);
- }
- },
-
- // @private
- onOrientationChange : Ext.emptyFn,
-
- beforeDestroy : function() {
- if (this.floating && this.modal && !this.hidden) {
- this.el.parent().unmask();
- }
- Ext.destroy(this.scroller);
- Ext.Component.superclass.beforeDestroy.call(this);
- },
-
- onDestroy : function() {
- if (this.monitorOrientation && Ext.EventManager.orientationEvent) {
- Ext.EventManager.orientationEvent.removeListener(this.setOrientation, this);
- }
-
- Ext.Component.superclass.onDestroy.call(this);
- }
-});
-
-// @xtype box
-Ext.BoxComponent = Ext.Component;
-
-Ext.reg('component', Ext.Component);
-Ext.reg('box', Ext.BoxComponent);
-/**
- * @class Ext.lib.Container
- * @extends Ext.Component
- * Shared Container class
- */
-Ext.lib.Container = Ext.extend(Ext.Component, {
- /**
- * @cfg {String/Object} layout
- * <p><b>*Important</b>: In order for child items to be correctly sized and
- * positioned, typically a layout manager <b>must</b> be specified through
- * the <code>layout</code> configuration option.</p>
- * <br><p>The sizing and positioning of child {@link items} is the responsibility of
- * the Container's layout manager which creates and manages the type of layout
- * you have in mind. For example:</p>
- * <p>If the {@link #layout} configuration is not explicitly specified for
- * a general purpose container (e.g. Container or Panel) the
- * {@link Ext.layout.AutoContainerLayout default layout manager} will be used
- * which does nothing but render child components sequentially into the
- * Container (no sizing or positioning will be performed in this situation).</p>
- * <br><p><b><code>layout</code></b> may be specified as either as an Object or
- * as a String:</p><div><ul class="mdetail-params">
- *
- * <li><u>Specify as an Object</u></li>
- * <div><ul class="mdetail-params">
- * <li>Example usage:</li>
- * <pre><code>
-layout: {
- type: 'vbox',
- align: 'left'
-}
- </code></pre>
- *
- * <li><code><b>type</b></code></li>
- * <br/><p>The layout type to be used for this container. If not specified,
- * a default {@link Ext.layout.ContainerLayout} will be created and used.</p>
- * <br/><p>Valid layout <code>type</code> values are:</p>
- * <div class="sub-desc"><ul class="mdetail-params">
- * <li><code><b>{@link Ext.layout.ContainerLayout auto}</b></code> <b>Default</b></li>
- * <li><code><b>{@link Ext.layout.CardLayout card}</b></code></li>
- * <li><code><b>{@link Ext.layout.FitLayout fit}</b></code></li>
- * <li><code><b>{@link Ext.layout.HBoxLayout hbox}</b></code></li>
- * <li><code><b>{@link Ext.layout.VBoxLayout vbox}</b></code></li>
- * </ul></div>
- *
- * <li>Layout specific configuration properties</li>
- * <br/><p>Additional layout specific configuration properties may also be
- * specified. For complete details regarding the valid config options for
- * each layout type, see the layout class corresponding to the <code>type</code>
- * specified.</p>
- *
- * </ul></div>
- *
- * <li><u>Specify as a String</u></li>
- * <div><ul class="mdetail-params">
- * <li>Example usage:</li>
- * <pre><code>
-layout: {
- type: 'vbox',
- padding: '5',
- align: 'left'
-}
- </code></pre>
- * <li><code><b>layout</b></code></li>
- * <br/><p>The layout <code>type</code> to be used for this container (see list
- * of valid layout type values above).</p><br/>
- * <br/><p>Additional layout specific configuration properties. For complete
- * details regarding the valid config options for each layout type, see the
- * layout class corresponding to the <code>layout</code> specified.</p>
- * </ul></div></ul></div>
- */
-
- /**
- * @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.CardLayout} and
- * {@link Ext.layout.FitLayout}). Related to {@link Ext.layout.ContainerLayout#activeItem}.
- */
- /**
- * @cfg {Object/Array} items
- * <pre><b>** IMPORTANT</b>: be sure to <b>{@link #layout specify a <code>layout</code>} if needed ! **</b></pre>
- * <p>A single item, or an array of child Components to be added to this container,
- * for example:</p>
- * <pre><code>
-// specifying a single item
-items: {...},
-layout: 'fit', // specify a layout!
-
-// specifying multiple items
-items: [{...}, {...}],
-layout: 'hbox', // specify a layout!
- </code></pre>
- * <p>Each item may be:</p>
- * <div><ul class="mdetail-params">
- * <li>any type of object based on {@link Ext.Component}</li>
- * <li>a fully instanciated object or</li>
- * <li>an object literal that:</li>
- * <div><ul class="mdetail-params">
- * <li>has a specified <code>{@link Ext.Component#xtype xtype}</code></li>
- * <li>the {@link Ext.Component#xtype} specified is associated with the Component
- * desired and should be chosen from one of the available xtypes as listed
- * in {@link Ext.Component}.</li>
- * <li>If an <code>{@link Ext.Component#xtype xtype}</code> is not explicitly
- * specified, the {@link #defaultType} for that Container is used.</li>
- * <li>will be "lazily instanciated", avoiding the overhead of constructing a fully
- * instanciated Component object</li>
- * </ul></div></ul></div>
- * <p><b>Notes</b>:</p>
- * <div><ul class="mdetail-params">
- * <li>Ext uses lazy rendering. Child Components will only be rendered
- * should it become necessary. Items are automatically laid out when they are first
- * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</li>
- * <li>Do not specify <code>{@link Ext.Panel#contentEl contentEl}</code>/
- * <code>{@link Ext.Panel#html html}</code> with <code>items</code>.</li>
- * </ul></div>
- */
- /**
- * @cfg {Object|Function} defaults
- * <p>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.</p>
- * <p>If an added item is a config object, and <b>not</b> an instantiated Component, then the default properties are
- * unconditionally applied. If the added item <b>is</b> an instantiated Component, then the default properties are
- * applied conditionally so as not to override existing properties in the item.</p>
- * <p>If the defaults option is specified as a function, then the function will be called using this Container as the
- * scope (<code>this</code> 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.</p>
- * <p>For example, to automatically apply padding to the body of each of a set of
- * contained {@link Ext.Panel} items, you could pass: <code>defaults: {bodyStyle:'padding:15px'}</code>.</p>
- * <p>Usage:</p><pre><code>
-defaults: { // defaults are applied to items, not the container
- autoScroll:true
-},
-items: [
- {
- xtype: 'panel', // defaults <b>do not</b> 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 <b>do</b> have precedence over options
- id: 'panel2', // options in components, so the defaults
- autoScroll: false // will be applied here, panel2 will be autoScroll:true.
- })
-]
- </code></pre>
- */
-
-
- /** @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 {String} defaultType
- * <p>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.</p>
- * <p>Defaults to <code>'panel'</code>.</p>
- */
- defaultType: 'panel',
-
- isContainer : true,
-
- baseCls: 'x-container',
-
- /**
- * @cfg {Array} bubbleEvents
- * <p>An array of events that, when fired, should be bubbled to any parent container.
- * See {@link Ext.util.Observable#enableBubble}.
- * Defaults to <code>['add', 'remove']</code>.
- */
- bubbleEvents: ['add', 'remove'],
-
- // @private
- initComponent : function(){
- 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',
- /**
- * @event beforecardswitch
- * Fires before this container switches the active card. This event
- * is only available if this container uses a CardLayout. Note that
- * TabPanel and Carousel both get a CardLayout by default, so both
- * will have this event.
- * A handler can return false to cancel the card switch.
- * @param {Ext.Container} this
- * @param {Ext.Component} newCard The card that will be switched to
- * @param {Ext.Component} oldCard The card that will be switched from
- * @param {Number} index The index of the card that will be switched to
- * @param {Boolean} animated True if this cardswitch will be animated
- */
- 'beforecardswitch',
- /**
- * @event cardswitch
- * Fires after this container switches the active card. If the card
- * is switched using an animation, this event will fire after the
- * animation has finished. This event is only available if this container
- * uses a CardLayout. Note that TabPanel and Carousel both get a CardLayout
- * by default, so both will have this event.
- * @param {Ext.Container} this
- * @param {Ext.Component} newCard The card that has been switched to
- * @param {Ext.Component} oldCard The card that has been switched from
- * @param {Number} index The index of the card that has been switched to
- * @param {Boolean} animated True if this cardswitch was animated
- */
- 'cardswitch'
- );
-
- // layoutOnShow stack
- this.layoutOnShow = new Ext.util.MixedCollection();
- Ext.lib.Container.superclass.initComponent.call(this);
- this.initItems();
- },
-
- // @private
- initItems : function() {
- var items = this.items;
-
- /**
- * The MixedCollection containing all the child items of this container.
- * @property items
- * @type Ext.util.MixedCollection
- */
- this.items = new Ext.util.MixedCollection(false, this.getComponentId);
-
- if (items) {
- if (!Ext.isArray(items)) {
- items = [items];
- }
-
- this.add(items);
- }
- },
-
- // @private
- afterRender : function() {
- this.getLayout();
- Ext.lib.Container.superclass.afterRender.apply(this, arguments);
- },
-
- // @private
- setLayout : function(layout) {
- var currentLayout = this.layout;
-
- if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
- currentLayout.setOwner(null);
- }
-
- this.layout = layout;
- layout.setOwner(this);
- },
-
- /**
- * Returns the {@link Ext.layout.ContainerLayout layout} instance currently associated with this Container.
- * If a layout has not been instantiated yet, that is done first
- * @return {Ext.layout.ContainerLayout} The layout
- */
- getLayout : function() {
- if (!this.layout || !this.layout.isLayout) {
- this.setLayout(Ext.layout.LayoutManager.create(this.layout, 'autocontainer'));
- }
-
- return this.layout;
- },
-
- /**
- * 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.
- * @return {Ext.Container} this
- */
- doLayout : function() {
- var layout = this.getLayout();
-
- if (this.rendered && layout) {
- layout.layout();
- }
-
- return this;
- },
-
- // @private
- afterLayout : function(layout) {
- this.fireEvent('afterlayout', this, layout);
- },
-
- // @private
- prepareItems : function(items, applyDefaults) {
- if (!Ext.isArray(items)) {
- items = [items];
- }
-
- // Make sure defaults are applied and item is initialized
- var item, i, ln;
-
- for (i = 0, ln = items.length; i < ln; i++) {
- item = items[i];
-
- if (applyDefaults) {
- item = this.applyDefaults(item);
- }
-
- items[i] = this.lookupComponent(item);
- }
-
- return items;
- },
-
- // @private
- applyDefaults : function(config) {
- var defaults = this.defaults;
-
- if (defaults) {
- if (Ext.isFunction(defaults)) {
- defaults = defaults.call(this, config);
- }
-
- if (Ext.isString(config)) {
- config = Ext.ComponentMgr.get(config);
- Ext.apply(config, defaults);
- } else if (!config.isComponent) {
- Ext.applyIf(config, defaults);
- } else {
- Ext.apply(config, defaults);
- }
- }
-
- return config;
- },
-
- // @private
- lookupComponent : function(comp) {
- if (Ext.isString(comp)) {
- return Ext.ComponentMgr.get(comp);
- } else {
- return this.createComponent(comp);
- }
- return comp;
- },
-
- // @private
- createComponent : function(config, defaultType) {
- if (config.isComponent) {
- return config;
- }
-
- // // add in ownerCt at creation time but then immediately
- // // remove so that onBeforeAdd can handle it
- // var component = Ext.create(Ext.apply({ownerCt: this}, config), defaultType || this.defaultType);
- //
- // delete component.initialConfig.ownerCt;
- // delete component.ownerCt;
-
- return Ext.create(config, defaultType || this.defaultType);
- },
-
- // @private - used as the key lookup function for the items collection
- getComponentId : function(comp) {
- return comp.getItemId();
- },
-
- /**
- * <p>Adds {@link Ext.Component Component}(s) to this Container.</p>
- * <br><p><b>Description</b></u> :
- * <div><ul class="mdetail-params">
- * <li>Fires the {@link #beforeadd} event before adding</li>
- * <li>The Container's {@link #defaults default config values} will be applied
- * accordingly (see <code>{@link #defaults}</code> for details).</li>
- * <li>Fires the {@link #add} event after the component has been added.</li>
- * </ul></div>
- * <br><p><b>Notes</b></u> :
- * <div><ul class="mdetail-params">
- * <li>If the Container is <i>already rendered</i> when <code>add</code>
- * is called, you may need to call {@link #doLayout} to refresh the view which causes
- * any unrendered child Components to be rendered. This is required so that you can
- * <code>add</code> multiple child components if needed while only refreshing the layout
- * once. For example:<pre><code>
-var tb = new {@link Ext.Toolbar}();
-tb.render(document.body); // toolbar is rendered
-tb.add({text:'Button 1'}); // add multiple items ({@link #defaultType} for {@link Ext.Toolbar Toolbar} is 'button')
-tb.add({text:'Button 2'});
-tb.{@link #doLayout}(); // refresh the layout
- * </code></pre></li>
- * <li><i>Warning:</i> Containers directly managed by the BorderLayout layout manager
- * may not be removed or added. See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
- * for more details.</li>
- * </ul></div>
- * @param {...Object/Array} component
- * <p>Either one or more Components to add or an Array of Components to add. See
- * <code>{@link #items}</code> for additional information.</p>
- * @return {Ext.Component/Array} The Components that were added.
- */
- add : function() {
- var args = Array.prototype.slice.call(arguments),
- index = -1;
-
- if (typeof args[0] == 'number') {
- index = args.shift();
- }
-
- var hasMultipleArgs = args.length > 1;
-
- if (hasMultipleArgs || Ext.isArray(args[0])) {
- var items = hasMultipleArgs ? args : args[0],
- results = [],
- i, ln, item;
-
- for (i = 0, ln = items.length; i < ln; i++) {
- item = items[i];
- if (!item) {
- throw "Trying to add a null item as a child of Container with itemId/id: " + this.getItemId();
- }
-
- if (index != -1) {
- item = this.add(index + i, item);
- } else {
- item = this.add(item);
- }
- results.push(item);
- }
-
- return results;
- }
-
- var cmp = this.prepareItems(args[0], true)[0];
- index = (index !== -1) ? index : this.items.length;
-
- if (this.fireEvent('beforeadd', this, cmp, index) !== false && this.onBeforeAdd(cmp) !== false) {
- this.items.insert(index, cmp);
- cmp.onAdded(this, index);
- this.onAdd(cmp, index);
- this.fireEvent('add', this, cmp, index);
- }
-
- return cmp;
- },
-
- onAdd : Ext.emptyFn,
- onRemove : Ext.emptyFn,
-
- /**
- * 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.<br><br>
- * Ext uses lazy rendering, and will only render the inserted Component should
- * it become necessary.<br><br>
- * 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.<br><br>
- * 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){
- return this.add(index, comp);
- },
-
- // @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) {
- var c = this.getComponent(comp);
- if (!c) {
- console.warn("Attempted to remove a component that does not exist. Ext.Container: remove takes an argument of the component to remove. cmp.remove() is incorrect usage.");
- }
-
- if (c && this.fireEvent('beforeremove', this, c) !== false) {
- this.doRemove(c, autoDestroy);
- this.fireEvent('remove', this, c);
- }
-
- return c;
- },
-
- // @private
- doRemove : function(component, autoDestroy) {
- var layout = this.layout,
- hasLayout = layout && this.rendered;
-
- this.items.remove(component);
- component.onRemoved();
-
- if (hasLayout) {
- layout.onRemove(component);
- }
-
- this.onRemove(component, autoDestroy);
-
- if (autoDestroy === true || (autoDestroy !== false && this.autoDestroy)) {
- component.destroy();
- }
-
- if (hasLayout && !autoDestroy) {
- layout.afterRemove(component);
- }
- },
-
- /**
- * 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) {
- var item,
- removeItems = this.items.items.slice(),
- items = [],
- ln = removeItems.length,
- i;
-
- for (i = 0; i < ln; i++) {
- item = removeItems[i];
- this.remove(item, autoDestroy);
-
- if (item.ownerCt !== this) {
- items.push(item);
- }
- }
-
- return items;
- },
-
- // Used by ComponentQuery to retrieve all of the items
- // which can potentially be considered a child of this Container.
- // This should be overriden by components which have child items
- // that are not contained in items. For example dockedItems, menu, etc
- getRefItems : function(deep) {
- var items = this.items.items.slice(),
- ln = items.length,
- i, item;
-
- if (deep) {
- for (i = 0; i < ln; i++) {
- item = items[i];
-
- if (item.getRefItems) {
- items = items.concat(item.getRefItems(true));
- }
- }
- }
-
- return items;
- },
-
- /**
- * Examines this container's <code>{@link #items}</code> <b>property</b>
- * and gets a direct child component of this container.
- * @param {String/Number} comp This parameter may be any of the following:
- * <div><ul class="mdetail-params">
- * <li>a <b><code>String</code></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
- * or <code>{@link Ext.Component#id id}</code> of the child component </li>
- * <li>a <b><code>Number</code></b> : representing the position of the child component
- * within the <code>{@link #items}</code> <b>property</b></li>
- * </ul></div>
- * <p>For additional information see {@link Ext.util.MixedCollection#get}.
- * @return Ext.Component The component (if found).
- */
- getComponent : function(comp) {
- if (Ext.isObject(comp)) {
- comp = comp.getItemId();
- }
-
- return this.items.get(comp);
- },
-
- /**
- * Retrieves all descendant components which match the passed selector.
- * Executes an Ext.ComponentQuery.query using this container as its root.
- * @param {String} selector Selector complying to an Ext.ComponentQuery selector
- * @return {Array} Ext.Component's which matched the selector
- */
- query : function(selector) {
- return Ext.ComponentQuery.query(selector, this);
- },
-
- /**
- * Retrieves the first direct child of this container which matches the passed selector.
- * The passed in selector must comply with an Ext.ComponentQuery selector.
- * @param {String} selector An Ext.ComponentQuery selector
- * @return Ext.Component
- */
- child : function(selector) {
- return this.query('> ' + selector)[0] || null;
- },
-
-
- /**
- * Retrieves the first descendant of this container which matches the passed selector.
- * The passed in selector must comply with an Ext.ComponentQuery selector.
- * @param {String} selector An Ext.ComponentQuery selector
- * @return Ext.Component
- */
- down : function(selector) {
- return this.query(selector)[0] || null;
- },
-
- // inherit docs
- show : function() {
- Ext.lib.Container.superclass.show.apply(this, arguments);
-
- var layoutCollection = this.layoutOnShow,
- ln = layoutCollection.getCount(),
- i = 0,
- needsLayout,
- item;
-
- for (; i < ln; i++) {
- item = layoutCollection.get(i);
- needsLayout = item.needsLayout;
-
- if (Ext.isObject(needsLayout)) {
- item.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize);
- }
- }
-
- layoutCollection.clear();
- },
-
- // @private
- beforeDestroy : function() {
- var items = this.items,
- c;
-
- if (items) {
- while ((c = items.first())) {
- this.doRemove(c, true);
- }
- }
-
- Ext.destroy(this.layout);
- Ext.lib.Container.superclass.beforeDestroy.call(this);
- }
-});
-
-// Declare here so we can test
-Ext.Container = Ext.extend(Ext.lib.Container, {});
-Ext.reg('container', Ext.Container);
-
-/**
- * @class Ext.Container
- * @extends Ext.lib.Container
- * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
- * basic behavior of containing items, namely adding, inserting and removing items.</p>
- *
- * <p><u><b>Layout</b></u></p>
- * <p>Container classes delegate the rendering of child Components to a layout
- * manager class which must be configured into the Container using the
- * <code><b>{@link #layout}</b></code> configuration property.</p>
- * <p>When either specifying child <code>{@link #items}</code> of a Container,
- * or dynamically {@link #add adding} Components to a Container, remember to
- * consider how you wish the Container to arrange those child elements, and
- * whether those child elements need to be sized using one of Ext's built-in
- * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
- * {@link Ext.layout.AutoContainerLayout AutoContainerLayout} scheme which only
- * renders child components, appending them one after the other inside the
- * Container, and <b>does not apply any sizing</b> at all.</p>
- * <p>A common mistake is when a developer neglects to specify a
- * <b><code>{@link #layout}</code></b>. If a Container is left to use the default
- * {@link Ext.layout.AutoContainerLayout AutoContainerLayout} scheme, none of its
- * child components will be resized, or changed in any way when the Container
- * is resized.</p>
- * @xtype container
- */
-Ext.Container = Ext.extend(Ext.lib.Container, {
- /**
- * @cfg {String/Mixed} cardSwitchAnimation
- * Animation to be used during transitions of cards. Note this only works when this container has a CardLayout.
- * Any valid value from Ext.anims can be used ('fade', 'slide', 'flip', 'cube', 'pop', 'wipe').
- * Defaults to <tt>null</tt>.
- */
- cardSwitchAnimation: null,
-
- initComponent: function() {
- if (this.scroll) {
- this.fields = new Ext.util.MixedCollection();
-
- this.fields.on({
- add: this.onFieldAdd,
- remove: this.onFieldRemove,
- scope: this
- });
-
- this.on({
- add: this.onItemAdd,
- remove: this.onItemRemove,
- scope: this
- });
- }
-
-
- Ext.Container.superclass.initComponent.apply(this, arguments);
- },
-
- afterRender: function() {
- Ext.Container.superclass.afterRender.apply(this, arguments);
-
- if (this.scroller) {
- if (Ext.is.Android && this.containsFormFields) {
- this.scroller.setUseCssTransform(false);
- }
-
- this.scroller.on('scrollstart', this.onFieldScrollStart, this);
- }
- },
-
- onFieldScrollStart: function() {
- var focusedField = this.focusedField;
-
- if (focusedField && Ext.is.iOS) {
- focusedField.blur();
-// Ext.Viewport.scrollToTop();
- }
- },
-
- onItemAdd: function(me, item) {
- this.fields.addAll(Ext.ComponentQuery.query('[isField]', item));
- },
-
- onItemRemove: function(me, item) {
- this.fields.removeAll(Ext.ComponentQuery.query('[isField]', item));
- },
-
- onFieldAdd: function(key, field) {
- this.handleFieldEventListener(true, field);
- },
-
- onFieldRemove: function(key, field) {
- this.handleFieldEventListener(false, field);
- },
-
- handleFieldEventListener: function(isAdding, item) {
- if (!this.fieldEventWrap)
- this.fieldEventWrap = {};
-
- if (['textfield', 'passwordfield', 'emailfield',
- 'textareafield', 'searchfield', 'urlfield',
- 'numberfield', 'spinnerfield'].indexOf(item.xtype) !== -1) {
- if (isAdding) {
- this.fieldEventWrap[item.id] = {
- beforefocus: function(e) {this.onFieldBeforeFocus(item, e);},
- focus: function(e) {this.onFieldFocus(item, e);},
- blur: function(e) {this.onFieldBlur(item, e);},
- keyup: function(e) {this.onFieldKeyUp(item, e);},
- scope: this
- };
-
- this.containsFormFields = true;
- }
-
- item[isAdding ? 'on' : 'un'](this.fieldEventWrap[item.id]);
-
- if (!isAdding) {
- delete this.fieldEventWrap[item.id];
- }
- }
- },
-
- onFieldKeyUp: function(field, e) {
- this.resetLastWindowScroll();
- },
-
- onFieldBeforeFocus: function(field, e) {
- this.focusingField = field;
- },
-
- getLastWindowScroll: function() {
- if (!this.lastWindowScroll) {
- this.resetLastWindowScroll();
- }
-
- return {x: this.lastWindowScroll.x, y: this.lastWindowScroll.y};
- },
-
- resetLastWindowScroll: function() {
- this.lastWindowScroll = {
- x: window.pageXOffset,
- y: window.pageYOffset
- };
- },
-
- adjustScroller: function(offset) {
- var scroller = this.getClosestScroller(),
- windowScroll = this.getLastWindowScroll();
-
- scroller.setOffset(offset);
-
- // Keep the window in the previous scroll position
- if (Ext.is.iOS) {
-// window.scrollTo(windowScroll.x, windowScroll.y || -1);
-// } else {
- window.scrollTo(windowScroll.x, windowScroll.y);
- }
-
- this.resetLastWindowScroll();
- },
-
- onFieldFocus: function(field, e) {
-
- var scroller = this.getClosestScroller(),
- containerRegion = Ext.util.Region.from(scroller.containerBox),
- fieldRegion = field.fieldEl.getPageBox(true);
-
- // Focus by mouse click or finger tap, or not iOS
- if (this.focusingField == field || !Ext.is.iOS) {
- if (Ext.is.iOS && window.pageYOffset == 0) {
- window.scrollTo(0, 0);
- }
-
- var adjustment = new Ext.util.Offset();
-
- if (fieldRegion.left < containerRegion.left) {
- adjustment.x = containerRegion.left - fieldRegion.left;
- }
-
- if (fieldRegion.top < containerRegion.top) {
- adjustment.y = containerRegion.top - fieldRegion.top;
- }
-
- if (!adjustment.isZero()) {
- var windowScroll = this.getLastWindowScroll();
-
- scroller.scrollBy(adjustment);
-
- if (Ext.is.iOS) {
- window.scrollTo(windowScroll.x, windowScroll.y);
- }
-
- this.resetLastWindowScroll();
- }
- }
- // Focus by next / previous / tab
- else {
- if (this.lastFocusedField) {
- var deltaY = fieldRegion.top - this.lastFocusedField.fieldEl.getY(),
- offsetY = scroller.offset.y - deltaY,
- selfHandling = false;
-
- if (!containerRegion.contains(fieldRegion) &&
- (offsetY != 0 || (offsetY == 0 && scroller.offset.y != 0))) {
- selfHandling = true;
- }
-
- if (offsetY > 0) {
- offsetY = 0;
- }
-
- if (selfHandling) {
- this.adjustScroller(new Ext.util.Offset(
- scroller.offset.x, offsetY
- ));
- }
- }
- }
-
- this.resetLastWindowScroll();
-
- this.lastFocusedField = field;
- this.focusedField = field;
- this.focusingField = null;
- },
-
- getClosestScroller: function() {
- if (!this.closestScroller) {
- this.closestScroller = this.scroller || this.el.getScrollParent();
- }
-
- return this.closestScroller;
- },
-
- onFieldBlur: function(field, e) {
- if (this.focusingField == field) {
- this.focusingField = null;
- }
-
- if (this.focusedField == field) {
- this.focusedField = null;
- }
- },
-
- // @private
- afterLayout : function(layout) {
- if (this.floating && this.centered) {
- this.setCentered(true, true);
- }
-
- if (this.scroller) {
- this.scroller.updateBoundary();
- }
- Ext.Container.superclass.afterLayout.call(this, layout);
- },
-
- /**
- * Returns the current activeItem for the layout (only for a card layout)
- * @return {activeItem} activeItem Current active component
- */
- getActiveItem : function() {
- if (this.layout && this.layout.type == 'card') {
- return this.layout.activeItem;
- }
- else {
- return null;
- }
- },
-
- /**
- * Allows you to set the active card in this container. This
- * method is only available if the container uses a CardLayout.
- * Note that a Carousel and TabPanel both get a CardLayout
- * automatically, so both of those components are able to use this method.
- * @param {Ext.Component/Number/Object} card The card you want to be made active. A number
- * is interpreted as a card index. An object will be converted to a Component using the
- * objects xtype property, then added to the container and made active. Passing a Component
- * will make sure the component is a child of this container, and then make it active.
- * @param {String/Object} cardSwitchAnimation (optional) The cardSwitchAnimation used to switch between the cards.
- * This can be an animation type string or an animation configuration object.
- * @return {Ext.Container} this
- */
- setActiveItem : function(card, animation) {
- this.layout.setActiveItem(card, animation);
- return this;
- },
-
-
- /**
- * A template method that can be implemented by subclasses of
- * Container. By returning false we can cancel the card switch.
- * @param {Ext.Component} newCard The card that will be switched to
- * @param {Ext.Component} oldCard The card that will be switched from
- * @param {Number} newIndex The Container index position of the selected card
- * @param {Boolean} animated True if this cardswitch will be animated
- * @private
- */
- onBeforeCardSwitch : function(newCard, oldCard, newIndex, animated) {
- return this.fireEvent('beforecardswitch', this, newCard, oldCard, newIndex, animated);
- },
-
- /**
- * A template method that can be implemented by subclasses of
- * Container. If the card is switched using an animation, this method
- * will be called after the animation has finished.
- * @param {Ext.Component} newCard The card that has been switched to
- * @param {Ext.Component} oldCard The card that has been switched from
- * @param {Number} newIndex The Container index position of the selected card
- * @param {Boolean} animated True if this cardswitch was animated
- * @private
- */
- onCardSwitch : function(newCard, oldCard, newIndex, animated) {
- return this.fireEvent('cardswitch', this, newCard, oldCard, newIndex, animated);
- },
-
- /**
- * Disable this container by masking out
- */
- disable: function() {
- Ext.Container.superclass.disable.call(this);
- this.el.mask(null, 'x-mask-gray');
- },
-
- /**
- * Enable this container by removing mask
- */
- enable: function() {
- Ext.Container.superclass.enable.call(this);
- this.el.unmask();
- }
-});
-
-Ext.reg('container', Ext.Container);
-
-/**
- * @class Ext.lib.Panel
- * @extends Ext.Container
- * Shared Panel class
- */
-Ext.lib.Panel = Ext.extend(Ext.Container, {
- /**
- * @cfg {String} baseCls
- * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
- */
- baseCls : 'x-panel',
-
- /**
- * @cfg {Number/Boolean} bodyPadding
- * A shortcut for setting a padding style on the body element. The value can either be
- * a number to be applied to all sides, or a normal css string describing padding.
- * Defaults to <tt>undefined</tt>.
- */
-
- /**
- * @cfg {Number/Boolean} bodyMargin
- * A shortcut for setting a margin style on the body element. The value can either be
- * a number to be applied to all sides, or a normal css string describing margins.
- * Defaults to <tt>undefined</tt>.
- */
-
- /**
- * @cfg {Number/Boolean} bodyBorder
- * A shortcut for setting a border style on the body element. The value can either be
- * a number to be applied to all sides, or a normal css string describing borders.
- * Defaults to <tt>undefined</tt>.
- */
-
- isPanel: true,
-
- componentLayout: 'dock',
-
- renderTpl: ['<div class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>></div>'],
-
- /**
- * @cfg {Object/Array} dockedItems
- * A component or series of components to be added as docked items to this panel.
- * The docked items can be docked to either the top, right, left or bottom of a panel.
- * This is typically used for things like toolbars or tab bars:
- * <pre><code>
-var panel = new Ext.Panel({
- fullscreen: true,
- dockedItems: [{
- xtype: 'toolbar',
- dock: 'top',
- items: [{
- text: 'Docked to the bottom'
- }]
- }]
-});</pre></code>
- */
-
- initComponent : function() {
- this.addEvents(
- /**
- * @event bodyresize
- * Fires after the Panel has been resized.
- * @param {Ext.Panel} p the Panel which has been resized.
- * @param {Number} width The Panel body's new width.
- * @param {Number} height The Panel body's new height.
- */
- 'bodyresize'
- // // inherited
- // 'activate',
- // // inherited
- // 'deactivate'
- );
-
- Ext.applyIf(this.renderSelectors, {
- body: '.' + this.baseCls + '-body'
- });
-
- Ext.lib.Panel.superclass.initComponent.call(this);
- },
-
- // @private
- initItems : function() {
- Ext.lib.Panel.superclass.initItems.call(this);
-
- var items = this.dockedItems;
- this.dockedItems = new Ext.util.MixedCollection(false, this.getComponentId);
- if (items) {
- this.addDocked(items);
- }
- },
-
- /**
- * Finds a docked component by id, itemId or position
- * @param {String/Number} comp The id, itemId or position of the child component (see {@link #getComponent} for details)
- * @return {Ext.Component} The component (if found)
- */
- getDockedComponent: function(comp) {
- if (Ext.isObject(comp)) {
- comp = comp.getItemId();
- }
- return this.dockedItems.get(comp);
- },
-
- /**
- * Attempts a default component lookup (see {@link Ext.Container#getComponent}). If the component is not found in the normal
- * items, the dockedItems are searched and the matched component (if any) returned (see {@loink #getDockedComponent}).
- * @param {String/Number} comp The docked component id or itemId to find
- * @return {Ext.Component} The docked component, if found
- */
- getComponent: function(comp) {
- var component = Ext.lib.Panel.superclass.getComponent.call(this, comp);
- if (component == undefined) {
- component = this.getDockedComponent(comp);
- }
- return component;
- },
-
- /**
- * Function description
- * @return {String} A CSS style string with style, padding, margin and border.
- * @private
- */
- initBodyStyles: function() {
- var bodyStyle = Ext.isString(this.bodyStyle) ? this.bodyStyle.split(';') : [],
- Element = Ext.Element;
-
- if (this.bodyPadding != undefined) {
- bodyStyle.push('padding: ' + Element.unitizeBox((this.bodyPadding === true) ? 5 : this.bodyPadding));
- }
- if (this.bodyMargin != undefined) {
- bodyStyle.push('margin: ' + Element.unitizeBox((this.bodyMargin === true) ? 5 : this.bodyMargin));
- }
- if (this.bodyBorder != undefined) {
- bodyStyle.push('border-width: ' + Element.unitizeBox((this.bodyBorder === true) ? 1 : this.bodyBorder));
- }
- delete this.bodyStyle;
- return bodyStyle.length ? bodyStyle.join(';') : undefined;
- },
-
- /**
- * Initialized the renderData to be used when rendering the renderTpl.
- * @return {Object} Object with keys and values that are going to be applied to the renderTpl
- * @private
- */
- initRenderData: function() {
- return Ext.applyIf(Ext.lib.Panel.superclass.initRenderData.call(this), {
- bodyStyle: this.initBodyStyles(),
- bodyCls: this.bodyCls
- });
- },
-
- /**
- * Adds docked item(s) to the panel.
- * @param {Object/Array} component. The Component or array of components to add. The components
- * must include a 'dock' paramater on each component to indicate where it should be docked ('top', 'right',
- * 'bottom', 'left').
- * @param {Number} pos (optional) The index at which the Component will be added
- */
- addDocked : function(items, pos) {
- items = this.prepareItems(items);
-
- var item, i, ln;
- for (i = 0, ln = items.length; i < ln; i++) {
- item = items[i];
- item.dock = item.dock || 'top';
- if (pos !== undefined) {
- this.dockedItems.insert(pos+i, item);
- }
- else {
- this.dockedItems.add(item);
- }
- item.onAdded(this, i);
- this.onDockedAdd(item);
- }
- if (this.rendered) {
- this.doComponentLayout();
- }
- },
-
- // Placeholder empty functions
- onDockedAdd : Ext.emptyFn,
- onDockedRemove : Ext.emptyFn,
-
- /**
- * Inserts docked item(s) to the panel at the indicated position.
- * @param {Number} pos The index at which the Component will be inserted
- * @param {Object/Array} component. The Component or array of components to add. The components
- * must include a 'dock' paramater on each component to indicate where it should be docked ('top', 'right',
- * 'bottom', 'left').
- */
- insertDocked : function(pos, items) {
- this.addDocked(items, pos);
- },
-
- /**
- * Removes the docked item from the panel.
- * @param {Ext.Component} item. The Component to remove.
- * @param {Boolean} autoDestroy (optional) Destroy the component after removal.
- */
- removeDocked : function(item, autoDestroy) {
- if (!this.dockedItems.contains(item)) {
- return item;
- }
-
- var layout = this.componentLayout,
- hasLayout = layout && this.rendered;
-
- if (hasLayout) {
- layout.onRemove(item);
- }
-
- this.dockedItems.remove(item);
- item.onRemoved();
- this.onDockedRemove(item);
-
- if (autoDestroy === true || (autoDestroy !== false && this.autoDestroy)) {
- item.destroy();
- }
-
- if (hasLayout && !autoDestroy) {
- layout.afterRemove(item);
- }
- this.doComponentLayout();
-
- return item;
- },
-
- /**
- * Retrieve an array of all currently docked components.
- * @return {Array} An array of components.
- */
- getDockedItems : function() {
- if (this.dockedItems && this.dockedItems.items.length) {
- return this.dockedItems.items.slice();
- }
- return [];
- },
-
- // @private
- getTargetEl : function() {
- return this.body;
- },
-
-
- getRefItems: function(deep) {
- var refItems = Ext.lib.Panel.superclass.getRefItems.call(this, deep),
- // deep does not account for dockedItems within dockedItems.
- dockedItems = this.getDockedItems(),
- ln = dockedItems.length,
- i = 0,
- item;
-
- refItems = refItems.concat(dockedItems);
-
- if (deep) {
- for (; i < ln; i++) {
- item = dockedItems[i];
- if (item.getRefItems) {
- refItems = refItems.concat(item.getRefItems(true));
- }
- }
- }
-
- return refItems;
- },
-
- beforeDestroy: function(){
- var docked = this.dockedItems,
- c;
-
- if (docked) {
- while ((c = docked.first())) {
- this.removeDocked(c, true);
- }
- }
- Ext.lib.Panel.superclass.beforeDestroy.call(this);
- }
-});
-
-// Declare here so we can test
-Ext.Panel = Ext.extend(Ext.lib.Panel, {});
-Ext.reg('panel', Ext.Panel);
-
-/**
- * @class Ext.Panel
- * @extends Ext.lib.Panel
- * <p>Panel is a container that has specific functionality and structural components that make
- * it the perfect building block for application-oriented user interfaces.</p>
- * <p>Panels are, by virtue of their inheritance from {@link Ext.Container}, capable
- * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.</p>
- * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
- * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
- * those child elements need to be sized using one of Ext's built-in <code><b>{@link Ext.Container#layout layout}</b></code> schemes. By
- * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
- * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
- * at all.</p>
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #fullscreen}</li>
- * <li>{@link #layout}</li>
- * <li>{@link #items}</li>
- * <li>{@link #dockedItems}</li>
- * <li>{@link #html}</li>
- * </ul>
- *
- * <h2>Useful Methods</h2>
- * <ul class="list">
- * <li>{@link #show}</li>
- * <li>{@link #hide}</li>
- * <li>{@link #showBy}</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.Panel/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <pre><code>
-var panel = new Ext.Panel({
- fullscreen: true,
-
- dockedItems: [
- {
- dock : 'top',
- xtype: 'toolbar',
- title: 'Standard Titlebar'
- },
- {
- dock : 'top',
- xtype: 'toolbar',
- ui : 'light',
- items: [
- {
- text: 'Test Button'
- }
- ]
- }
- ],
-
- html: 'Testing'
-});</code></pre>
- *
- * @constructor
- * Create a new Panel
- * @param {Object} config The config object
- * @xtype panel
- */
-Ext.Panel = Ext.extend(Ext.lib.Panel, {
- // inherited
- scroll: false
-});
-
-Ext.reg('panel', Ext.Panel);
-/**
- * @class Ext.Button
- * @extends Ext.Component
- *
- * <p>A simple class to display different styles of buttons.</p>
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #ui} (defines the style of the button)</li>
- * </ul>
- *
- * <h2>Useful Methods</h2>
- * <ul class="list">
- * <li>{@link #handler} (method to be called when the button is tapped)</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.Button/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
-<pre><code>
-// an array of buttons (using xtypes) to be included in the panel below
-var buttons = [
- {
- text: 'Normal'
- },
- {
- ui : 'round',
- text: 'Round'
- },
- {
- ui : 'small',
- text: 'Small'
- }
-];
-
-var panel = new Ext.Panel({
- layout: {
- type : 'vbox',
- pack : 'center',
- align: 'stretch'
- },
- defaults: {
- layout: {
- type: 'hbox'
- },
- flex: 1,
- defaults: {
- xtype: 'button',
- cls : 'demobtn',
- flex : 1
- }
- },
- items: [
- {
- items: buttons // buttons array defined above
- },
- {
- items: [
- new Ext.Button({
- ui : 'decline',
- text: 'Drastic'
- }),
- {
- ui : 'decline-round',
- text: 'Round'
- },
- {
- ui : 'decline-small',
- text: 'Small'
- }
- ]
- },
- {
- items: [
- {
- ui : 'confirm',
- text: 'Confirm'
- },
- {
- ui : 'confirm-round',
- text: 'Round'
- },
- {
- ui : 'confirm-small',
- text: 'Small'
- }
- ]
- }
- ]
-});
-</code></pre>
- */
-
-/**
- * @constructor
- * Create a new button
- * @param {Object} config The config object
- * @xtype button
- */
-Ext.Button = Ext.extend(Ext.Component, {
- /**
- * @cfg {String/Object} autoEvent If provided, a handler function is automatically created that fires
- * the given event in the configured {@link #scope}.
- */
-
- initComponent: function(){
- this.addEvents(
- /**
- * @event tap
- * Fires when the button is tapped.
- * @param {Ext.Button} this
- * @param {Ext.EventObject} e
- */
- 'tap',
-
- /**
- * @event beforetap
- * Fires when the button is tapped but before we call the handler or fire the tap event.
- * Return false in a handler to prevent this.
- * @param {Ext.Button} this
- * @param {Ext.EventObject} e
- */
- 'beforetap'
- );
- Ext.Button.superclass.initComponent.call(this);
-
- this.createAutoHandler();
- },
-
- /**
- * @cfg {String} iconCls
- * A css class which sets a background image to be used as the icon for this button
- */
-
- /**
- * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
- */
-
- /**
- * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
- * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
- */
-
- /**
- * @cfg {String} iconAlign The alignment of the buttons icon if one has been defined. Valid options
- * are 'top', 'right', 'bottom', 'left' (defaults to 'left').
- */
- iconAlign: 'left',
-
- /**
- * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
- * The handler is passed the following parameters:<div class="mdetail-params"><ul>
- * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
- * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
- * </ul></div>
- */
-
- /**
- * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
- * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
- * executed. Defaults to this Button.
- */
-
- /**
- * @cfg {Boolean} hidden True to start hidden (defaults to false)
- */
-
- /**
- * @cfg {Boolean} disabled True to start disabled (defaults to false)
- */
-
- /**
- * @cfg {String} baseCls Base CSS class
- * Defaults to <tt>'x-button'</tt>
- */
- baseCls: 'x-button',
-
- /**
- * @cfg {String} pressedCls CSS class when the button is in pressed state
- * Defaults to <tt>'x-button-pressed'</tt>
- */
- pressedCls: 'x-button-pressed',
-
- /**
- * @cfg {String} badgeText The text to be used for a small badge on the button.
- * Defaults to <tt>''</tt>
- */
- badgeText: '',
-
- /**
- * @cfg {String} badgeCls CSS class for badge
- * Defaults to <tt>'x-badge'</tt>
- */
- badgeCls: 'x-badge',
-
- hasBadgeCls: 'x-hasbadge',
-
- labelCls: 'x-button-label',
-
- /**
- * @cfg {String} ui
- * Determines the UI look and feel of the button. Valid options are 'normal', 'back', 'round', 'action', 'forward'.
- * Defaults to 'normal'.
- */
- ui: 'normal',
-
- isButton: true,
-
- /**
- * @cfg {String} cls
- * A CSS class string to apply to the button's main element.
- */
-
- /**
- * @cfg {Number} pressedDelay
- * The amount of delay between the tapstart and the moment we add the pressedCls.
- * Settings this to true defaults to 100ms
- */
- pressedDelay: 0,
-
- /**
- * @cfg {String} iconMaskCls
- * CSS class to be added to the iconEl when the iconMask config is set to true.
- * Defaults to 'x-icon-mask'
- */
- iconMaskCls: 'x-icon-mask',
-
- /**
- * @cfg {Boolean} iconMask
- * Whether or not to mask the icon with the iconMaskCls configuration. Defaults to false.
- */
- iconMask: false,
-
- // @private
- afterRender : function(ct, position) {
- var me = this;
-
- Ext.Button.superclass.afterRender.call(me, ct, position);
-
- var text = me.text,
- icon = me.icon,
- iconCls = me.iconCls,
- badgeText = me.badgeText;
-
- me.text = me.icon = me.iconCls = me.badgeText = null;
-
- me.setText(text);
- me.setIcon(icon);
- me.setIconClass(iconCls);
-
- if (me.iconMask && me.iconEl) {
- me.iconEl.addCls(me.iconMaskCls);
- }
- me.setBadge(badgeText);
- },
-
- // @private
- initEvents : function() {
- var me = this;
-
- Ext.Button.superclass.initEvents.call(me);
-
- me.mon(me.el, {
- scope: me,
-
- tap : me.onPress,
- tapstart : me.onTapStart,
- tapcancel: me.onTapCancel
- });
- },
-
- // @private
- onTapStart : function() {
- var me = this;
- if (!me.disabled) {
- if (me.pressedDelay) {
- me.pressedTimeout = setTimeout(function() {
- me.el.addCls(me.pressedCls);
- }, Ext.isNumber(me.pressedDelay) ? me.pressedDelay : 100);
- }
- else {
- me.el.addCls(me.pressedCls);
- }
- }
- },
-
- // @private
- onTapCancel : function() {
- var me = this;
- if (me.pressedTimeout) {
- clearTimeout(me.pressedTimeout);
- delete me.pressedTimeout;
- }
- me.el.removeCls(me.pressedCls);
- },
-
- /**
- * Assigns this Button's click handler
- * @param {Function} handler The function to call when the button is clicked
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
- * Defaults to this Button.
- * @return {Ext.Button} this
- */
- setHandler : function(handler, scope) {
- this.handler = handler;
- this.scope = scope;
- return this;
- },
-
- /**
- * Sets this Button's text
- * @param {String} text The button text. If you pass null or undefined the text will be removed.
- * @return {Ext.Button} this
- */
- setText: function(text) {
- var me = this;
-
- if (me.rendered) {
- if (!me.textEl && text) {
- me.textEl = me.el.createChild({
- tag: 'span',
- html: text,
- cls: this.labelCls
- });
- }
- else if (me.textEl && text != me.text) {
- if (text) {
- me.textEl.setHTML(text);
- }
- else {
- me.textEl.remove();
- me.textEl = null;
- }
- }
- }
- me.text = text;
- return me;
- },
-
- /**
- * Sets the background image (inline style) of the button. This method also changes
- * the value of the {@link icon} config internally.
- * @param {String} icon The path to an image to display in the button. If you pass null or undefined the icon will be removed.
- * @return {Ext.Button} this
- */
- setIcon: function(icon) {
- var me = this;
-
- if (me.rendered) {
- if (!me.iconEl && icon) {
- me.iconEl = me.el.createChild({
- tag: 'img',
- src: Ext.BLANK_IMAGE_URL,
- style: 'background-image: ' + (icon ? 'url(' + icon + ')' : '')
- });
-
- me.setIconAlign(me.iconAlign);
- }
- else if (me.iconEl && icon != me.icon) {
- if (icon) {
- me.iconEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
- me.setIconAlign(me.iconAlign);
- }
- else {
- me.setIconAlign(false);
- me.iconEl.remove();
- me.iconEl = null;
- }
- }
- }
- me.icon = icon;
- return me;
- },
-
- /**
- * Sets the CSS class that provides a background image to use as the button's icon. This method also changes
- * the value of the {@link iconCls} config internally.
- * @param {String} cls The CSS class providing the icon image. If you pass null or undefined the iconCls will be removed.
- * @return {Ext.Button} this
- */
- setIconClass: function(cls) {
- var me = this;
-
- if (me.rendered) {
- if (!me.iconEl && cls) {
- me.iconEl = me.el.createChild({
- tag: 'img',
- src: Ext.BLANK_IMAGE_URL,
- cls: cls
- });
-
- me.setIconAlign(me.iconAlign);
- }
- else if (me.iconEl && cls != me.iconCls) {
- if (cls) {
- if (me.iconCls) {
- me.iconEl.removeCls(me.iconCls);
- }
- me.iconEl.addCls(cls);
- me.setIconAlign(me.iconAlign);
- }
- else {
- me.setIconAlign(false);
- me.iconEl.remove();
- me.iconEl = null;
- }
- }
- }
- me.iconCls = cls;
- return me;
- },
-
- /**
- * Adds a CSS class to the button that changes the align of the button's icon (if one has been defined). If no icon or iconClass has
- * been defined, it will only set the value of the {@link iconAlign} internal config.
- * @param {String} alignment The alignment you would like to align the button. Valid options are 'top', 'bottom', 'left', 'right'.
- * If you pass false, it will remove the current iconAlign. If you pass nothing or an invalid alignment,
- * it will default to the last used/default iconAlign.
- * @return {Ext.Button} this
- */
- setIconAlign: function(alignment) {
- var me = this,
- alignments = ['top', 'right', 'bottom', 'left'],
- alignment = ((alignments.indexOf(alignment) == -1 || !alignment) && alignment !== false) ? me.iconAlign : alignment,
- i;
-
- if (me.rendered && me.iconEl) {
- me.el.removeCls('x-iconalign-' + me.iconAlign);
-
- if (alignment) me.el.addCls('x-iconalign-' + alignment);
- }
- me.iconAlign = (alignment === false) ? me.iconAlign : alignment;
- return me;
- },
-
- /**
- * Creates a badge overlay on the button for displaying notifications.
- * @param {String} text The text going into the badge. If you pass null or undefined the badge will be removed.
- * @return {Ext.Button} this
- */
- setBadge : function(text) {
- var me = this;
-
- if (me.rendered) {
- if (!me.badgeEl && text) {
- me.badgeEl = me.el.createChild({
- tag: 'span',
- cls: me.badgeCls,
- html: text
- });
- me.el.addCls(me.hasBadgeCls);
- }
- else if (me.badgeEl && text != me.badgeText) {
- if (text) {
- me.badgeEl.setHTML(text);
- me.el.addCls(me.hasBadgeCls);
- }
- else {
- me.badgeEl.remove();
- me.badgeEl = null;
- me.el.removeCls(me.hasBadgeCls);
- }
- }
- }
- me.badgeText = text;
- return me;
- },
-
- /**
- * Gets the text for this Button
- * @return {String} The button text
- */
- getText : function() {
- return this.text;
- },
-
- /**
- * Gets the text for this Button's badge
- * @return {String} The button text
- */
- getBadgeText : function() {
- return this.badgeText;
- },
-
- // @private
- onDisable : function() {
- this.onDisableChange(true);
- },
-
- // @private
- onEnable : function() {
- this.onDisableChange(false);
- },
-
- // @private
- onDisableChange : function(disabled) {
- var me = this;
- if (me.el) {
- me.el[disabled ? 'addCls' : 'removeCls'](me.disabledCls);
- me.el.dom.disabled = disabled;
- }
- me.disabled = disabled;
- },
-
- // @private
- onPress : function(e) {
- var me = this;
- if (!me.disabled && this.fireEvent('beforetap') !== false) {
- setTimeout(function() {
- if (!me.preventCancel) {
- me.onTapCancel();
- }
- me.callHandler(e);
- me.fireEvent('tap', me, e);
- }, 10);
- }
- },
-
- // @private
- callHandler: function(e) {
- var me = this;
- if (me.handler) {
- me.handler.call(me.scope || me, me, e);
- }
- },
-
- /**
- * @private
- * If {@link #autoEvent} is set, this creates a handler function that automatically fires that configured
- * event. This is called by initComponent and should never need to be called again.
- */
- createAutoHandler: function() {
- var me = this,
- autoEvent = me.autoEvent;
-
- if (autoEvent) {
- if (typeof autoEvent == 'string') {
- autoEvent = {
- name: autoEvent,
- scope: me.scope || me
- };
- }
-
- me.addEvents(autoEvent.name);
-
- me.setHandler(function() {
- autoEvent.scope.fireEvent(autoEvent.name, autoEvent.scope, me);
- }, autoEvent.scope);
- }
- }
-});
-
-Ext.reg('button', Ext.Button);
-
-/**
- * @class Ext.SegmentedButton
- * @extends Ext.Container
- * <p>SegmentedButton is a container for a group of {@link Ext.Button}s. Generally a SegmentedButton would be
- * a child of a {@link Ext.Toolbar} and would be used to switch between different views.</p>
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #allowMultiple}</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.SegmentedButton/screenshot.png" /></p>
- *
- * <h2>Example usage:</h2>
- * <pre><code>
-var segmentedButton = new Ext.SegmentedButton({
- allowMultiple: true,
- items: [
- {
- text: 'Option 1'
- },
- {
- text : 'Option 2',
- pressed: true,
- handler: tappedFn
- },
- {
- text: 'Option 3'
- }
- ],
- listeners: {
- toggle: function(container, button, pressed){
- console.log("User toggled the '" + button.text + "' button: " + (pressed ? 'on' : 'off'));
- }
- }
-});</code></pre>
- * @constructor
- * @param {Object} config The config object
- * @xtype buttons
- */
-Ext.SegmentedButton = Ext.extend(Ext.Container, {
- defaultType: 'button',
- componentCls: 'x-segmentedbutton',
- pressedCls: 'x-button-pressed',
-
- /**
- * @cfg {Boolean} allowMultiple
- * Allow multiple pressed buttons (defaults to false).
- */
- allowMultiple: false,
-
- /**
- * @cfg {Boolean} allowDepress
- * Allow to depress a pressed button. (defaults to true when allowMultiple is true)
- */
-
- // @private
- initComponent : function() {
- this.layout = Ext.apply({}, this.layout || {}, {
- type: 'hbox',
- align: 'stretch'
- });
-
- Ext.SegmentedButton.superclass.initComponent.call(this);
-
- if (this.allowDepress === undefined) {
- this.allowDepress = this.allowMultiple;
- }
-
- this.addEvents(
- /**
- * @event toggle
- * Fires when any child button's pressed state has changed.
- * @param {Ext.SegmentedButton} this
- * @param {Ext.Button} button The button whose state has changed
- * @param {Boolean} pressed The new button state.
- */
- 'toggle'
- );
- },
-
- // @private
- initEvents : function() {
- Ext.SegmentedButton.superclass.initEvents.call(this);
-
- this.mon(this.el, {
- tap: this.onTap,
- capture: true,
- scope: this
- });
- },
-
- // @private
- afterLayout : function(layout) {
- var me = this;
-
- Ext.SegmentedButton.superclass.afterLayout.call(me, layout);
-
- if (!me.initialized) {
- me.items.each(function(item, index) {
- me.setPressed(item, !!item.pressed, true);
- });
- if (me.allowMultiple) {
- me.pressedButtons = me.getPressedButtons();
- }
- me.initialized = true;
- }
- },
-
- // @private
- onTap : function(e, t) {
- if (!this.disabled && (t = e.getTarget('.x-button'))) {
- this.setPressed(t.id, this.allowDepress ? undefined : true);
- }
- },
-
- /**
- * Gets the pressed button(s)
- * @returns {Array/Button} The pressed button or an array of pressed buttons (if allowMultiple is true)
- */
- getPressed : function() {
- return this.allowMultiple ? this.getPressedButtons() : this.pressedButton;
- },
-
- /**
- * Activates a button
- * @param {Number/String/Button} position/id/button. The button to activate.
- * @param {Boolean} pressed if defined, sets the pressed state of the button,
- * otherwise the pressed state is toggled
- * @param {Boolean} suppressEvents true to suppress toggle events during the action.
- * If allowMultiple is true, then setPressed will toggle the button state.
- */
- setPressed : function(btn, pressed, suppressEvents) {
- var me = this;
-
- btn = me.getComponent(btn);
- if (!btn || !btn.isButton || btn.disabled) {
- if (!me.allowMultiple && me.pressedButton) {
- me.setPressed(me.pressedButton, false);
- }
- return;
- }
-
- if (!Ext.isBoolean(pressed)) {
- pressed = !btn.pressed;
- }
-
- if (pressed) {
- if (!me.allowMultiple) {
- if (me.pressedButton && me.pressedButton !== btn) {
- me.pressedButton.el.removeCls(me.pressedCls);
- me.pressedButton.pressed = false;
- if (suppressEvents !== true) {
- me.fireEvent('toggle', me, me.pressedButton, false);
- }
- }
- me.pressedButton = btn;
- }
-
- btn.el.addCls(me.pressedCls);
- btn.pressed = true;
- btn.preventCancel = true;
- if (me.initialized && suppressEvents !== true) {
- me.fireEvent('toggle', me, btn, true);
- }
- }
- else if (!pressed) {
- if (!me.allowMultiple && btn === me.pressedButton) {
- me.pressedButton = null;
- }
-
- if (btn.pressed) {
- btn.el.removeCls(me.pressedCls);
- btn.pressed = false;
- if (suppressEvents !== true) {
- me.fireEvent('toggle', me, btn, false);
- }
- }
- }
-
- if (me.allowMultiple && me.initialized) {
- me.pressedButtons = me.getPressedButtons();
- }
- },
-
- // @private
- getPressedButtons : function(toggleEvents) {
- var pressed = this.items.filterBy(function(item) {
- return item.isButton && !item.disabled && item.pressed;
- });
- return pressed.items;
- },
-
- /**
- * Disables all buttons
- */
- disable : function() {
- this.items.each(function(item) {
- item.disable();
- });
-
- Ext.SegmentedButton.superclass.disable.apply(this, arguments);
- },
-
- /**
- * Enables all buttons
- */
- enable : function() {
- this.items.each(function(item) {
- item.enable();
- }, this);
-
- Ext.SegmentedButton.superclass.enable.apply(this, arguments);
- }
-});
-
-Ext.reg('segmentedbutton', Ext.SegmentedButton);
-/**
- * @class Ext.AbstractStoreSelectionModel
- * @extends Ext.util.Observable
- *
- * Tracks what records are currently selected in a databound widget.
- *
- * This is an abstract class and is not meant to be directly used.
- *
- * DataBound UI widgets such as GridPanel, TreePanel, and ListView
- * should subclass AbstractStoreSelectionModel and provide a way
- * to binding to the component.
- *
- * The abstract methods onSelectChange and onLastFocusChanged should
- * be implemented in these subclasses to update the UI widget.
- */
-Ext.AbstractStoreSelectionModel = Ext.extend(Ext.util.Observable, {
- // lastSelected
-
- /**
- * @cfg {String} mode
- * Modes of selection.
- * Valid values are SINGLE, SIMPLE, and MULTI. Defaults to 'SINGLE'
- */
-
- /**
- * @cfg {Boolean} allowDeselect
- * Allow users to deselect a record in a DataView, List or Grid. Only applicable when the SelectionModel's mode is 'SINGLE'. Defaults to false.
- */
- allowDeselect: false,
-
- /**
- * @property selected
- * READ-ONLY A MixedCollection that maintains all of the currently selected
- * records.
- */
- selected: null,
-
- constructor: function(cfg) {
- cfg = cfg || {};
- Ext.apply(this, cfg);
-
- this.modes = {
- SINGLE: true,
- SIMPLE: true,
- MULTI: true
- };
-
- // sets this.selectionMode
- this.setSelectionMode(cfg.mode);
-
- // maintains the currently selected records.
- this.selected = new Ext.util.MixedCollection();
-
- Ext.AbstractStoreSelectionModel.superclass.constructor.call(this, cfg);
- },
-
- // binds the store to the selModel.
- bind : function(store, initial){
- if(!initial && this.store){
- if(store !== this.store && this.store.autoDestroy){
- this.store.destroy();
- }else{
- this.store.un("add", this.onStoreAdd, this);
- this.store.un("clear", this.onStoreClear, this);
- this.store.un("remove", this.onStoreRemove, this);
- this.store.un("update", this.onStoreUpdate, this);
- }
- }
- if(store){
- store = Ext.StoreMgr.lookup(store);
- store.on({
- add: this.onStoreAdd,
- clear: this.onStoreClear,
- remove: this.onStoreRemove,
- update: this.onStoreUpdate,
- scope: this
- });
- }
- this.store = store;
- if(store && !initial) {
- this.refresh();
- }
- },
-
- selectAll: function(silent) {
- var selections = this.store.getRange();
- for (var i = 0, ln = selections.length; i < ln; i++) {
- this.doSelect(selections[i], true, silent);
- }
- },
-
- deselectAll: function() {
- var selections = this.getSelection();
- for (var i = 0, ln = selections.length; i < ln; i++) {
- this.doDeselect(selections[i]);
- }
- },
-
- // Provides differentiation of logic between MULTI, SIMPLE and SINGLE
- // selection modes. Requires that an event be passed so that we can know
- // if user held ctrl or shift.
- selectWithEvent: function(record, e) {
- switch (this.selectionMode) {
- case 'MULTI':
- if (e.ctrlKey && this.isSelected(record)) {
- this.doDeselect(record, false);
- } else if (e.shiftKey && this.lastFocused) {
- this.selectRange(this.lastFocused, record, e.ctrlKey);
- } else if (e.ctrlKey) {
- this.doSelect(record, true, false);
- } else if (this.isSelected(record) && !e.shiftKey && !e.ctrlKey && this.selected.getCount() > 1) {
- this.doSelect(record, false, false);
- } else {
- this.doSelect(record, false);
- }
- break;
- case 'SIMPLE':
- if (this.isSelected(record)) {
- this.doDeselect(record);
- } else {
- this.doSelect(record, true);
- }
- break;
- case 'SINGLE':
- // if allowDeselect is on and this record isSelected, deselect it
- if (this.allowDeselect && this.isSelected(record)) {
- this.doDeselect(record);
- // select the record and do NOT maintain existing selections
- } else {
- this.doSelect(record, false);
- }
- break;
- }
- },
-
- /**
- * Selects a range of rows if the selection model
- * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
- * All rows in between startRow and endRow are also selected.
- * @param {Number} startRow The index of the first row in the range
- * @param {Number} endRow The index of the last row in the range
- * @param {Boolean} keepExisting (optional) True to retain existing selections
- */
- selectRange : function(startRecord, endRecord, keepExisting, dir){
- var i,
- startRow = this.store.indexOf(startRecord),
- endRow = this.store.indexOf(endRecord),
- tmp,
- selectedCount = 0,
- dontDeselect;
-
- if (this.isLocked()){
- return;
- }
-
- // swap values
- if (startRow > endRow){
- tmp = endRow;
- endRow = startRow;
- startRow = tmp;
- }
-
- for (i = startRow; i <= endRow; i++) {
- if (this.isSelected(this.store.getAt(i))) {
- selectedCount++;
- }
- }
-
- if (!dir) {
- dontDeselect = -1;
- } else {
- dontDeselect = (dir == 'up') ? startRow : endRow;
- }
- for (i = startRow; i <= endRow; i++){
- if (selectedCount == (endRow - startRow + 1)) {
- if (i != dontDeselect) {
- this.doDeselect(i, true);
- }
- } else {
- this.doSelect(i, true);
- }
-
- }
- },
-
- /**
- * Selects a record instance by record instance or index.
- * @param {Ext.data.Record/Index} records An array of records or an index
- * @param {Boolean} keepExisting
- * @param {Boolean} suppressEvent Set to false to not fire a select event
- */
- select: function(records, keepExisting, suppressEvent) {
- this.doSelect(records, keepExisting, suppressEvent);
- },
-
- /**
- * Deselects a record instance by record instance or index.
- * @param {Ext.data.Record/Index} records An array of records or an index
- * @param {Boolean} suppressEvent Set to false to not fire a deselect event
- */
- deselect: function(records, suppressEvent) {
- this.doDeselect(records, suppressEvent);
- },
-
- doSelect: function(records, keepExisting, suppressEvent) {
- if (this.locked) {
- return;
- }
- if (typeof records === "number") {
- records = [this.store.getAt(records)];
- }
- if (this.selectionMode == "SINGLE" && records) {
- var record = records.length ? records[0] : records;
- this.doSingleSelect(record, suppressEvent);
- } else {
- this.doMultiSelect(records, keepExisting, suppressEvent);
- }
- },
-
- doMultiSelect: function(records, keepExisting, suppressEvent) {
- if (this.locked) {
- return;
- }
- var selected = this.selected,
- change = false,
- record;
-
- records = !Ext.isArray(records) ? [records] : records;
- if (!keepExisting && selected.getCount() > 0) {
- change = true;
- this.doDeselect(this.getSelection(), true);
- }
-
- for (var i = 0, ln = records.length; i < ln; i++) {
- record = records[i];
- if (keepExisting && this.isSelected(record)) {
- continue;
- }
- change = true;
- this.lastSelected = record;
- selected.add(record);
- if (!suppressEvent) {
- this.setLastFocused(record);
- }
-
- this.onSelectChange(record, true, suppressEvent);
- }
- // fire selchange if there was a change and there is no suppressEvent flag
- this.maybeFireSelectionChange(change && !suppressEvent);
- },
-
- // records can be an index, a record or an array of records
- doDeselect: function(records, suppressEvent) {
- if (this.locked) {
- return;
- }
-
- if (typeof records === "number") {
- records = [this.store.getAt(records)];
- }
-
- var change = false,
- selected = this.selected,
- record;
-
- records = !Ext.isArray(records) ? [records] : records;
- for (var i = 0, ln = records.length; i < ln; i++) {
- record = records[i];
- if (selected.remove(record)) {
- if (this.lastSelected == record) {
- this.lastSelected = selected.last();
- }
- this.onSelectChange(record, false, suppressEvent);
- change = true;
- }
- }
- // fire selchange if there was a change and there is no suppressEvent flag
- this.maybeFireSelectionChange(change && !suppressEvent);
- },
-
- doSingleSelect: function(record, suppressEvent) {
- if (this.locked) {
- return;
- }
- // already selected.
- // should we also check beforeselect?
- if (this.isSelected(record)) {
- return;
- }
- var selected = this.selected;
- if (selected.getCount() > 0) {
- this.doDeselect(this.lastSelected, suppressEvent);
- }
- selected.add(record);
- this.lastSelected = record;
- this.onSelectChange(record, true, suppressEvent);
- this.setLastFocused(record);
- this.maybeFireSelectionChange(!suppressEvent);
- },
-
- /**
- * @param {Ext.data.Record} record
- * Set a record as the last focused record. This does NOT mean
- * that the record has been selected.
- */
- setLastFocused: function(record) {
- var recordBeforeLast = this.lastFocused;
- this.lastFocused = record;
- this.onLastFocusChanged(recordBeforeLast, record);
- },
-
-
- // fire selection change as long as true is not passed
- // into maybeFireSelectionChange
- maybeFireSelectionChange: function(fireEvent) {
- if (fireEvent) {
- this.fireEvent('selectionchange', this, this.getSelection());
- }
- },
-
- /**
- * Returns the last selected record.
- */
- getLastSelected: function() {
- return this.lastSelected;
- },
-
- getLastFocused: function() {
- return this.lastFocused;
- },
-
- /**
- * Returns an array of the currently selected records.
- */
- getSelection: function() {
- return this.selected.getRange();
- },
-
- /**
- * Returns the current selectionMode. SINGLE, MULTI or SIMPLE.
- */
- getSelectionMode: function() {
- return this.selectionMode;
- },
-
- /**
- * Sets the current selectionMode. SINGLE, MULTI or SIMPLE.
- */
- setSelectionMode: function(selMode) {
- selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
- // set to mode specified unless it doesnt exist, in that case
- // use single.
- this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
- },
-
- /**
- * Returns true if the selections are locked.
- * @return {Boolean}
- */
- isLocked: function() {
- return this.locked;
- },
-
- /**
- * Locks the current selection and disables any changes from
- * happening to the selection.
- * @param {Boolean} locked
- */
- setLocked: function(locked) {
- this.locked = !!locked;
- },
-
- /**
- * Returns <tt>true</tt> if the specified row is selected.
- * @param {Record/Number} record The record or index of the record to check
- * @return {Boolean}
- */
- isSelected: function(record) {
- record = Ext.isNumber(record) ? this.store.getAt(record) : record;
- return this.selected.indexOf(record) !== -1;
- },
-
- /**
- * Returns true if there is a selected record.
- * @return {Boolean}
- */
- hasSelection: function() {
- return this.selected.getCount() > 0;
- },
-
- refresh: function() {
- var toBeSelected = [],
- oldSelections = this.getSelection(),
- ln = oldSelections.length,
- selection,
- change,
- i = 0;
-
- // check to make sure that there are no records
- // missing after the refresh was triggered, prune
- // them from what is to be selected if so
- for (; i < ln; i++) {
- selection = oldSelections[i];
- if (this.store.indexOf(selection) != -1) {
- toBeSelected.push(selection);
- }
- }
-
- // there was a change from the old selected and
- // the new selection
- if (this.selected.getCount() != toBeSelected.length) {
- change = true;
- }
-
- this.clearSelections();
-
- if (toBeSelected.length) {
- // perform the selection again
- this.doSelect(toBeSelected, false, true);
- }
-
- this.maybeFireSelectionChange(change);
- },
-
- clearSelections: function() {
- // reset the entire selection to nothing
- this.selected.clear();
- this.lastSelected = null;
- this.setLastFocused(null);
- },
-
- // when a record is added to a store
- onStoreAdd: function() {
-
- },
-
- // when a store is cleared remove all selections
- // (if there were any)
- onStoreClear: function() {
- var selected = this.selected;
- if (selected.getCount > 0) {
- selected.clear();
- this.lastSelected = null;
- this.setLastFocused(null);
- this.maybeFireSelectionChange(true);
- }
- },
-
- // prune records from the SelectionModel if
- // they were selected at the time they were
- // removed.
- onStoreRemove: function(store, record) {
- if (this.locked) {
- return;
- }
- var selected = this.selected;
- if (selected.remove(record)) {
- if (this.lastSelected == record) {
- this.lastSelected = null;
- }
- if (this.getLastFocused() == record) {
- this.setLastFocused(null);
- }
- this.maybeFireSelectionChange(true);
- }
- },
-
- getCount: function() {
- return this.selected.getCount();
- },
-
- // cleanup.
- destroy: function() {
-
- },
-
- // if records are updated
- onStoreUpdate: function() {
-
- },
-
- // @abstract
- onSelectChange: function(record, isSelected, suppressEvent) {
-
- },
-
- // @abstract
- onLastFocusChanged: function(oldFocused, newFocused) {
-
- },
-
- // @abstract
- onEditorKey: function(field, e) {
-
- },
-
- // @abstract
- bindComponent: function(cmp) {
-
- }
-});
-Ext.DataViewSelectionModel = Ext.extend(Ext.AbstractStoreSelectionModel, {
- deselectOnContainerClick: true,
- bindComponent: function(view) {
- this.view = view;
- this.bind(view.getStore());
- var eventListeners = {
- refresh: this.refresh,
- scope: this,
- el: {
- scope: this
- }
- };
- eventListeners.el[view.triggerEvent] = this.onItemClick;
- eventListeners.el[view.triggerCtEvent] = this.onContainerClick;
-
- view.on(eventListeners);
- },
-
-
- onItemClick: function(e) {
- var view = this.view,
- node = view.findTargetByEvent(e);
-
- if (node) {
- this.selectWithEvent(view.getRecord(node), e);
- } else {
- return false;
- }
- },
-
- onContainerClick: function() {
- if (this.deselectOnContainerClick) {
- this.deselectAll();
- }
- },
-
- // Allow the DataView to update the ui
- onSelectChange: function(record, isSelected, suppressEvent) {
- var view = this.view;
-
- if (isSelected) {
- view.onItemSelect(record);
- if (!suppressEvent) {
- this.fireEvent('select', this, record);
- }
- } else {
- view.onItemDeselect(record);
- if (!suppressEvent) {
- this.fireEvent('deselect', this, record);
- }
- }
- }
-});
-
-/**
- * @class Ext.DataView
- * @extends Ext.Component
- * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
- * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
- * so that as the data in the store changes the view is automatically updated to reflect the changes. The view also
- * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
- * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
- * config must be provided for the DataView to determine what nodes it will be working with.</b>
- *
- * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
- * <pre><code>
-var store = new Ext.data.JsonStore({
- url: 'get-images.php',
- root: 'images',
- fields: [
- 'name', 'url',
- {name:'size', type: 'float'},
- {name:'lastmod', type:'date', dateFormat:'timestamp'}
- ]
-});
-store.load();
-
-var tpl = new Ext.XTemplate(
- '<tpl for=".">',
- '<div class="thumb-wrap" id="{name}">',
- '<div class="thumb"><img src="{url}" title="{name}"></div>',
- '<span class="x-editable">{shortName}</span></div>',
- '</tpl>',
- '<div class="x-clear"></div>'
-);
-
-var panel = new Ext.Panel({
- id:'images-view',
- frame:true,
- width:535,
- autoHeight:true,
- collapsible:true,
- layout:'fit',
- title:'Simple DataView',
-
- items: new Ext.DataView({
- store: store,
- tpl: tpl,
- autoHeight:true,
- multiSelect: true,
- overCls:'x-view-over',
- itemSelector:'div.thumb-wrap',
- emptyText: 'No images to display'
- })
-});
-panel.render(document.body);
-</code></pre>
- * @constructor
- * Create a new DataView
- * @param {Object} config The config object
- * @xtype dataview
- */
-// dataview will extend from DataPanel/BoundPanel
-Ext.DataView = Ext.extend(Ext.Component, {
- /**
- * @cfg {String/Array} tpl
- * @required
- * The HTML fragment or an array of fragments that will make up the template used by this DataView. This should
- * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
- */
- /**
- * @cfg {Ext.data.Store} store
- * @required
- * The {@link Ext.data.Store} to bind this DataView to.
- */
-
- /**
- * @cfg {String} itemSelector
- * @required
- * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or
- * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
- * working with.
- */
-
-
- /**
- * @cfg {String} overItemCls
- * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
- */
-
- /**
- * @cfg {String} loadingText
- * A string to display during data load operations (defaults to undefined). If specified, this text will be
- * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
- * contents will continue to display normally until the new data is loaded and the contents are replaced.
- */
- loadingText: 'Loading...',
-
- /**
- * @cfg {String} selectedItemCls
- * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
- */
- selectedItemCls: "x-item-selected",
-
- /**
- * @cfg {String} emptyText
- * The text to display in the view when there is no data to display (defaults to '').
- */
- emptyText: "",
-
- /**
- * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
- */
- deferEmptyText: true,
-
- /**
- * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
- */
- trackOver: false,
-
- /**
- * @cfg {Boolean} blockRefresh Set this to true to ignore datachanged events on the bound store. This is useful if
- * you wish to provide custom transition animations via a plugin (defaults to false)
- */
- blockRefresh: false,
-
- /**
- * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selection within the DataView. Defaults to <tt>false</tt>.
- * This configuration will lock the selection model that the DataView uses.</p>
- */
-
-
- //private
- last: false,
-
- triggerEvent: 'click',
- triggerCtEvent: 'containerclick',
-
- addCmpEvents: function() {
-
- },
-
- // private
- initComponent : function(){
- var isDef = Ext.isDefined;
- if (!isDef(this.tpl) || !isDef(this.store) || !isDef(this.itemSelector)) {
- throw "DataView requires tpl, store and itemSelector configurations to be defined.";
- }
-
- Ext.DataView.superclass.initComponent.call(this);
- if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
- this.tpl = new Ext.XTemplate(this.tpl);
- }
-
- // backwards compat alias for overClass/selectedClass
- // TODO: Consider support for overCls generation Ext.Component config
- if (Ext.isDefined(this.overCls) || Ext.isDefined(this.overClass)) {
- this.overItemCls = this.overCls || this.overClass;
- delete this.overCls;
- delete this.overClass;
- throw "Using the deprecated overCls or overClass configuration. Use overItemCls.";
- }
-
- if (Ext.isDefined(this.selectedCls) || Ext.isDefined(this.selectedClass)) {
- this.selectedItemCls = this.selectedCls || this.selectedClass;
- delete this.selectedCls;
- delete this.selectedClass;
- throw "Using the deprecated selectedCls or selectedClass configuration. Use selectedItemCls.";
- }
-
- this.addCmpEvents();
-
- this.store = Ext.StoreMgr.lookup(this.store)
- this.all = new Ext.CompositeElementLite();
- this.getSelectionModel().bindComponent(this);
- },
-
- onRender : function() {
- Ext.DataView.superclass.onRender.apply(this, arguments);
- if (this.loadingText) {
- this.loadMask = new Ext.LoadMask(this.el, {
- msg: this.loadingText
- });
- }
- },
-
- getSelectionModel: function(){
- if (!this.selModel) {
- this.selModel = {};
- }
-
- var mode;
- switch(true) {
- case this.simpleSelect:
- mode = 'SIMPLE';
- break;
-
- case this.multiSelect:
- mode = 'MULTI';
- break;
-
- case this.singleSelect:
- default:
- mode = 'SINGLE';
- break;
- }
-
- Ext.applyIf(this.selModel, {
- allowDeselect: this.allowDeselect,
- mode: mode
- });
-
- if (!this.selModel.events) {
- this.selModel = new Ext.DataViewSelectionModel(this.selModel);
- }
-
- if (!this.selModel.hasRelaySetup) {
- this.relayEvents(this.selModel, ['selectionchange', 'select', 'deselect']);
- this.selModel.hasRelaySetup = true;
- }
-
- // lock the selection model if user
- // has disabled selection
- if (this.disableSelection) {
- this.selModel.locked = true;
- }
-
- return this.selModel;
- },
-
- /**
- * Refreshes the view by reloading the data from the store and re-rendering the template.
- */
- refresh: function() {
- if (!this.rendered) {
- return;
- }
-
- var el = this.getTargetEl(),
- records = this.store.getRange();
-
- el.update('');
- if (records.length < 1) {
- if (!this.deferEmptyText || this.hasSkippedEmptyText) {
- el.update(this.emptyText);
- }
- this.all.clear();
- } else {
- this.tpl.overwrite(el, this.collectData(records, 0));
- this.all.fill(Ext.query(this.itemSelector, el.dom));
- this.updateIndexes(0);
- }
- this.hasSkippedEmptyText = true;
- this.fireEvent('refresh');
- },
-
- /**
- * Function which can be overridden to provide custom formatting for each Record that is used by this
- * DataView's {@link #tpl template} to render each node.
- * @param {Array/Object} data The raw data object that was used to create the Record.
- * @param {Number} recordIndex the index number of the Record being prepared for rendering.
- * @param {Record} record The Record being prepared for rendering.
- * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
- * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
- */
- prepareData: function(data, index, record) {
- if (record) {
- Ext.apply(data, this.prepareAssociatedData(record));
- }
- return data;
- },
-
- /**
- * @private
- * This complex-looking method takes a given Model instance and returns an object containing all data from
- * all of that Model's *loaded* associations. It does this recursively - for example if we have a User which
- * hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
- *
- * {
- * orders: [
- * {
- * id: 123,
- * status: 'shipped',
- * orderItems: [
- * ...
- * ]
- * }
- * ]
- * }
- *
- * This makes it easy to iterate over loaded associations in a DataView.
- *
- * @param {Ext.data.Model} record The Model instance
- * @param {Array} ids PRIVATE. The set of Model instance internalIds that have already been loaded
- * @return {Object} The nested data set for the Model's loaded associations
- */
- prepareAssociatedData: function(record, ids) {
- //we keep track of all of the internalIds of the models that we have loaded so far in here
- ids = ids || [];
-
- var associations = record.associations.items,
- associationCount = associations.length,
- associationData = {},
- associatedStore, associatedName, associatedRecords, associatedRecord,
- associatedRecordCount, association, internalId, i, j;
-
- for (i = 0; i < associationCount; i++) {
- association = associations[i];
-
- //this is the hasMany store filled with the associated data
- associatedStore = record[association.storeName];
-
- //we will use this to contain each associated record's data
- associationData[association.name] = [];
-
- //if it's loaded, put it into the association data
- if (associatedStore && associatedStore.data.length > 0) {
- associatedRecords = associatedStore.data.items;
- associatedRecordCount = associatedRecords.length;
-
- //now we're finally iterating over the records in the association. We do this recursively
- for (j = 0; j < associatedRecordCount; j++) {
- associatedRecord = associatedRecords[j];
- internalId = associatedRecord.internalId;
-
- //when we load the associations for a specific model instance we add it to the set of loaded ids so that
- //we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
- if (ids.indexOf(internalId) == -1) {
- ids.push(internalId);
-
- associationData[association.name][j] = associatedRecord.data;
- Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids));
- }
- }
- }
- }
-
- return associationData;
- },
-
- /**
- * <p>Function which can be overridden which returns the data object passed to this
- * DataView's {@link #tpl template} to render the whole DataView.</p>
- * <p>This is usually an Array of data objects, each element of which is processed by an
- * {@link Ext.XTemplate XTemplate} which uses <tt>'<tpl for=".">'</tt> to iterate over its supplied
- * data object as an Array. However, <i>named</i> properties may be placed into the data object to
- * provide non-repeating data such as headings, totals etc.</p>
- * @param {Array} records An Array of {@link Ext.data.Model}s to be rendered into the DataView.
- * @param {Number} startIndex the index number of the Record being prepared for rendering.
- * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
- * contain <i>named</i> properties.
- */
- collectData : function(records, startIndex){
- var r = [],
- i = 0,
- len = records.length;
-
- for(; i < len; i++){
- r[r.length] = this.prepareData(records[i].data, startIndex + i, records[i]);
- }
-
- return r;
- },
-
- // private
- bufferRender : function(records, index){
- var div = document.createElement('div');
- this.tpl.overwrite(div, this.collectData(records, index));
- return Ext.query(this.itemSelector, div);
- },
-
- // private
- onUpdate : function(ds, record){
- var index = this.store.indexOf(record),
- original,
- node;
-
- if (index > -1){
- original = this.all.elements[index];
- node = this.bufferRender([record], index)[0];
-
- this.all.replaceElement(index, node, true);
- this.updateIndexes(index, index);
-
- // Maintain selection after update
- // TODO: Move to approriate event handler.
- this.selModel.refresh();
- }
- },
-
- // private
- onAdd : function(ds, records, index){
- if (this.all.getCount() === 0) {
- this.refresh();
- return;
- }
-
- var nodes = this.bufferRender(records, index), n, a = this.all.elements;
- if (index < this.all.getCount()) {
- n = this.all.item(index).insertSibling(nodes, 'before', true);
- a.splice.apply(a, [index, 0].concat(nodes));
- } else {
- n = this.all.last().insertSibling(nodes, 'after', true);
- a.push.apply(a, nodes);
- }
- this.updateIndexes(index);
- },
-
- // private
- onRemove : function(ds, record, index){
- this.all.removeElement(index, true);
- this.updateIndexes(index);
- if (this.store.getCount() === 0){
- this.refresh();
- }
- },
-
- /**
- * Refreshes an individual node's data from the store.
- * @param {Number} index The item's data index in the store
- */
- refreshNode : function(index){
- this.onUpdate(this.store, this.store.getAt(index));
- },
-
- // private
- updateIndexes : function(startIndex, endIndex){
- var ns = this.all.elements;
- startIndex = startIndex || 0;
- endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
- for(var i = startIndex; i <= endIndex; i++){
- ns[i].viewIndex = i;
- }
- },
-
- /**
- * Returns the store associated with this DataView.
- * @return {Ext.data.Store} The store
- */
- getStore : function(){
- return this.store;
- },
-
- /**
- * Changes the data store bound to this view and refreshes it.
- * @param {Store} store The store to bind to this view
- */
- bindStore : function(store, initial) {
- if (!initial && this.store) {
- if (store !== this.store && this.store.autoDestroy) {
- this.store.destroy();
- }
- else {
- this.mun(this.store, {
- scope: this,
- beforeload: this.onBeforeLoad,
- datachanged: this.onDataChanged,
- add: this.onAdd,
- remove: this.onRemove,
- update: this.onUpdate,
- clear: this.refresh
- });
- }
- if (!store) {
- if (this.loadMask) {
- this.loadMask.bindStore(null);
- }
- this.store = null;
- }
- }
- if (store) {
- store = Ext.StoreMgr.lookup(store);
- this.mon(store, {
- scope: this,
- beforeload: this.onBeforeLoad,
- datachanged: this.onDataChanged,
- add: this.onAdd,
- remove: this.onRemove,
- update: this.onUpdate,
- clear: this.refresh
- });
- if (this.loadMask) {
- this.loadMask.bindStore(store);
- }
- }
-
- this.store = store;
- // Bind the store to our selection model
- this.getSelectionModel().bind(store);
-
- if (store) {
- this.refresh();
- }
- },
-
- /**
- * @private
- * Calls this.refresh if this.blockRefresh is not true
- */
- onDataChanged: function() {
- if (this.blockRefresh !== true) {
- this.refresh.apply(this, arguments);
- }
- },
-
- /**
- * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
- * @param {HTMLElement} node
- * @return {HTMLElement} The template node
- */
- findItemByChild: function(node){
- return Ext.fly(node).findParent(this.itemSelector, this.getTargetEl());
- },
-
- /**
- * Returns the template node by the Ext.EventObject or null if it is not found.
- * @param {Ext.EventObject} e
- */
- findTargetByEvent: function(e) {
- return e.getTarget(this.itemSelector, this.getTargetEl());
- },
-
-
- /**
- * Gets the currently selected nodes.
- * @return {Array} An array of HTMLElements
- */
- getSelectedNodes: function(){
- var nodes = [],
- records = this.selModel.getSelection(),
- ln = records.length,
- i = 0;
-
- for (; i < ln; i++) {
- nodes.push(this.getNode(records[i]));
- }
-
- return nodes;
- },
-
- /**
- * Gets an array of the records from an array of nodes
- * @param {Array} nodes The nodes to evaluate
- * @return {Array} records The {@link Ext.data.Model} objects
- */
- getRecords: function(nodes) {
- var records = [],
- i = 0,
- len = nodes.length;
-
- for (; i < len; i++) {
- records[records.length] = this.store.getAt(nodes[i].viewIndex);
- }
-
- return r;
- },
-
- /**
- * Gets a record from a node
- * @param {HTMLElement} node The node to evaluate
- * @return {Record} record The {@link Ext.data.Model} object
- */
- getRecord: function(node){
- return this.store.getAt(node.viewIndex);
- },
-
- /**
- * Returns true if the passed node is selected, else false.
- * @param {HTMLElement/Number/Ext.data.Model} node The node, node index or record to check
- * @return {Boolean} True if selected, else false
- */
- isSelected : function(node) {
- // TODO: El/Idx/Record
- var r = this.getRecord(node);
- return this.selModel.isSelected(r);
- },
-
- /**
- * Selects a record instance by record instance or index.
- * @param {Ext.data.Record/Index} records An array of records or an index
- * @param {Boolean} keepExisting
- * @param {Boolean} suppressEvent Set to false to not fire a select event
- */
- select: function(records, keepExisting, suppressEvent) {
- this.selModel.select(records, keepExisting, suppressEvent);
- },
-
- /**
- * Deselects a record instance by record instance or index.
- * @param {Ext.data.Record/Index} records An array of records or an index
- * @param {Boolean} suppressEvent Set to false to not fire a deselect event
- */
- deselect: function(records, suppressEvent) {
- this.selModel.deselect(records, suppressEvent);
- },
-
- /**
- * Gets a template node.
- * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo An HTMLElement template node, index of a template node,
- * the id of a template node or the record associated with the node.
- * @return {HTMLElement} The node or null if it wasn't found
- */
- getNode : function(nodeInfo) {
- if (Ext.isString(nodeInfo)) {
- return document.getElementById(nodeInfo);
- } else if (Ext.isNumber(nodeInfo)) {
- return this.all.elements[nodeInfo];
- } else if (nodeInfo instanceof Ext.data.Model) {
- var idx = this.store.indexOf(nodeInfo);
- return this.all.elements[idx];
- }
- return nodeInfo;
- },
-
- /**
- * Gets a range nodes.
- * @param {Number} start (optional) The index of the first node in the range
- * @param {Number} end (optional) The index of the last node in the range
- * @return {Array} An array of nodes
- */
- getNodes: function(start, end) {
- var ns = this.all.elements,
- nodes = [],
- i;
-
- start = start || 0;
- end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
- if (start <= end) {
- for (i = start; i <= end && ns[i]; i++) {
- nodes.push(ns[i]);
- }
- } else {
- for (i = start; i >= end && ns[i]; i--) {
- nodes.push(ns[i]);
- }
- }
- return nodes;
- },
-
- /**
- * Finds the index of the passed node.
- * @param {HTMLElement/String/Number/Record} nodeInfo An HTMLElement template node, index of a template node, the id of a template node
- * or a record associated with a node.
- * @return {Number} The index of the node or -1
- */
- indexOf: function(node) {
- node = this.getNode(node);
- if (Ext.isNumber(node.viewIndex)) {
- return node.viewIndex;
- }
- return this.all.indexOf(node);
- },
-
- // private
- onBeforeLoad: function() {
- if (this.loadingText) {
- this.getTargetEl().update('');
- this.all.clear();
- }
- },
-
- onDestroy : function() {
- this.all.clear();
- Ext.DataView.superclass.onDestroy.call(this);
- this.bindStore(null);
- this.selModel.destroy();
- },
-
- // invoked by the selection model to maintain visual UI cues
- onItemSelect: function(record) {
- var node = this.getNode(record);
- Ext.fly(node).addCls(this.selectedItemCls);
- },
-
- // invoked by the selection model to maintain visual UI cues
- onItemDeselect: function(record) {
- var node = this.getNode(record);
- Ext.fly(node).removeCls(this.selectedItemCls);
- },
-
- select: function(records, keepExisting, supressEvents) {
- console.warn("DataView: select will be removed, please access select through a DataView's SelectionModel, ie: view.getSelectionModel().select()");
- var sm = this.getSelectionModel();
- return sm.select.apply(sm, arguments);
- },
- clearSelections: function() {
- console.warn("DataView: clearSelections will be removed, please access deselectAll through DataView's SelectionModel, ie: view.getSelectionModel().deselectAll()");
- var sm = this.getSelectionModel();
- return sm.deselectAll();
- }
-});
-Ext.reg('dataview', Ext.DataView);
-
-
-// all of this information is available directly
-// from the SelectionModel itself, the only added methods
-// to DataView regarding selection will perform some transformation/lookup
-// between HTMLElement/Nodes to records and vice versa.
-Ext.DataView.override({
- /**
- * @cfg {Boolean} multiSelect
- * True to allow selection of more than one item at a time, false to allow selection of only a single item
- * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
- */
- /**
- * @cfg {Boolean} singleSelect
- * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
- * Note that if {@link #multiSelect} = true, this value will be ignored.
- */
- /**
- * @cfg {Boolean} simpleSelect
- * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
- * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
- */
-
- /**
- * Gets the number of selected nodes.
- * @return {Number} The node count
- */
- getSelectionCount : function(){
- return this.selModel.getSelection().length;
- },
-
- /**
- * Gets an array of the selected records
- * @return {Array} An array of {@link Ext.data.Model} objects
- */
- getSelectedRecords : function(){
- return this.selModel.getSelection();
- }
-});
-/**
- * @class Ext.DataView
- * @extends Ext.Component
- * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
- * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
- * so that as the data in the store changes the view is automatically updated to reflect the changes. The view also
- * provides built-in behavior for many common events that can occur for its contained items including itemtap, itemdoubletap, itemswipe, containertap,
- * etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
- * config must be provided for the DataView to determine what nodes it will be working with.</b>
- *
- * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
- * <pre><code>
-var store = new Ext.data.JsonStore({
- url: 'get-images.php',
- root: 'images',
- fields: [
- 'name', 'url',
- {name:'size', type: 'float'},
- {name:'lastmod', type:'date', dateFormat:'timestamp'}
- ]
-});
-store.load();
-
-var tpl = new Ext.XTemplate(
- '<tpl for=".">',
- '<div class="thumb-wrap" id="{name}">',
- '<div class="thumb"><img src="{url}" title="{name}"></div>',
- '<span class="x-editable">{shortName}</span></div>',
- '</tpl>',
- '<div class="x-clear"></div>'
-);
-
-var panel = new Ext.Panel({
- id:'images-view',
- frame:true,
- width:535,
- autoHeight:true,
- collapsible:true,
- layout:'fit',
- title:'Simple DataView',
-
- items: new Ext.DataView({
- store: store,
- tpl: tpl,
- autoHeight:true,
- multiSelect: true,
- overClass:'x-view-over',
- itemSelector:'div.thumb-wrap',
- emptyText: 'No images to display'
- })
-});
-panel.render(Ext.getBody());
- </code></pre>
- * @constructor
- * Create a new DataView
- * @param {Object} config The config object
- * @xtype dataview
- */
-Ext.DataView.override({
- scroll: 'vertical',
-
- /**
- * @cfg {String} pressedCls
- * A CSS class to apply to an item on the view while it is being pressed (defaults to 'x-item-pressed').
- */
- pressedCls : "x-item-pressed",
-
- /**
- * @cfg {Number} pressedDelay
- * The amount of delay between the tapstart and the moment we add the pressedCls.
- * Settings this to true defaults to 100ms
- */
- pressedDelay: 100,
-
- /**
- * @cfg {Boolean} allowDeselect Only respected if {@link #singleSelect} is true. If this is set to false,
- * a selected item will not be deselected when tapped on, ensuring that once an item has been selected at
- * least one item will always be selected. Defaults to allowed (true).
- */
- allowDeselect: true,
-
- /**
- * @cfg {String} triggerEvent
- * Defaults to 'singletap'. Other valid options are 'tap'
- */
- triggerEvent: 'singletap',
-
- triggerCtEvent: 'containertap',
-
-
- // @private
- addCmpEvents: function() {
-
- this.addEvents(
- /**
- * @event itemtap
- * Fires when a node is tapped on
- * @param {Ext.DataView} this The DataView object
- * @param {Number} index The index of the item that was tapped
- * @param {Ext.Element} item The item element
- * @param {Ext.EventObject} e The event object
- */
- 'itemtap',
-
- /**
- * @event itemdoubletap
- * Fires when a node is double tapped on
- * @param {Ext.DataView} this The DataView object
- * @param {Number} index The index of the item that was tapped
- * @param {Ext.Element} item The item element
- * @param {Ext.EventObject} e The event object
- */
- 'itemdoubletap',
-
- /**
- * @event itemswipe
- * Fires when a node is swipped
- * @param {Ext.DataView} this The DataView object
- * @param {Number} index The index of the item that was tapped
- * @param {Ext.Element} item The item element
- * @param {Ext.EventObject} e The event object
- */
- 'itemswipe',
-
- /**
- * @event containertap
- * Fires when a tap occurs and it is not on a template node.
- * @param {Ext.DataView} this
- * @param {Ext.EventObject} e The raw event object
- */
- "containertap",
-
- /**
- * @event selectionchange
- * Fires when the selected nodes change.
- * @param {Ext.DataViewSelectionModel} selectionModel The selection model of this DataView object
- * @param {Array} records Array of the selected records
- */
- "selectionchange",
-
- /**
- * @event beforeselect
- * Fires before a selection is made. If any handlers return false, the selection is cancelled.
- * @param {Ext.DataView} this
- * @param {HTMLElement} node The node to be selected
- * @param {Array} selections Array of currently selected nodes
- */
- "beforeselect"
- );
-
- },
-
- // @private
- afterRender: function() {
- var me = this;
-
- Ext.DataView.superclass.afterRender.call(me);
-
- var eventHandlers = {
- tapstart : me.onTapStart,
- tapcancel: me.onTapCancel,
- touchend : me.onTapCancel,
- doubletap: me.onDoubleTap,
- swipe : me.onSwipe,
- scope : me
- };
- eventHandlers[this.triggerEvent] = me.onTap;
- me.mon(me.getTargetEl(), eventHandlers);
-
- if (this.store) {
- this.bindStore(this.store, true);
- }
- },
-
- // @private
- onTap: function(e) {
- var item = this.findTargetByEvent(e);
- if (item) {
- Ext.fly(item).removeCls(this.pressedCls);
- var index = this.indexOf(item);
- if (this.onItemTap(item, index, e) !== false) {
- this.fireEvent("itemtap", this, index, item, e);
- }
- }
- else {
- if(this.fireEvent("containertap", this, e) !== false) {
- this.onContainerTap(e);
- }
- }
- },
-
- // @private
- onTapStart: function(e, t) {
- var me = this,
- item = this.findTargetByEvent(e);
-
- if (item) {
- if (me.pressedDelay) {
- if (me.pressedTimeout) {
- clearTimeout(me.pressedTimeout);
- }
- me.pressedTimeout = setTimeout(function() {
- Ext.fly(item).addCls(me.pressedCls);
- }, Ext.isNumber(me.pressedDelay) ? me.pressedDelay : 100);
- }
- else {
- Ext.fly(item).addCls(me.pressedCls);
- }
- }
- },
-
- // @private
- onTapCancel: function(e, t) {
- var me = this,
- item = this.findTargetByEvent(e);
-
- if (me.pressedTimeout) {
- clearTimeout(me.pressedTimeout);
- delete me.pressedTimeout;
- }
-
- if (item) {
- Ext.fly(item).removeCls(me.pressedCls);
- }
- },
-
- // @private
- onContainerTap: function(e) {
- //if (this.allowDeselect) {
- // this.clearSelections();
- //}
- },
-
- // @private
- onDoubleTap: function(e) {
- var item = this.findTargetByEvent(e);
- if (item) {
- this.fireEvent("itemdoubletap", this, this.indexOf(item), item, e);
- }
- },
-
- // @private
- onSwipe: function(e) {
- var item = this.findTargetByEvent(e);
- if (item) {
- this.fireEvent("itemswipe", this, this.indexOf(item), item, e);
- }
- },
-
- // @private
- onItemTap: function(item, index, e) {
- if (this.pressedTimeout) {
- clearTimeout(this.pressedTimeout);
- delete this.pressedTimeout;
- }
- return true;
- }
-});
-
-/**
- * @class Ext.List
- * @extends Ext.DataView
- * <p>A mechanism for displaying data using a list layout template. List uses an {@link Ext.XTemplate}
- * as its internal templating mechanism, and is bound to an {@link Ext.data.Store} so that as the data
- * in the store changes the view is automatically updated to reflect the changes.</p>
- * <p>The view also provides built-in behavior for many common events that can occur for its contained items
- * including itemtap, containertap, etc. as well as a built-in selection model. <b>In order to use these
- * features, an {@link #itemSelector} config must be provided for the DataView to determine what nodes it
- * will be working with.</b></p>
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #itemTpl}</li>
- * <li>{@link #store}</li>
- * <li>{@link #grouped}</li>
- * <li>{@link #indexBar}</li>
- * <li>{@link #singleSelect}</li>
- * <li>{@link #multiSelect}</li>
- * </ul>
- *
- * <h2>Useful Methods</h2>
- * <ul class="list">
- * <li>{@link #bindStore}</li>
- * <li>{@link #getRecord}</li>
- * <li>{@link #getRecords}</li>
- * <li>{@link #getSelectedRecords}</li>
- * <li>{@link #getSelectedNodes}</li>
- * <li>{@link #indexOf}</li>
- * </ul>
- *
- * <h2>Useful Events</h2>
- * <ul class="list">
- * <li>{@link #itemtap}</li>
- * <li>{@link #itemdoubletap}</li>
- * <li>{@link #itemswipe}</li>
- * <li>{@link #selectionchange}</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.List/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <pre><code>
-Ext.regModel('Contact', {
- fields: ['firstName', 'lastName']
-});
-
-var store = new Ext.data.JsonStore({
- model : 'Contact',
- sorters: 'lastName',
-
- getGroupString : function(record) {
- return record.get('lastName')[0];
- },
-
- data: [
- {firstName: 'Tommy', lastName: 'Maintz'},
- {firstName: 'Rob', lastName: 'Dougan'},
- {firstName: 'Ed', lastName: 'Spencer'},
- {firstName: 'Jamie', lastName: 'Avins'},
- {firstName: 'Aaron', lastName: 'Conran'},
- {firstName: 'Dave', lastName: 'Kaneda'},
- {firstName: 'Michael', lastName: 'Mullany'},
- {firstName: 'Abraham', lastName: 'Elias'},
- {firstName: 'Jay', lastName: 'Robinson'}
- ]
-});
-
-var list = new Ext.List({
- fullscreen: true,
-
- itemTpl : '{firstName} {lastName}',
- grouped : true,
- indexBar: true,
-
- store: store
-});
-list.show();
- </code></pre>
- * @constructor
- * Create a new List
- * @param {Object} config The config object
- * @xtype list
- */
-Ext.List = Ext.extend(Ext.DataView, {
- componentCls: 'x-list',
-
- /**
- * @cfg {Boolean} pinHeaders
- * Whether or not to pin headers on top of item groups while scrolling for an iPhone native list experience.
- * Defaults to <tt>false</tt> on Android and Blackberry (for performance reasons)
- * Defaults to <tt>true</tt> on other devices.
- */
- pinHeaders: Ext.is.iOS || Ext.is.Desktop,
-
- /**
- * @cfg {Boolean/Object} indexBar
- * True to render an alphabet IndexBar docked on the right.
- * This can also be a config object that will be passed to {@link Ext.IndexBar}
- * (defaults to false)
- */
- indexBar: false,
-
- /**
- * @cfg {Boolean} grouped
- * True to group the list items together (defaults to false). When using grouping, you must specify a method getGroupString
- * on the store so that grouping can be maintained.
- * <pre><code>
-Ext.regModel('Contact', {
- fields: ['firstName', 'lastName']
-});
-
-var store = new Ext.data.JsonStore({
- model : 'Contact',
- sorters: 'lastName',
-
- getGroupString : function(record) {
- // Group by the last name
- return record.get('lastName')[0];
- },
-
- data: [
- {firstName: 'Tommy', lastName: 'Maintz'},
- {firstName: 'Rob', lastName: 'Dougan'},
- {firstName: 'Ed', lastName: 'Spencer'},
- {firstName: 'Jamie', lastName: 'Avins'},
- {firstName: 'Aaron', lastName: 'Conran'},
- {firstName: 'Dave', lastName: 'Kaneda'},
- {firstName: 'Michael', lastName: 'Mullany'},
- {firstName: 'Abraham', lastName: 'Elias'},
- {firstName: 'Jay', lastName: 'Robinson'},
- {firstName: 'Tommy', lastName: 'Maintz'},
- {firstName: 'Rob', lastName: 'Dougan'},
- {firstName: 'Ed', lastName: 'Spencer'},
- {firstName: 'Jamie', lastName: 'Avins'},
- {firstName: 'Aaron', lastName: 'Conran'},
- {firstName: 'Dave', lastName: 'Kaneda'},
- {firstName: 'Michael', lastName: 'Mullany'},
- {firstName: 'Abraham', lastName: 'Elias'},
- {firstName: 'Jay', lastName: 'Robinson'}
- ]
-});
- </code></pre>
- */
- grouped: false,
-
- /**
- * @cfg {Boolean} clearSelectionOnDeactivate
- * True to clear any selections on the list when the list is deactivated (defaults to true).
- */
- clearSelectionOnDeactivate: true,
-
- renderTpl: [
- '<tpl if="grouped"><h3 class="x-list-header x-list-header-swap x-hidden-display"></h3></tpl>'
- ],
-
- groupTpl : [
- '<tpl for=".">',
- '<div class="x-list-group x-group-{id}">',
- '<h3 class="x-list-header">{group}</h3>',
- '<div class="x-list-group-items">',
- '{items}',
- '</div>',
- '</div>',
- '</tpl>'
- ],
-
- /**
- * @cfg {String} itemSelector
- * @private
- * @ignore
- * Not to be used.
- */
- itemSelector: '.x-list-item',
-
- /**
- * @cfg {String/Array} itemTpl
- * The inner portion of the item template to be rendered. Follows an XTemplate
- * structure and will be placed inside of a tpl for in the tpl configuration.
- */
-
- /**
- * @cfg {Boolean/Function/Object} onItemDisclosure
- * True to display a disclosure icon on each list item.
- * This won't bind a listener to the tap event. The list
- * will still fire the disclose event though.
- * By setting this config to a function, it will automatically
- * add a tap event listeners to the disclosure buttons which
- * will fire your function.
- * Finally you can specify an object with a 'scope' and 'handler'
- * property defined. This will also be bound to the tap event listener
- * and is useful when you want to change the scope of the handler.
- */
- onItemDisclosure: false,
-
- /**
- * @cfg {Boolean} preventSelectionOnDisclose True to prevent the item selection when the user
- * taps a disclose icon. Defaults to <tt>true</tt>
- */
- preventSelectionOnDisclose: true,
-
- // @private
- initComponent : function() {
-
-
-
-
-
-
- var memberFnsCombo = {};
-
- if (Ext.isArray(this.itemTpl)) {
- this.itemTpl = this.itemTpl.join('');
- } else if (this.itemTpl && this.itemTpl.html) {
- Ext.apply(memberFnsCombo, this.itemTpl.initialConfig);
- this.itemTpl = this.itemTpl.html;
- }
-
- if (!Ext.isDefined(this.itemTpl)) {
- throw new Error("Ext.List: itemTpl is a required configuration.");
- }
- // this check is not enitrely fool proof, does not account for spaces or multiple classes
- // if the check is done without "s then things like x-list-item-entity would throw exceptions that shouldn't have.
- if (this.itemTpl && this.itemTpl.indexOf("\"x-list-item\"") !== -1) {
- throw new Error("Ext.List: Using a CSS class of x-list-item within your own tpl will break Ext.Lists. Remove the x-list-item from the tpl/itemTpl");
- }
-
- this.tpl = '<tpl for="."><div class="x-list-item"><div class="x-list-item-body">' + this.itemTpl + '</div>';
- if (this.onItemDisclosure) {
- this.tpl += '<div class="x-list-disclosure"></div>';
- }
- this.tpl += '</div></tpl>';
- this.tpl = new Ext.XTemplate(this.tpl, memberFnsCombo);
-
-
- if (this.grouped) {
-
- this.listItemTpl = this.tpl;
- if (Ext.isString(this.listItemTpl) || Ext.isArray(this.listItemTpl)) {
- // memberFns will go away after removal of tpl configuration for itemTpl
- // this copies memberFns by storing the original configuration.
- this.listItemTpl = new Ext.XTemplate(this.listItemTpl, memberFnsCombo);
- }
- if (Ext.isString(this.groupTpl) || Ext.isArray(this.groupTpl)) {
- this.tpl = new Ext.XTemplate(this.groupTpl);
- }
- }
- else {
- this.indexBar = false;
- }
-
- if (this.scroll !== false) {
- this.scroll = {
- direction: 'vertical',
- useIndicators: !this.indexBar
- };
- }
-
- Ext.List.superclass.initComponent.call(this);
-
- if (this.onItemDisclosure) {
- // disclosure can be a function that will be called when
- // you tap the disclosure button
- if (Ext.isFunction(this.onItemDisclosure)) {
- this.onItemDisclosure = {
- scope: this,
- handler: this.onItemDisclosure
- };
- }
- }
-
- this.on('deactivate', this.onDeactivate, this);
-
- this.addEvents(
- /**
- * @event disclose
- * Fires when the user taps the disclosure icon on an item
- * @param {Ext.data.Record} record The record associated with the item
- * @param {Ext.Element} node The wrapping element of this node
- * @param {Number} index The index of this list item
- * @param {Ext.util.Event} e The tap event that caused this disclose to fire
- */
- 'disclose'
- );
- },
-
- // @private
- onRender : function() {
- if (this.grouped) {
- Ext.applyIf(this.renderData, {
- grouped: true
- });
-
- if (this.scroll) {
- Ext.applyIf(this.renderSelectors, {
- header: '.x-list-header-swap'
- });
- }
- }
-
- Ext.List.superclass.onRender.apply(this, arguments);
- },
-
- // @private
- onDeactivate : function() {
- if (this.clearSelectionOnDeactivate) {
- this.getSelectionModel().deselectAll();
- }
- },
-
- // @private
- afterRender : function() {
- if (!this.grouped) {
- this.el.addCls('x-list-flat');
- }
- this.getTargetEl().addCls('x-list-parent');
-
- if (this.indexBar) {
- this.indexBar = new Ext.IndexBar(Ext.apply({}, Ext.isObject(this.indexBar) ? this.indexBar : {}, {
- xtype: 'indexbar',
- alphabet: true,
- renderTo: this.el
- }));
- this.addCls('x-list-indexed');
- }
-
- Ext.List.superclass.afterRender.call(this);
-
- if (this.onItemDisclosure) {
- this.mon(this.getTargetEl(), 'singletap', this.handleItemDisclosure, this, {delegate: '.x-list-disclosure'});
- }
- },
-
- // @private
- initEvents : function() {
- Ext.List.superclass.initEvents.call(this);
-
- if (this.grouped) {
- if (this.pinHeaders && this.scroll) {
- this.mon(this.scroller, {
- scrollstart: this.onScrollStart,
- scroll: this.onScroll,
- scope: this
- });
- }
-
- if (this.indexBar) {
- this.mon(this.indexBar, {
- index: this.onIndex,
- scope: this
- });
- }
- }
- },
-
- //@private
- handleItemDisclosure : function(e, t) {
- var node = this.findItemByChild(t),
- record, index;
-
- if (node) {
- record = this.getRecord(node);
- index = this.indexOf(node);
- if (this.preventSelectionOnDisclose) {
- e.stopEvent();
- }
- this.fireEvent('disclose', record, node, index, e);
-
- if (Ext.isObject(this.onItemDisclosure) && this.onItemDisclosure.handler) {
- this.onItemDisclosure.handler.call(this, record, node, index);
- }
- }
- },
-
- /**
- * Set the current active group
- * @param {Object} group The group to set active
- */
- setActiveGroup : function(group) {
- var me = this;
- if (group) {
- if (!me.activeGroup || me.activeGroup.header != group.header) {
- me.header.setHTML(group.header.getHTML());
- me.header.show();
- }
- }
- else {
- me.header.hide();
- }
-
- this.activeGroup = group;
- },
-
- // @private
- getClosestGroups : function(pos) {
- // force update if not already done
- if (!this.groupOffsets) {
- this.updateOffsets();
- }
- var groups = this.groupOffsets,
- ln = groups.length,
- group, i,
- current, next;
-
- for (i = 0; i < ln; i++) {
- group = groups[i];
- if (group.offset > pos.y) {
- next = group;
- break;
- }
- current = group;
- }
-
- return {
- current: current,
- next: next
- };
- },
-
- updateIndexes : function() {
- Ext.List.superclass.updateIndexes.apply(this, arguments);
- this.updateOffsets();
- },
-
- afterComponentLayout : function() {
- Ext.List.superclass.afterComponentLayout.apply(this, arguments);
- this.updateOffsets();
- },
-
- updateOffsets : function() {
- if (this.grouped) {
- this.groupOffsets = [];
-
- var headers = this.getTargetEl().query('h3.x-list-header'),
- ln = headers.length,
- header, i;
-
- for (i = 0; i < ln; i++) {
- header = Ext.get(headers[i]);
- header.setDisplayMode(Ext.Element.VISIBILITY);
- this.groupOffsets.push({
- header: header,
- offset: header.dom.offsetTop
- });
- }
- }
- },
-
- // @private
- onScrollStart : function() {
- var offset = this.scroller.getOffset();
- this.closest = this.getClosestGroups(offset);
- this.setActiveGroup(this.closest.current);
- },
-
- // @private
- onScroll : function(scroller, pos, options) {
- if (!this.closest) {
- this.closest = this.getClosestGroups(pos);
- }
-
- if (!this.headerHeight) {
- this.headerHeight = this.header.getHeight();
- }
-
- if (pos.y <= 0) {
- if (this.activeGroup) {
- this.setActiveGroup(false);
- this.closest.next = this.closest.current;
- }
- return;
- }
- else if (
- (this.closest.next && pos.y > this.closest.next.offset) ||
- (pos.y < this.closest.current.offset)
- ) {
- this.closest = this.getClosestGroups(pos);
- this.setActiveGroup(this.closest.current);
- }
- if (this.closest.next && pos.y > 0 && this.closest.next.offset - pos.y <= this.headerHeight) {
- var transform = this.headerHeight - (this.closest.next.offset - pos.y);
- Ext.Element.cssTranslate(this.header, {x: 0, y: -transform});
- this.transformed = true;
- }
- else if (this.transformed) {
- this.header.setStyle('-webkit-transform', null);
- this.transformed = false;
- }
- },
-
- // @private
- onIndex : function(record, target, index) {
- var key = record.get('key').toLowerCase(),
- groups = this.store.getGroups(),
- ln = groups.length,
- group, i, closest, id;
-
- for (i = 0; i < ln; i++) {
- group = groups[i];
- id = this.getGroupId(group);
-
- if (id == key || id > key) {
- closest = id;
- break;
- }
- else {
- closest = id;
- }
- }
-
- closest = this.getTargetEl().down('.x-group-' + id);
- if (closest) {
- this.scroller.scrollTo({x: 0, y: closest.getOffsetsTo(this.scrollEl)[1]}, false, null, true);
- }
- },
-
- getGroupId : function(group) {
- return group.name.toLowerCase();
- },
-
- // @private
- collectData : function(records, startIndex) {
- if (!this.grouped) {
- return Ext.List.superclass.collectData.call(this, records, startIndex);
- }
-
- var results = [],
- groups = this.store.getGroups(),
- ln = groups.length,
- children, cln, c,
- group, i;
-
- for (i = 0, ln = groups.length; i < ln; i++) {
- group = groups[i];
- children = group.children;
- for (c = 0, cln = children.length; c < cln; c++) {
- children[c] = children[c].data;
- }
- results.push({
- group: group.name,
- id: this.getGroupId(group),
- items: this.listItemTpl.apply(children)
- });
- }
-
- return results;
- },
-
- // Because the groups might change by an update/add/remove we refresh the whole dataview
- // in each one of them
- // @private
- onUpdate : function(store, record) {
- if (this.grouped) {
- this.refresh();
- }
- else {
- Ext.List.superclass.onUpdate.apply(this, arguments);
- }
- },
-
- // @private
- onAdd : function(ds, records, index) {
- if (this.grouped) {
- this.refresh();
- }
- else {
- Ext.List.superclass.onAdd.apply(this, arguments);
- }
- },
-
- // @private
- onRemove : function(ds, record, index) {
- if (this.grouped) {
- this.refresh();
- }
- else {
- Ext.List.superclass.onRemove.apply(this, arguments);
- }
- }
-});
-
-Ext.reg('list', Ext.List);
-
-/**
- * @class Ext.IndexBar
- * @extends Ext.DataPanel
- * <p>IndexBar is a component used to display a list of data (primarily an {@link #alphabet}) which can then be used to quickly
- * navigate through a list (see {@link Ext.List}) of data. When a user taps on an item in the {@link Ext.IndexBar}, it will fire
- * the <tt>{@link #index}</tt> event.</p>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.IndexBar/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <p>Here is an example of the usage in a {@link Ext.List}:</p>
- * <pre><code>
-Ext.regModel('Contact', {
- fields: ['firstName', 'lastName']
-});
-
-var store = new Ext.data.JsonStore({
- model : 'Contact',
- sorters: 'lastName',
-
- getGroupString : function(record) {
- return record.get('lastName')[0];
- },
-
- data: [
- {firstName: 'Tommy', lastName: 'Maintz'},
- {firstName: 'Rob', lastName: 'Dougan'},
- {firstName: 'Ed', lastName: 'Spencer'},
- {firstName: 'Jamie', lastName: 'Avins'},
- {firstName: 'Aaron', lastName: 'Conran'},
- {firstName: 'Dave', lastName: 'Kaneda'},
- {firstName: 'Michael', lastName: 'Mullany'},
- {firstName: 'Abraham', lastName: 'Elias'},
- {firstName: 'Jay', lastName: 'Robinson'},
- {firstName: 'Tommy', lastName: 'Maintz'},
- {firstName: 'Rob', lastName: 'Dougan'},
- {firstName: 'Ed', lastName: 'Spencer'},
- {firstName: 'Jamie', lastName: 'Avins'},
- {firstName: 'Aaron', lastName: 'Conran'},
- {firstName: 'Dave', lastName: 'Kaneda'},
- {firstName: 'Michael', lastName: 'Mullany'},
- {firstName: 'Abraham', lastName: 'Elias'},
- {firstName: 'Jay', lastName: 'Robinson'}
- ]
-});
-
-var list = new Ext.List({
- tpl: '<tpl for="."><div class="contact">{firstName} <strong>{lastName}</strong></div></tpl>',
-
- itemSelector: 'div.contact',
- singleSelect: true,
- grouped : true,
- indexBar : true,
-
- store: store,
-
- floating : true,
- width : 350,
- height : 370,
- centered : true,
- modal : true,
- hideOnMaskTap: false
-});
-list.show();
- </code></pre>
- *
- * <p>Alternatively you can initate the {@link Ext.IndexBar} component manually in a custom component by using something
- * similar to the following example:<p>
- *
- * <code><pre>
-var indexBar = new Ext.IndexBar({
- dock : 'right',
- overlay : true,
- alphabet: true
-});
- * </code></pre>
- * @constructor
- * Create a new IndexBar
- * @param {Object} config The config object
- * @xtype indexbar
- */
-Ext.IndexBar = Ext.extend(Ext.DataView, {
- /**
- * @cfg {String} componentCls Base CSS class
- * Defaults to <tt>'x-indexbar'</tt>
- */
- componentCls: 'x-indexbar',
-
- /**
- * @cfg {String} direction Layout direction, can be either 'vertical' or 'horizontal'
- * Defaults to <tt>'vertical'</tt>
- */
- direction: 'vertical',
-
- /**
- * @cfg {String} tpl Template for items
- */
- tpl: '<tpl for="."><div class="x-indexbar-item">{value}</div></tpl>',
-
- /**
- * @cfg {String} itemSelector <b>Required</b>. A simple CSS selector (e.g. <tt>div.x-indexbar-item</tt> for items
- */
- itemSelector: 'div.x-indexbar-item',
-
- /**
- * @cfg {Array} letters
- * The letters to show on the index bar. Defaults to the English alphabet, A-Z.
- */
- letters: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
-
- /**
- * @cfg {String} listPrefix
- * The prefix string to be appended at the beginning of the list. E.g: useful to add a "#" prefix before numbers
- */
- listPrefix: '',
-
- /**
- * @cfg {Boolean} alphabet true to use the {@link #letters} property to show a list of the alphabet. Should <b>not</b> be used
- * in conjunction with {@link #store}.
- */
-
- /**
- * @cfg {Ext.data.Store} store
- * The store to be used for displaying data on the index bar. The store model must have a <tt>value</tt> field when using the
- * default {@link #tpl}. If no {@link #store} is defined, it will create a store using the <tt>IndexBarModel</tt> model.
- */
-
- // We dont want the body of this component to be sized by a DockLayout, thus we set the layout to be an autocomponent layout.
- componentLayout: 'autocomponent',
-
- scroll: false,
-
- // @private
- initComponent : function() {
- // No docking and no sizing of body!
- this.componentLayout = this.getComponentLayout();
-
- if (!this.store) {
- this.store = new Ext.data.Store({
- model: 'IndexBarModel'
- });
- }
-
- if (this.alphabet == true) {
- this.ui = this.ui || 'alphabet';
- }
-
- if (this.direction == 'horizontal') {
- this.horizontal = true;
- }
- else {
- this.vertical = true;
- }
-
- this.addEvents(
- /**
- * @event index
- * Fires when an item in the index bar display has been tapped
- * @param {Ext.data.Model} record The record tapped on the indexbar
- * @param {HTMLElement} target The node on the indexbar that has been tapped
- * @param {Number} index The index of the record tapped on the indexbar
- */
- 'index'
- );
-
- Ext.apply(this.renderData, {
- componentCls: this.componentCls
- });
-
- Ext.apply(this.renderSelectors, {
- body: '.' + this.componentCls + '-body'
- });
-
- Ext.IndexBar.superclass.initComponent.call(this);
- },
-
- renderTpl : ['<div class="{componentCls}-body"></div>'],
-
- getTargetEl : function() {
- return this.body;
- },
-
- // @private
- afterRender : function() {
- Ext.IndexBar.superclass.afterRender.call(this);
-
- if (this.alphabet === true) {
- this.loadAlphabet();
- }
- if (this.vertical) {
- this.el.addCls(this.componentCls + '-vertical');
- }
- else if (this.horizontal) {
- this.el.addCls(this.componentCls + '-horizontal');
- }
- },
-
- // @private
- loadAlphabet : function() {
- var letters = this.letters,
- len = letters.length,
- data = [],
- i, letter;
-
- for (i = 0; i < len; i++) {
- letter = letters[i];
- data.push({key: letter.toLowerCase(), value: letter});
- }
-
- this.store.loadData(data);
- },
-
- /**
- * Refreshes the view by reloading the data from the store and re-rendering the template.
- */
- refresh: function() {
- var el = this.getTargetEl(),
- records = this.store.getRange();
-
- el.update('');
- if (records.length < 1) {
- if (!this.deferEmptyText || this.hasSkippedEmptyText) {
- el.update(this.emptyText);
- }
- this.all.clear();
- } else {
- this.tpl.overwrite(el, this.collectData(records, 0));
- this.all.fill(Ext.query(this.itemSelector, el.dom));
- this.updateIndexes(0);
- }
- this.hasSkippedEmptyText = true;
- this.fireEvent('refresh');
- },
-
- collectData : function() {
- var data = Ext.IndexBar.superclass.collectData.apply(this, arguments);
- if (this.listPrefix.length > 0) {
- data.unshift({
- key: '',
- value: this.listPrefix
- });
- }
- return data;
- },
-
- // @private
- initEvents : function() {
- Ext.IndexBar.superclass.initEvents.call(this);
-
- this.mon(this.el, {
- touchstart: this.onTouchStart,
- touchend: this.onTouchEnd,
- touchmove: this.onTouchMove,
- scope: this
- });
- },
-
- // @private
- onTouchStart : function(e, t) {
- e.stopEvent();
- this.el.addCls(this.componentCls + '-pressed');
- this.pageBox = this.el.getPageBox();
- this.onTouchMove(e);
- },
-
- // @private
- onTouchEnd : function(e, t) {
- e.stopEvent();
- this.el.removeCls(this.componentCls + '-pressed');
- },
-
- // @private
- onTouchMove : function(e) {
- e.stopPropagation();
-
- var point = Ext.util.Point.fromEvent(e),
- target,
- record,
- pageBox = this.pageBox;
-
- if (!pageBox) {
- pageBox = this.pageBox = this.el.getPageBox();
- }
-
- if (this.vertical) {
- if (point.y > pageBox.bottom || point.y < pageBox.top) {
- return;
- }
- target = Ext.Element.fromPoint(pageBox.left + (pageBox.width / 2), point.y);
- }
- else if (this.horizontal) {
- if (point.x > pageBox.right || point.x < pageBox.left) {
- return;
- }
- target = Ext.Element.fromPoint(point.x, pageBox.top + (pageBox.height / 2));
- }
-
- if (target) {
- record = this.getRecord(target.dom);
- if (record) {
- this.fireEvent('index', record, target, this.indexOf(target));
- }
- }
- },
-
- /**
- * Method to determine whether this Sortable is currently disabled.
- * @return {Boolean} the disabled state of this Sortable.
- */
- isVertical : function() {
- return this.vertical;
- },
-
- /**
- * Method to determine whether this Sortable is currently sorting.
- * @return {Boolean} the sorting state of this Sortable.
- */
- isHorizontal : function() {
- return this.horizontal;
- }
-});
-
-Ext.reg('indexbar', Ext.IndexBar);
-
-Ext.regModel('IndexBarModel', {
- fields: ['key', 'value']
-});
-
-/**
- * @class Ext.Toolbar
- * @extends Ext.Container
- *
- * <p>Toolbars are most commonly used as dockedItems within an Ext.Panel. They can
- * be docked at the 'top' or 'bottom' of a Panel by specifying the dock config.</p>
- *
- * <p>The {@link #defaultType} of Toolbar's is '{@link Ext.Button button}'.</p>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.Toolbar/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <pre><code>
-var myToolbar = new Ext.Toolbar({
- dock : 'top',
- title: 'My Toolbar',
- items: [
- {
- text: 'My Button'
- }
- ]
-});
-
-var myPanel = new Ext.Panel({
- dockedItems: [myToolbar],
- fullscreen : true,
- html : 'Test Panel'
-});</code></pre>
- * @xtype toolbar
- */
-Ext.Toolbar = Ext.extend(Ext.Container, {
- // private
- isToolbar: true,
-
- /**
- * @cfg {xtype} defaultType
- * The default xtype to create. (Defaults to 'button')
- */
- defaultType: 'button',
-
- /**
- * @cfg {String} baseCls
- * The base CSS class to apply to the Carousel's element (defaults to <code>'x-toolbar'</code>).
- */
- baseCls: 'x-toolbar',
-
- /**
- * @cfg {String} titleCls
- * The CSS class to apply to the titleEl (defaults to <code>'x-toolbar-title'</code>).
- */
- titleCls: 'x-toolbar-title',
-
- /**
- * @cfg {String} ui
- * Style options for Toolbar. Default is 'dark'. 'light' is also available.
- */
- ui: 'dark',
-
- /**
- * @cfg {Object} layout (optional)
- * A layout config object. A string is NOT supported here.
- */
- layout: null,
-
- /**
- * @cfg {String} title (optional)
- * The title of the Toolbar.
- */
-
- // properties
-
- /**
- * The title Element
- * @property titleEl
- * @type Ext.Element
- */
- titleEl: null,
-
- initComponent : function() {
- this.layout = Ext.apply({}, this.layout || {}, {
- type: 'hbox',
- align: 'center'
- });
- Ext.Toolbar.superclass.initComponent.call(this);
- },
-
- afterRender : function() {
- Ext.Toolbar.superclass.afterRender.call(this);
-
- if (this.title) {
- this.titleEl = this.el.createChild({
- cls: this.titleCls,
- html: this.title
- });
- }
- },
-
- /**
- * Set the title of the Toolbar
- * @param title {String} This can be arbitrary HTML.
- */
- setTitle : function(title) {
- this.title = title;
- if (this.rendered) {
- if (!this.titleEl) {
- this.titleEl = this.el.createChild({
- cls: this.titleCls,
- html: this.title
- });
- }
- this.titleEl.setHTML(title);
- }
- },
-
- /**
- * Show the title if it exists.
- */
- showTitle : function() {
- if (this.titleEl) {
- this.titleEl.show();
- }
- },
-
- /**
- * Hide the title if it exists.
- */
- hideTitle : function() {
- if (this.titleEl) {
- this.titleEl.hide();
- }
- }
-});
-
-Ext.reg('toolbar', Ext.Toolbar);
-
-
-/**
- * @class Ext.Spacer
- * @extends Ext.Component
- *
- * <p>By default the spacer component will take up a flex of 1 unless a width is set.</p>
- *
- * <p>Example usage:</p>
- * <pre><code>
-var toolbar = new Ext.Toolbar({
- title: 'Toolbar Title',
-
- items: [
- {xtype: 'spacer'},
- {
- xtype: 'Button',
- text : 'Button!'
- }
- ]
-});
- * </code></pre>
- *
- * @xtype spacer
- */
-Ext.Spacer = Ext.extend(Ext.Component, {
- initComponent : function() {
- if (!this.width) {
- this.flex = 1;
- }
-
- Ext.Spacer.superclass.initComponent.call(this);
- },
-
- onRender : function() {
- Ext.Spacer.superclass.onRender.apply(this, arguments);
-
- if (this.flex) {
- this.el.setStyle('-webkit-box-flex', this.flex);
- }
- }
-});
-
-Ext.reg('spacer', Ext.Spacer);
-/**
- * @class Ext.Sheet
- * @extends Ext.Panel
- *
- * <p>A general sheet class. This renderable container provides base support for orientation-aware
- * transitions for popup or side-anchored sliding Panels.</p>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.Sheet/screenshot.png" /></p>
- *
- * <h2>Example usage:</h2>
- * <pre><code>
-var sheet = new Ext.Sheet({
- height : 200,
- stretchX: true,
- stretchY: true,
-
- layout: {
- type: 'hbox',
- align: 'stretch'
- },
-
- dockedItems: [
- {
- dock : 'bottom',
- xtype: 'button',
- text : 'Click me'
- }
- ]
-});
-sheet.show();
- * </code></pre>
- *
- * <p>See {@link Ext.Picker} and {@link Ext.DatePicker}</p>
- * @xtype sheet
- */
-Ext.Sheet = Ext.extend(Ext.Panel, {
- baseCls : 'x-sheet',
- centered : false,
- floating : true,
- modal : true,
- draggable : false,
- monitorOrientation : true,
-
- hidden : true,
-
- /**
- * @cfg {Boolean} hideOnMaskTap
- * True to automatically bind a tap listener to the mask that hides the window.
- * Defaults to false. Note: if you don't set this property to false you have to programmaticaly
- * hide the overlay.
- */
- hideOnMaskTap : false,
-
- /**
- * @cfg {Boolean} stretchX
- * If true, the width of anchored Sheets are adjusted to fill the entire top/bottom axis width,
- * or false to center the Sheet along the same axis based upon the sheets current/calculated width.
- * This option is ignored when {link #centered} is true or x/y coordinates are specified for the Sheet.
- */
-
- /**
- * @cfg {Boolean} stretchY
- * If true, the height of anchored Sheets are adjusted to fill the entire right/left axis height,
- * or false to center the Sheet along the same axis based upon the sheets current/calculated height.
- * This option is ignored when {link #centered} is true or x/y coordinates are specified for the Sheet.
- */
-
- /**
- * @cfg {String} enter
- * The viewport side from which to anchor the sheet when made visible (top, bottom, left, right)
- * Defaults to 'bottom'
- */
- enter : 'bottom',
-
- /**
- * @cfg {String} exit
- * The viewport side used as the exit point when hidden (top, bottom, left, right)
- * Applies to sliding animation effects only. Defaults to 'bottom'
- */
- exit : 'bottom',
-
-
- /**
- * @cfg {String/Object} enterAnimation
- * the named Ext.anim effect or animation configuration object used for transitions
- * when the component is shown. Defaults to 'slide'
- */
- enterAnimation : 'slide',
-
- /**
- *
- * @cfg {String/Object} exitAnimation
- * the named Ext.anim effect or animation configuration object used for transitions
- * when the component is hidden. Defaults to 'slide'
- */
- exitAnimation : 'slide',
-
- // @private slide direction defaults
- transitions : {
- bottom : 'up',
- top : 'down',
- right : 'left',
- left : 'right'
- },
-
- //@private
- animSheet : function(animate) {
- var anim = null,
- me = this,
- tr = me.transitions,
- opposites = Ext.Anim.prototype.opposites || {};
-
- if (animate && this[animate]) {
- if (animate == 'enter') {
- anim = (typeof me.enterAnimation == 'string') ?
- {
- type : me.enterAnimation || 'slide',
- direction : tr[me.enter] || 'up'
- } : me.enterAnimation;
-
- }
- else if (animate == 'exit') {
- anim = (typeof me.exitAnimation == 'string') ?
- {
- type : me.exitAnimation || 'slide',
- direction : tr[me.exit] || 'down'
- } : me.exitAnimation;
- }
- }
- return anim;
- },
-
- // @private
- orient : function(orientation, w, h) {
- if(!this.container || this.centered || !this.floating){
- return this;
- }
-
- var me = this,
- cfg = me.initialConfig || {},
- //honor metrics (x, y, height, width) initialConfig
- size = {width : cfg.width, height : cfg.height},
- pos = {x : cfg.x, y : cfg.y},
- box = me.el.getPageBox(),
- pageBox, scrollTop = 0;
-
- if (me.container.dom == document.body) {
- pageBox = {
- width: window.innerWidth,
- height: window.innerHeight
- };
- scrollTop = document.body.scrollTop;
- }
- else {
- pageBox = me.container.getPageBox();
- }
-
- pageBox.centerY = pageBox.height / 2;
- pageBox.centerX = pageBox.width / 2;
-
- if(pos.x != undefined || pos.y != undefined){
- pos.x = pos.x || 0;
- pos.y = pos.y || 0;
- }
- else {
- if (/^(bottom|top)/i.test(me.enter)) {
- size.width = me.stretchX ? pageBox.width : Math.min(200,Math.max(size.width || box.width || pageBox.width, pageBox.width));
- size.height = Math.min(size.height || 0, pageBox.height) || undefined;
- size = me.setSize(size).getSize();
- pos.x = pageBox.centerX - size.width / 2;
- pos.y = me.enter == 'top' ? 0 : pageBox.height - size.height + scrollTop;
-
- } else if (/^(left|right)/i.test(me.enter)) {
- size.height = me.stretchY ? pageBox.height : Math.min(200, Math.max(size.height || box.height || pageBox.height, pageBox.height));
- size.width = Math.min(size.width || 0, pageBox.width) || undefined;
- size = me.setSize(size).getSize();
- pos.y = 0;
- pos.x = me.enter == 'left' ? 0 : pageBox.width - size.width;
- }
- }
- me.setPosition(pos);
- return this;
- },
-
- // @private
- afterRender : function() {
- Ext.Sheet.superclass.afterRender.apply(this, arguments);
- this.el.setDisplayMode(Ext.Element.OFFSETS);
- },
-
- // @private
- onShow : function(animation) {
- this.orient();
- return Ext.Sheet.superclass.onShow.call(this, animation || this.animSheet('enter'));
- },
-
- // @private
- onOrientationChange : function(orientation, w, h) {
- this.orient();
- Ext.Sheet.superclass.onOrientationChange.apply(this, arguments);
- },
-
- // @private
- beforeDestroy : function() {
- delete this.showAnimation;
- this.hide(false);
- Ext.Sheet.superclass.beforeDestroy.call(this);
- }
-});
-
-Ext.reg('sheet', Ext.Sheet);
-
-/**
- * @class Ext.ActionSheet
- * @extends Ext.Sheet
- *
- * <p>A Button Sheet class designed to popup or slide/anchor a series of buttons.</p>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.ActionSheet/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <pre><code>
-var actionSheet = new Ext.ActionSheet({
- items: [
- {
- text: 'Delete draft',
- ui : 'decline'
- },
- {
- text: 'Save draft'
- },
- {
- text: 'Cancel',
- ui : 'confirm'
- }
- ]
-});
-actionSheet.show();</code></pre>
- * @xtype sheet
- */
-
-Ext.ActionSheet = Ext.extend(Ext.Sheet, {
- componentCls: 'x-sheet-action',
-
- stretchY: true,
- stretchX: true,
-
- defaultType: 'button',
-
- constructor : function(config) {
- config = config || {};
-
- Ext.ActionSheet.superclass.constructor.call(this, Ext.applyIf({
- floating : true
- }, config));
- }
-});
-
-Ext.reg('actionsheet', Ext.ActionSheet);
-/**
- * @class Ext.TabBar
- * @extends Ext.Panel
- *
- * <p>Used in the {@link Ext.TabPanel} component to display {@link Ext.Tab} components.</p>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.TabBar/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
-<pre><code>
-var bar = new Ext.TabBar({
- dock : 'top',
- ui : 'dark',
- items: [
- {
- text: '1st Button'
- },
- {
- text: '2nd Button'
- }
- ]
-});
-
-var myPanel = new Ext.Panel({
- dockedItems: [bar],
- fullscreen : true,
- html : 'Test Panel'
-});
-</code></pre>
- *
- * @xtype tabbar
- */
-Ext.TabBar = Ext.extend(Ext.Panel, {
- componentCls: 'x-tabbar',
-
- /**
- * @type {Ext.Tab}
- * Read-only property of the currently active tab.
- */
- activeTab: null,
-
- // @private
- defaultType: 'tab',
-
- /**
- * @cfg {Boolean} sortable
- * Enable sorting functionality for the TabBar.
- */
- sortable: false,
-
- /**
- * @cfg {Number} sortHoldThreshold
- * Duration in milliseconds that a user must hold a tab
- * before dragging. The sortable configuration must be set for this setting
- * to be used.
- */
- sortHoldThreshold: 350,
-
- // @private
- initComponent : function() {
- /**
- * @event change
- * @param {Ext.TabBar} this
- * @param {Ext.Tab} tab The Tab button
- * @param {Ext.Component} card The component that has been activated
- */
- this.addEvents('change');
-
- this.layout = Ext.apply({}, this.layout || {}, {
- type: 'hbox',
- align: 'middle'
- });
-
- Ext.TabBar.superclass.initComponent.call(this);
-
- },
-
- // @private
- initEvents : function() {
- if (this.sortable) {
- this.sortable = new Ext.util.Sortable(this.el, {
- itemSelector: '.x-tab',
- direction: 'horizontal',
- delay: this.sortHoldThreshold,
- constrain: true
- });
- this.mon(this.sortable, 'sortchange', this.onSortChange, this);
- }
-
- this.mon(this.el, {
- touchstart: this.onTouchStart,
- scope: this
- });
-
- Ext.TabBar.superclass.initEvents.call(this);
- },
-
- // @private
- onTouchStart : function(e, t) {
- t = e.getTarget('.x-tab');
- if (t) {
- this.onTabTap(Ext.getCmp(t.id));
- }
- },
-
- // @private
- onSortChange : function(sortable, el, index) {
- },
-
- // @private
- onTabTap : function(tab) {
- if (!tab.disabled) {
- if (this.cardLayout) {
- if (this.cardSwitchAnimation) {
- var animConfig = {
- reverse: (this.items.indexOf(tab) < this.items.indexOf(this.activeTab)) ? true : false
- };
-
- if (Ext.isObject(this.cardSwitchAnimation)) {
- Ext.apply(animConfig, this.cardSwitchAnimation);
- } else {
- Ext.apply(animConfig, {
- type: this.cardSwitchAnimation
- });
- }
- }
-
- this.cardLayout.setActiveItem(tab.card, animConfig || this.cardSwitchAnimation);
- }
- this.activeTab = tab;
- this.fireEvent('change', this, tab, tab.card);
- }
- },
-
- /**
- * Returns a reference to the TabPanel's layout that wraps around the TabBar.
- * @private
- */
- getCardLayout : function() {
- return this.cardLayout;
- }
-});
-
-Ext.reg('tabbar', Ext.TabBar);
-
-/**
- * @class Ext.Tab
- * @extends Ext.Button
- *
- * <p>Used in the {@link Ext.TabBar} component. This shouldn't be used directly, instead use {@link Ext.TabBar} or {@link Ext.TabPanel}.</p>
- *
- * @xtype tab
- */
-Ext.Tab = Ext.extend(Ext.Button, {
- isTab: true,
- baseCls: 'x-tab',
-
- /**
- * @cfg {String} pressedCls
- * The CSS class to be applied to a Tab when it is pressed. Defaults to 'x-tab-pressed'.
- * Providing your own CSS for this class enables you to customize the pressed state.
- */
- pressedCls: 'x-tab-pressed',
-
- /**
- * @cfg {String} activeCls
- * The CSS class to be applied to a Tab when it is active. Defaults to 'x-tab-active'.
- * Providing your own CSS for this class enables you to customize the active state.
- */
- activeCls: 'x-tab-active',
-
- /**
- * @property Boolean
- * Read-only property indicating that this tab is currently active.
- * This is NOT a public configuration.
- */
- active: false,
-
- // @private
- initComponent : function() {
- this.addEvents(
- /**
- * @event activate
- * @param {Ext.Tab} this
- */
- 'activate',
- /**
- * @event deactivate
- * @param {Ext.Tab} this
- */
- 'deactivate'
- );
-
- Ext.Tab.superclass.initComponent.call(this);
-
- var card = this.card;
- if (card) {
- this.card = null;
- this.setCard(card);
- }
- },
-
- /**
- * Sets the card associated with this tab
- */
- setCard : function(card) {
- if (this.card) {
- this.mun(this.card, {
- activate: this.activate,
- deactivate: this.deactivate,
- scope: this
- });
- }
- this.card = card;
- if (card) {
- Ext.apply(this, card.tab || {});
- this.setText(this.title || card.title || this.text );
- this.setIconClass(this.iconCls || card.iconCls);
- this.setBadge(this.badgeText || card.badgeText);
-
- this.mon(card, {
- beforeactivate: this.activate,
- beforedeactivate: this.deactivate,
- scope: this
- });
- }
- },
-
- onRender: function() {
- Ext.Tab.superclass.onRender.apply(this, arguments);
- if (this.active) {
- this.el.addCls(this.activeCls);
- }
- },
-
- /**
- * Retrieves a reference to the card associated with this tab
- * @returns {Mixed} card
- */
- getCard : function() {
- return this.card;
- },
-
- // @private
- activate : function() {
- this.active = true;
- if (this.el) {
- this.el.addCls(this.activeCls);
- }
- this.fireEvent('activate', this);
- },
-
- // @private
- deactivate : function() {
- this.active = false;
- if (this.el) {
- this.el.removeCls(this.activeCls);
- }
- this.fireEvent('deactivate', this);
- }
-});
-
-Ext.reg('tab', Ext.Tab);
-
-/**
- * @class Ext.TabPanel
- * @extends Ext.Panel
- *
- * TabPanel is a Container which can hold other components to be accessed in a tabbed
- * interface. It uses a {@link Ext.TabBar} to display a {@link Ext.Tab} for each item defined.
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #ui}</li>
- * <li>{@link #tabBarDock}</li>
- * <li>{@link #cardSwitchAnimation}</li>
- * <li>{@link #sortable}</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.TabPanel/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <pre><code>
-new Ext.TabPanel({
- fullscreen: true,
- ui : 'dark',
- sortable : true,
- items: [
- {
- title: 'Tab 1',
- html : '1',
- cls : 'card1'
- },
- {
- title: 'Tab 2',
- html : '2',
- cls : 'card2'
- },
- {
- title: 'Tab 3',
- html : '3',
- cls : 'card3'
- }
- ]
-});</code></pre>
- * @xtype tabpanel
- */
-Ext.TabPanel = Ext.extend(Ext.Panel, {
- /**
- * @cfg {String} cardSwitchAnimation
- * Animation to be used during transitions of cards.
- * Any valid value from Ext.anims can be used ('fade', 'slide', 'flip', 'cube', 'pop', 'wipe'), or false.
- * Defaults to <tt>'slide'</tt>.
- */
- cardSwitchAnimation: 'slide',
-
- /**
- * @cfg {String} tabBarDock
- * Where to dock the Ext.TabPanel. Valid values are 'top' and 'bottom'.
- */
- tabBarDock: 'top',
- componentCls: 'x-tabpanel',
-
- /**
- * @cfg {String} ui
- * Defaults to 'dark'.
- */
- ui: 'dark',
-
- /**
- * @cfg {Object} layout
- * @hide
- */
-
- /**
- * @cfg {Object} tabBar
- * An Ext.TabBar configuration
- */
-
- /**
- * @cfg {Boolean} sortable
- * Enable sorting functionality for the TabBar.
- */
-
- // @private
- initComponent : function() {
-
-
- var layout = new Ext.layout.CardLayout(this.layout || {});
- this.layout = null;
- this.setLayout(layout);
-
- this.tabBar = new Ext.TabBar(Ext.apply({}, this.tabBar || {}, {
- cardLayout: layout,
- cardSwitchAnimation: this.cardSwitchAnimation,
- dock: this.tabBarDock,
- ui: this.ui,
- sortable: this.sortable
- }));
-
- if (this.dockedItems && !Ext.isArray(this.dockedItems)) {
- this.dockedItems = [this.dockedItems];
- }
- else if (!this.dockedItems) {
- this.dockedItems = [];
- }
- this.dockedItems.push(this.tabBar);
-
- Ext.TabPanel.superclass.initComponent.call(this);
- },
-
- /**
- * Retrieves a reference to the Ext.TabBar associated with the TabPanel.
- * @returns {Ext.TabBar} tabBar
- */
- getTabBar : function() {
- return this.tabBar;
- },
-
- // whenever a component is added to the TabPanel, add a
- // tab into the tabBar
- onAdd: function(cmp, idx) {
- var tabBar = this.tabBar;
- // maintain cross reference between tab and card
- cmp.tab = tabBar.insert(idx, {
- xtype: 'tab',
- card: cmp
- });
- tabBar.doLayout();
- },
-
-
- onRemove: function(cmp, autoDestroy) {
- // remove the tab from the tabBar
- this.tabBar.remove(cmp.tab, autoDestroy);
- this.tabBar.doLayout();
- }
-});
-
-Ext.reg('tabpanel', Ext.TabPanel);
-
-/**
- * @class Ext.Carousel
- * @extends Ext.Panel
- *
- * <p>A customized Panel which provides the ability to slide back and forth between
- * different child items.</p>
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #ui} (defines the style of the carousel)</li>
- * <li>{@link #direction} (defines the direction of the carousel)</li>
- * <li>{@link #indicator} (defines if the indicator show be shown)</li>
- * </ul>
- *
- * <h2>Useful Methods</h2>
- * <ul class="list">
- * <li>{@link #next} (moves to the next card)</li>
- * <li>{@link #prev} (moves to the previous card)</li>
- * <li>{@link #setActiveItem} (moves to the passed card)</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.Carousel/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
-<pre><code>
-var carousel = new Ext.Carousel({
- items: [
- {
- html: '<p>Navigate the carousel on this page by swiping left/right.</p>',
- cls : 'card card1'
- },
- {
- html: '<p>Clicking on either side of the indicators below</p>',
- cls : 'card card2'
- },
- {
- html: 'Card #3',
- cls : 'card card3'
- }
- ]
-});
-
-var panel = new Ext.Panel({
- cls: 'cards',
- layout: {
- type : 'vbox',
- align: 'stretch'
- },
- defaults: {
- flex: 1
- },
- items: [
- carousel,
- {
- xtype : 'carousel',
- ui : 'light',
- direction: 'vertical',
-
- items: [
- {
- html: '<p>Carousels can be vertical and given a ui of "light" or "dark".</p>',
- cls : 'card card1'
- },
- {
- html: 'Card #2',
- cls : 'card card2'
- },
- {
- html: 'Card #3',
- cls : 'card card3'
- }
- ]
- }
- ]
-});
-</code></pre>
- * @xtype carousel
- */
-Ext.Carousel = Ext.extend(Ext.Panel, {
- /**
- * @cfg {String} baseCls
- * The base CSS class to apply to the Carousel's element (defaults to <code>'x-carousel'</code>).
- */
- baseCls: 'x-carousel',
-
- /**
- * @cfg {Boolean} indicator
- * Provides an indicator while toggling between child items to let the user
- * know where they are in the card stack.
- */
- indicator: true,
-
- /**
- * @cfg {String} ui
- * Style options for Carousel. Default is 'dark'. 'light' is also available.
- */
- ui: 'dark',
-
- /**
- * @cfg {String} direction
- * The direction of the Carousel. Default is 'horizontal'. 'vertical' also available.
- */
- direction: 'horizontal',
-
- // @private
- horizontal: false,
- // @private
- vertical: false,
-
- // @private
- initComponent: function() {
- this.layout = {
- type: 'card',
- // This will set the size of all cards in this container on each layout
- sizeAllCards: true,
- // This will prevent the hiding of items on card switch
- hideInactive: false,
- itemCls: 'x-carousel-item',
- targetCls: 'x-carousel-body',
- setOwner : function(owner) {
- Ext.layout.CardLayout.superclass.setOwner.call(this, owner);
- }
- };
-
- if (this.indicator) {
- var cfg = Ext.isObject(this.indicator) ? this.indicator : {};
- this.indicator = new Ext.Carousel.Indicator(Ext.apply({}, cfg, {
- direction: this.direction,
- carousel: this,
- ui: this.ui
- }));
- }
-
- if (this.direction == 'horizontal') {
- this.horizontal = true;
- }
- else {
- this.vertical = true;
- }
-
- Ext.Carousel.superclass.initComponent.call(this);
- },
-
- // @private
- afterRender: function() {
- Ext.Carousel.superclass.afterRender.call(this);
-
- // Bind the required listeners
- this.mon(this.body, {
- drag: this.onDrag,
- dragThreshold: 5,
- dragend: this.onDragEnd,
- direction: this.direction,
- scope: this
- });
-
- this.el.addCls(this.baseCls + '-' + this.direction);
- },
-
- // private, inherit docs
- onAdd: function(){
- Ext.Carousel.superclass.onAdd.apply(this, arguments);
- var indicator = this.indicator;
- if (indicator) {
- indicator.onCardAdd();
- }
- },
-
- // private, inherit docs
- onRemove: function(){
- Ext.Carousel.superclass.onRemove.apply(this, arguments);
- var indicator = this.indicator;
- if (indicator) {
- indicator.onCardRemove();
- }
- },
-
- /**
- * The afterLayout method on the carousel just makes sure the active card
- * is still into view. It also makes sure the indicator is pointing to
- * the right card.
- * @private
- */
- afterLayout : function() {
- Ext.Carousel.superclass.afterLayout.apply(this, arguments);
-
- this.currentSize = this.body.getSize();
- this.currentScroll = {x: 0, y: 0};
-
- this.updateCardPositions();
-
- var activeItem = this.layout.getActiveItem();
- if (activeItem && this.indicator) {
- this.indicator.onBeforeCardSwitch(this, activeItem, null, this.items.indexOf(activeItem));
- }
- },
-
- /**
- * The onDrag method sets the currentScroll object. It also slows down the drag
- * if we are at the bounds of the carousel.
- * @private
- */
- onDrag : function(e) {
- this.currentScroll = {
- x: e.deltaX,
- y: e.deltaY
- };
-
- // Slow the drag down in the bounce
- var activeIndex = this.items.items.indexOf(this.layout.activeItem);
- // If this is a horizontal carousel
- if (this.horizontal) {
- if (
- // And we are on the first card and dragging left
- (activeIndex == 0 && e.deltaX > 0) ||
- // Or on the last card and dragging right
- (activeIndex == this.items.length - 1 && e.deltaX < 0)
- ) {
- // Then slow the drag down
- this.currentScroll.x = e.deltaX / 2;
- }
- }
- // If this is a vertical carousel
- else if (this.vertical) {
- if (
- // And we are on the first card and dragging up
- (activeIndex == 0 && e.deltaY > 0) ||
- // Or on the last card and dragging down
- (activeIndex == this.items.length - 1 && e.deltaY < 0)
- ) {
- // Then slow the drag down
- this.currentScroll.y = e.deltaY / 2;
- }
- }
- // This will update all the cards to their correct position based on the current drag
- this.updateCardPositions();
- },
-
- /**
- * This will update all the cards to their correct position based on the current drag.
- * It can be passed true to animate the position updates.
- * @private
- */
- updateCardPositions : function(animate) {
- var cards = this.items.items,
- ln = cards.length,
- cardOffset,
- i, card, el, style;
-
- // Now we loop over the items and position the active item
- // in the middle of the strip, and the two items on either
- // side to the left and right.
- for (i = 0; i < ln; i++) {
- card = cards[i];
-
- // This means the items is within 2 cards of the active item
- if (this.isCardInRange(card)) {
- if (card.hidden) {
- card.show();
- }
-
- el = card.el;
- style = el.dom.style;
-
- if (animate) {
- if (card === this.layout.activeItem) {
- el.on('webkitTransitionEnd', this.onTransitionEnd, this, {single: true});
- }
- style.webkitTransitionDuration = '300ms';
- }
- else {
- style.webkitTransitionDuration = '0ms';
- }
-
- cardOffset = this.getCardOffset(card);
- if (this.horizontal) {
- Ext.Element.cssTransform(el, {translate: [cardOffset, 0]});
- }
- else {
- Ext.Element.cssTransform(el, {translate: [0, cardOffset]});
- }
- }
- else if (!card.hidden) {
- // All other items we position far away
- card.hide();
- }
- }
- },
-
- /**
- * Returns the amount of pixels from the current drag to a card.
- * @private
- */
- getCardOffset : function(card) {
- var cardOffset = this.getCardIndexOffset(card),
- currentSize = this.currentSize,
- currentScroll = this.currentScroll;
-
- return this.horizontal ?
- (cardOffset * currentSize.width) + currentScroll.x :
- (cardOffset * currentSize.height) + currentScroll.y;
- },
-
- /**
- * Returns the difference between the index of the active card and the passed card.
- * @private
- */
- getCardIndexOffset : function(card) {
- return this.items.items.indexOf(card) - this.getActiveIndex();
- },
-
- /**
- * Returns true if the passed card is within 2 cards from the active card.
- * @private
- */
- isCardInRange : function(card) {
- return Math.abs(this.getCardIndexOffset(card)) <= 2;
- },
-
- /**
- * Returns the index of the currently active card.
- * @return {Number} The index of the currently active card.
- */
- getActiveIndex : function() {
- return this.items.indexOf(this.layout.activeItem);
- },
-
- /**
- * This determines if we are going to the next card, the previous card, or back to the active card.
- * @private
- */
- onDragEnd : function(e, t) {
- var previousDelta, deltaOffset;
-
- if (this.horizontal) {
- deltaOffset = e.deltaX;
- previousDelta = e.previousDeltaX;
- }
- else {
- deltaOffset = e.deltaY;
- previousDelta = e.previousDeltaY;
- }
-
- // We have gone to the right
- if (deltaOffset < 0 && Math.abs(deltaOffset) > 3 && previousDelta <= 0 && this.layout.getNext()) {
- this.next();
- }
- // We have gone to the left
- else if (deltaOffset > 0 && Math.abs(deltaOffset) > 3 && previousDelta >= 0 && this.layout.getPrev()) {
- this.prev();
- }
- else {
- // drag back to current active card
- this.scrollToCard(this.layout.activeItem);
- }
- },
-
- /**
- * Here we make sure that the card we are switching to is not translated
- * by the carousel anymore. This is only if we are switching card using
- * the setActiveItem of setActiveItem methods and thus customDrag is not set
- * to true.
- * @private
- */
- onBeforeCardSwitch : function(newCard) {
- if (!this.customDrag && this.items.indexOf(newCard) != -1) {
- var style = newCard.el.dom.style;
- style.webkitTransitionDuration = null;
- style.webkitTransform = null;
- }
- return Ext.Carousel.superclass.onBeforeCardSwitch.apply(this, arguments);
- },
-
- /**
- * This is an internal function that is called in onDragEnd that goes to
- * the next or previous card.
- * @private
- */
- scrollToCard : function(newCard) {
- this.currentScroll = {x: 0, y: 0};
- this.oldCard = this.layout.activeItem;
-
- if (newCard != this.oldCard && this.isCardInRange(newCard) && this.onBeforeCardSwitch(newCard, this.oldCard, this.items.indexOf(newCard), true) !== false) {
- this.layout.activeItem = newCard;
- if (this.horizontal) {
- this.currentScroll.x = -this.getCardOffset(newCard);
- }
- else {
- this.currentScroll.y = -this.getCardOffset(newCard);
- }
- }
-
- this.updateCardPositions(true);
- },
-
- // @private
- onTransitionEnd : function(e, t) {
- this.customDrag = false;
- this.currentScroll = {x: 0, y: 0};
- if (this.oldCard && this.layout.activeItem != this.oldCard) {
- this.onCardSwitch(this.layout.activeItem, this.oldCard, this.items.indexOf(this.layout.activeItem), true);
- }
- delete this.oldCard;
- },
-
- /**
- * This function makes sure that all the cards are in correct locations
- * after a card switch
- * @private
- */
- onCardSwitch : function(newCard, oldCard, index, animated) {
- this.currentScroll = {x: 0, y: 0};
- this.updateCardPositions();
- Ext.Carousel.superclass.onCardSwitch.apply(this, arguments);
- newCard.fireEvent('activate', newCard);
- },
-
- /**
- * Switches the next card
- */
- next: function() {
- var next = this.layout.getNext();
- if (next) {
- this.customDrag = true;
- this.scrollToCard(next);
- }
- return this;
- },
-
- /**
- * Switches the previous card
- */
- prev: function() {
- var prev = this.layout.getPrev();
- if (prev) {
- this.customDrag = true;
- this.scrollToCard(prev);
- }
- return this;
- },
-
- /**
- * Method to determine whether this Sortable is currently disabled.
- * @return {Boolean} the disabled state of this Sortable.
- */
- isVertical : function() {
- return this.vertical;
- },
-
- /**
- * Method to determine whether this Sortable is currently sorting.
- * @return {Boolean} the sorting state of this Sortable.
- */
- isHorizontal : function() {
- return this.horizontal;
- },
-
- // private, inherit docs
- beforeDestroy: function(){
- Ext.destroy(this.indicator);
- Ext.Carousel.superclass.beforeDestroy.call(this);
- }
-});
-
-Ext.reg('carousel', Ext.Carousel);
-
-/**
- * @class Ext.Carousel.Indicator
- * @extends Ext.Component
- * @xtype carouselindicator
- * @private
- *
- * A private utility class used by Ext.Carousel to create indicators.
- */
-Ext.Carousel.Indicator = Ext.extend(Ext.Component, {
- baseCls: 'x-carousel-indicator',
-
- initComponent: function() {
- if (this.carousel.rendered) {
- this.render(this.carousel.body);
- this.onBeforeCardSwitch(null, null, this.carousel.items.indexOf(this.carousel.layout.getActiveItem()));
- }
- else {
- this.carousel.on('render', function() {
- this.render(this.carousel.body);
- }, this, {single: true});
- }
- Ext.Carousel.Indicator.superclass.initComponent.call(this);
- },
-
- // @private
- onRender: function() {
- Ext.Carousel.Indicator.superclass.onRender.apply(this, arguments);
-
- for (var i = 0, ln = this.carousel.items.length; i < ln; i++) {
- this.createIndicator();
- }
-
- this.mon(this.carousel, {
- beforecardswitch: this.onBeforeCardSwitch,
- scope: this
- });
-
- this.mon(this.el, {
- tap: this.onTap,
- scope: this
- });
-
- this.el.addCls(this.baseCls + '-' + this.direction);
- },
-
- // @private
- onTap: function(e, t) {
- var box = this.el.getPageBox(),
- centerX = box.left + (box.width / 2),
- centerY = box.top + (box.height / 2),
- carousel = this.carousel;
-
- if ((carousel.isHorizontal() && e.pageX > centerX) || (carousel.isVertical() && e.pageY > centerY)) {
- this.carousel.next();
- } else {
- this.carousel.prev();
- }
- },
-
- // @private
- createIndicator: function() {
- this.indicators = this.indicators || [];
- this.indicators.push(this.el.createChild({
- tag: 'span'
- }));
- },
-
- // @private
- onBeforeCardSwitch: function(carousel, card, old, index) {
- if (Ext.isNumber(index) && index != -1 && this.indicators[index]) {
- this.indicators[index].radioCls('x-carousel-indicator-active');
- }
- },
-
- // @private
- onCardAdd: function() {
- if (this.rendered) {
- this.createIndicator();
- }
- },
-
- // @private
- onCardRemove: function() {
- if (this.rendered) {
- this.indicators.pop().remove();
- }
- }
-});
-
-Ext.reg('carouselindicator', Ext.Carousel.Indicator);
-/**
- * @class Ext.Map
- * @extends Ext.Component
- *
- * <p>Wraps a Google Map in an Ext.Component.<br/>
- * http://code.google.com/apis/maps/documentation/v3/introduction.html</p>
- *
- * <p>To use this component you must include an additional JavaScript file from
- * Google:</p>
- * <pre><code><script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script></code></pre>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.Map/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <pre><code>
-var pnl = new Ext.Panel({
- fullscreen: true,
- items : [
- {
- xtype : 'map',
- useCurrentLocation: true
- }
- ]
-});</code></pre>
- * @xtype map
- */
-Ext.Map = Ext.extend(Ext.Component, {
- /**
- * @cfg {String} baseCls
- * The base CSS class to apply to the Maps's element (defaults to <code>'x-map'</code>).
- */
- baseCls: 'x-map',
-
- /**
- * @cfg {Boolean} useCurrentLocation
- * Pass in true to center the map based on the geolocation coordinates.
- */
- useCurrentLocation: false,
-
- monitorResize : true,
-
- /**
- * @cfg {Object} mapOptions
- * MapOptions as specified by the Google Documentation:
- * http://code.google.com/apis/maps/documentation/v3/reference.html
- */
-
- /**
- * @type {google.maps.Map}
- * The wrapped map.
- */
- map: null,
-
- /**
- * @type {Ext.util.GeoLocation}
- */
- geo: null,
-
- /**
- * @cfg {Boolean} maskMap
- * Masks the map (Defaults to false)
- */
- maskMap: false,
- /**
- * @cfg {Strng} maskMapCls
- * CSS class to add to the map when maskMap is set to true.
- */
- maskMapCls: 'x-mask-map',
-
-
- initComponent : function() {
- this.mapOptions = this.mapOptions || {};
-
- this.scroll = false;
-
-
- if(!(window.google || {}).maps){
- this.html = 'Google Maps API is required';
- }
- else if (this.useCurrentLocation) {
- this.geo = this.geo || new Ext.util.GeoLocation({autoLoad: false});
- this.geo.on({
- locationupdate : this.onGeoUpdate,
- locationerror : this.onGeoError,
- scope : this
- });
- }
-
- Ext.Map.superclass.initComponent.call(this);
-
- this.addEvents (
- /**
- * @event maprender
- * @param {Ext.Map} this
- * @param {google.maps.Map} map The rendered google.map.Map instance
- */
- 'maprender',
-
- /**
- * @event centerchange
- * @param {Ext.Map} this
- * @param {google.maps.Map} map The rendered google.map.Map instance
- * @param {google.maps.LatLong} center The current LatLng center of the map
- */
- 'centerchange',
-
- /**
- * @event typechange
- * @param {Ext.Map} this
- * @param {google.maps.Map} map The rendered google.map.Map instance
- * @param {Number} mapType The current display type of the map
- */
- 'typechange',
-
- /**
- * @event zoomchange
- * @param {Ext.Map} this
- * @param {google.maps.Map} map The rendered google.map.Map instance
- * @param {Number} zoomLevel The current zoom level of the map
- */
- 'zoomchange'
- );
-
- if (this.geo){
- this.on({
- activate: this.onUpdate,
- scope: this,
- single: true
- });
- this.geo.updateLocation();
- }
-
- },
-
- // @private
- onRender : function(container, position) {
- Ext.Map.superclass.onRender.apply(this, arguments);
- this.el.setDisplayMode(Ext.Element.OFFSETS);
- },
-
- // @private
- afterRender : function() {
- Ext.Map.superclass.afterRender.apply(this, arguments);
- this.renderMap();
- },
-
- // @private
- onResize : function( w, h) {
- Ext.Map.superclass.onResize.apply(this, arguments);
- if (this.map) {
- google.maps.event.trigger(this.map, 'resize');
- }
- },
-
- afterComponentLayout : function() {
- if (this.maskMap && !this.mask) {
- this.el.mask(null, this.maskMapCls);
- this.mask = true;
- }
- },
-
- renderMap : function(){
- var me = this,
- gm = (window.google || {}).maps;
-
- if (gm) {
- if (Ext.is.iPad) {
- Ext.applyIf(me.mapOptions, {
- navigationControlOptions: {
- style: gm.NavigationControlStyle.ZOOM_PAN
- }
- });
- }
-
- Ext.applyIf(me.mapOptions, {
- center: new gm.LatLng(37.381592, -122.135672), // Palo Alto
- zoom: 12,
- mapTypeId: gm.MapTypeId.ROADMAP
- });
-
- if (me.maskMap && !me.mask) {
- me.el.mask(null, this.maskMapCls);
- me.mask = true;
- }
-
- if (me.el && me.el.dom && me.el.dom.firstChild) {
- Ext.fly(me.el.dom.firstChild).remove();
- }
-
- if (me.map) {
- gm.event.clearInstanceListeners(me.map);
- }
-
- me.map = new gm.Map(me.el.dom, me.mapOptions);
-
- var event = gm.event;
- //Track zoomLevel and mapType changes
- event.addListener(me.map, 'zoom_changed', Ext.createDelegate(me.onZoom, me));
- event.addListener(me.map, 'maptypeid_changed', Ext.createDelegate(me.onTypeChange, me));
- event.addListener(me.map, 'center_changed', Ext.createDelegate(me.onCenterChange, me));
-
- me.fireEvent('maprender', me, me.map);
- }
-
- },
-
- onGeoUpdate : function(coords) {
- var center;
- if (coords) {
- center = this.mapOptions.center = new google.maps.LatLng(coords.latitude, coords.longitude);
- }
-
- if (this.rendered) {
- this.update(center);
- }
- else {
- this.on('activate', this.onUpdate, this, {single: true, data: center});
- }
- },
-
- onGeoError : function(geo){
-
- },
-
- onUpdate : function(map, e, options) {
- this.update((options || {}).data);
- },
-
-
- /**
- * Moves the map center to the designated coordinates hash of the form:
-<code><pre>
- { latitude : 37.381592,
- longitude : -122.135672
- }</pre></code>
- * or a google.maps.LatLng object representing to the target location.
- * @param {Object/google.maps.LatLng} coordinates Object representing the desired Latitude and
- * longitude upon which to center the map
- */
- update : function(coordinates) {
- var me = this,
- gm = (window.google || {}).maps;
-
- if (gm) {
- coordinates = coordinates || me.coords || new gm.LatLng(37.381592, -122.135672);
-
- if (coordinates && !(coordinates instanceof gm.LatLng) && 'longitude' in coordinates) {
- coordinates = new gm.LatLng(coordinates.latitude, coordinates.longitude);
- }
-
- if (!me.hidden && me.rendered) {
- me.map || me.renderMap();
- if (me.map && coordinates instanceof gm.LatLng) {
- me.map.panTo(coordinates);
- }
- }
- else {
- me.on('activate', me.onUpdate, me, {single: true, data: coordinates});
- }
- }
- },
-
- // @private
- onZoom : function() {
- this.mapOptions.zoom = (this.map && this.map.getZoom
- ? this.map.getZoom()
- : this.mapOptions.zoom) || 10 ;
-
- this.fireEvent('zoomchange', this, this.map, this.mapOptions.zoom);
- },
-
- // @private
- onTypeChange : function() {
- this.mapOptions.mapTypeId = this.map && this.map.getMapTypeId
- ? this.map.getMapTypeId()
- : this.mapOptions.mapTypeId;
-
- this.fireEvent('typechange', this, this.map, this.mapOptions.mapTypeId);
- },
-
- // @private
- onCenterChange : function(){
- this.mapOptions.center = this.map && this.map.getCenter
- ? this.map.getCenter()
- : this.mapOptions.center;
-
- this.fireEvent('centerchange', this, this.map, this.mapOptions.center);
-
- },
-
- getState : function(){
- return this.mapOptions;
- },
-
- // @private
- onDestroy : function() {
- Ext.destroy(this.geo);
- if (this.maskMap && this.mask) {
- this.el.unmask();
- }
- if (this.map && (window.google || {}).maps) {
- google.maps.event.clearInstanceListeners(this.map);
- }
- Ext.Map.superclass.onDestroy.call(this);
- }
-});
-
-Ext.reg('map', Ext.Map);
-/**
- * @class Ext.NestedList
- * @extends Ext.Panel
- *
- * <p>NestedList provides a miller column interface to navigate between nested sets
- * and provide a clean interface with limited screen real-estate.</p>
- *
- * <pre><code>
-// store with data
-var data = {
- text: 'Groceries',
- items: [{
- text: 'Drinks',
- items: [{
- text: 'Water',
- items: [{
- text: 'Sparkling',
- leaf: true
- },{
- text: 'Still',
- leaf: true
- }]
- },{
- text: 'Coffee',
- leaf: true
- },{
- text: 'Espresso',
- leaf: true
- },{
- text: 'Redbull',
- leaf: true
- },{
- text: 'Coke',
- leaf: true
- },{
- text: 'Diet Coke',
- leaf: true
- }]
- },{
- text: 'Fruit',
- items: [{
- text: 'Bananas',
- leaf: true
- },{
- text: 'Lemon',
- leaf: true
- }]
- },{
- text: 'Snacks',
- items: [{
- text: 'Nuts',
- leaf: true
- },{
- text: 'Pretzels',
- leaf: true
- },{
- text: 'Wasabi Peas',
- leaf: true
- }]
- },{
- text: 'Empty Category',
- items: []
- }]
-};
-Ext.regModel('ListItem', {
- fields: [{name: 'text', type: 'string'}]
-});
-var store = new Ext.data.TreeStore({
- model: 'ListItem',
- root: data,
- proxy: {
- type: 'ajax',
- reader: {
- type: 'tree',
- root: 'items'
- }
- }
-});
-var nestedList = new Ext.NestedList({
- fullscreen: true,
- title: 'Groceries',
- displayField: 'text',
- store: store
-});</code></pre>
- *
- * @xtype nestedlist
- */
-Ext.NestedList = Ext.extend(Ext.Panel, {
- componentCls: 'x-nested-list',
- /**
- * @cfg {String} layout
- * @hide
- */
- layout: 'card',
-
- /**
- * @cfg {String} tpl
- * @hide
- */
-
- /**
- * @cfg {String} defaultType
- * @hide
- */
-
- // Putting this in getSubList otherwise users would have to explicitly
- // specify the xtype to create in getDetailCard
- //defaultType: 'list',
-
- /**
- * @cfg {String} cardSwitchAnimation
- * Animation to be used during transitions of cards.
- * Any valid value from Ext.anims can be used ('fade', 'slide', 'flip', 'cube', 'pop', 'wipe').
- * Defaults to 'slide'.
- */
- cardSwitchAnimation: 'slide',
-
- /**
- * @type Ext.Button
- */
- backButton: null,
-
- /**
- * @cfg {String} backText
- * The label to display for the back button. Defaults to "Back".
- */
- backText: 'Back',
-
- /**
- * @cfg {Boolean} useTitleAsBackText
- */
- useTitleAsBackText: true,
-
- /**
- * @cfg {Boolean} updateTitleText
- * Update the title with the currently selected category. Defaults to true.
- */
- updateTitleText: true,
-
- /**
- * @cfg {String} displayField
- * Display field to use when setting item text and title.
- * This configuration is ignored when overriding getItemTextTpl or
- * getTitleTextTpl for the item text or title. (Defaults to 'text')
- */
- displayField: 'text',
-
- /**
- * @cfg {String} loadingText
- * Loading text to display when a subtree is loading.
- */
- loadingText: 'Loading...',
-
- /**
- * @cfg {String} emptyText
- * Empty text to display when a subtree is empty.
- */
- emptyText: 'No items available.',
-
- /**
- * @cfg {Boolean/Function} onItemDisclosure
- * Maps to the Ext.List onItemDisclosure configuration for individual lists. (Defaults to false)
- */
- onItemDisclosure: false,
-
- /**
- * @cfg {Boolean/Number} clearSelectionDelay
- * Number of milliseconds to show the highlight when going back in a list. (Defaults to 200).
- * Passing false will keep the prior list selection.
- */
- clearSelectionDelay: 200,
-
-
- /**
- * @cfg {Boolean} allowDeselect
- * Set to true to alow the user to deselect leaf items via interaction.
- * Defaults to false.
- */
- allowDeselect: false,
-
- /**
- * Override this method to provide custom template rendering of individual
- * nodes. The template will receive all data within the Record and will also
- * receive whether or not it is a leaf node.
- * @param {Ext.data.Record} node
- */
- getItemTextTpl: function(node) {
- return '{' + this.displayField + '}';
- },
-
- /**
- * Override this method to provide custom template rendering of titles/back
- * buttons when useTitleAsBackText is enabled.
- * @param {Ext.data.Record} node
- */
- getTitleTextTpl: function(node) {
- return '{' + this.displayField + '}';
- },
-
- // private
- renderTitleText: function(node) {
- // caching this on the record/node
- // could store in an internal cache via id
- // so that we could clean it up
- if (!node.titleTpl) {
- node.titleTpl = new Ext.XTemplate(this.getTitleTextTpl(node));
- }
- var record = node.getRecord();
- if (record) {
- return node.titleTpl.applyTemplate(record.data);
- } else if (node.isRoot) {
- return this.title || this.backText;
- // <debug>
- } else {
- throw new Error("No RecordNode passed into renderTitleText");
- }
- // </debug>
- },
-
- /**
- * @property toolbar
- * Ext.Toolbar shared across each of the lists.
- * This will only exist when useToolbar is true which
- * is the default.
- */
- useToolbar: true,
-
- /**
- * @property store
- * Ext.data.TreeStore
- */
-
- /**
- * @cfg {Ext.data.TreeStore} store
- * The {@link Ext.data.TreeStore} to bind this NestedList to.
- */
-
- /**
- * Implement getDetailCard to provide a final card for leaf nodes when useDetailCard
- * is enabled. getDetailCard will be passed the currentRecord and the parentRecord.
- * The default implementation will return false
- * @param {Ext.data.Record} record
- * @param {Ext.data.Record} parentRecord
- */
- getDetailCard: function(recordNode, parentNode) {
- return false;
- },
-
- initComponent : function() {
-
- var store = Ext.StoreMgr.lookup(this.store),
- rootNode = store.getRootNode(),
- title = rootNode.getRecord() ? this.renderTitleText(rootNode) : this.title || '';
-
- this.store = store;
-
- if (this.useToolbar) {
- // Add the back button
- this.backButton = new Ext.Button({
- text: this.backText,
- ui: 'back',
- handler: this.onBackTap,
- scope: this,
- // First stack doesn't show back
- hidden: true
- });
- if (!this.toolbar || !this.toolbar.isComponent) {
- /**
- * @cfg {Object} toolbar
- * Configuration for the Ext.Toolbar that is created within the Ext.NestedList.
- */
- this.toolbar = Ext.apply({}, this.toolbar || {}, {
- dock: 'top',
- xtype: 'toolbar',
- ui: 'light',
- title: title,
- items: []
- });
- this.toolbar.items.unshift(this.backButton);
- this.toolbar = new Ext.Toolbar(this.toolbar);
-
- this.dockedItems = this.dockedItems || [];
- this.dockedItems.push(this.toolbar);
- } else {
- this.toolbar.insert(0, this.backButton);
- }
- }
-
- this.items = [this.getSubList(rootNode)];
-
- Ext.NestedList.superclass.initComponent.call(this);
- this.on('itemtap', this.onItemTap, this);
-
-
- this.addEvents(
- /**
- * @event itemtap
- * Fires when a node is tapped on
- * @param {Ext.List} list The Ext.List that is currently active
- * @param {Number} index The index of the item that was tapped
- * @param {Ext.Element} item The item element
- * @param {Ext.EventObject} e The event object
- */
-
- /**
- * @event itemdoubletap
- * Fires when a node is double tapped on
- * @param {Ext.List} list The Ext.List that is currently active
- * @param {Number} index The index of the item that was tapped
- * @param {Ext.Element} item The item element
- * @param {Ext.EventObject} e The event object
- */
-
- /**
- * @event containertap
- * Fires when a tap occurs and it is not on a template node.
- * @param {Ext.List} list The Ext.List that is currently active
- * @param {Ext.EventObject} e The raw event object
- */
-
- /**
- * @event selectionchange
- * Fires when the selected nodes change.
- * @param {Ext.List} list The Ext.List that is currently active
- * @param {Array} selections Array of the selected nodes
- */
-
- /**
- * @event beforeselect
- * Fires before a selection is made. If any handlers return false, the selection is cancelled.
- * @param {Ext.List} list The Ext.List that is currently active
- * @param {HTMLElement} node The node to be selected
- * @param {Array} selections Array of currently selected nodes
- */
-
- // new events.
- /**
- * @event listchange
- * Fires when the user taps a list item
- * @param {Ext.NestedList} this
- * @param {Object} listitem
- */
- 'listchange',
-
- /**
- * @event leafitemtap
- * Fires when the user taps a leaf list item
- * @param {Ext.List} subList The subList the item is on
- * @param {Number} subIdx The id of the item tapped
- * @param {Ext.Element} el The element of the item tapped
- * @param {Ext.EventObject} e The event
- * @param {Ext.Panel} card The next card to be shown
- */
- 'leafitemtap'
- );
- },
-
- /**
- * @private
- * Returns the list config for a specified node.
- * @param {HTMLElement} node The node for the list config
- */
- getListConfig: function(node) {
- var itemId = node.internalId,
- emptyText = this.emptyText;
-
- return {
- itemId: itemId,
- xtype: 'list',
- autoDestroy: true,
- recordNode: node,
- store: this.store.getSubStore(node),
- loadingText: this.loadingText,
- onItemDisclosure: this.onItemDisclosure,
- displayField: this.displayField,
- singleSelect: true,
- clearSelectionOnDeactivate: false,
- bubbleEvents: [
- 'itemtap',
- 'containertap',
- 'beforeselect',
- 'itemdoubletap',
- 'selectionchange'
- ],
- itemTpl: '<span<tpl if="leaf == true"> class="x-list-item-leaf"</tpl>>' + this.getItemTextTpl(node) + '</span>',
- deferEmptyText: true,
- allowDeselect: this.allowDeselect,
- refresh: function() {
- if (this.hasSkippedEmptyText) {
- this.emptyText = emptyText;
- }
- Ext.List.prototype.refresh.apply(this, arguments);
- }
- };
- },
-
- /**
- * Returns the subList for a specified node
- * @param {HTMLElement} node The node for the subList
- */
- getSubList: function(node) {
- var items = this.items,
- list,
- itemId = node.internalId;
-
- // can be invoked prior to items being transformed into
- // a MixedCollection
- if (items && items.get) {
- list = items.get(itemId);
- }
-
- if (list) {
- return list;
- } else {
- return this.getListConfig(node);
- }
- },
-
- addNextCard: function(recordNode, swapTo) {
- var nextList,
- parentNode = recordNode ? recordNode.parentNode : null,
- card;
-
- if (recordNode.leaf) {
- card = this.getDetailCard(recordNode, parentNode);
- if (card) {
- nextList = this.add(card);
- }
- } else {
- nextList = this.getSubList(recordNode);
- nextList = this.add(nextList);
- }
- return nextList;
- },
-
- setActivePath: function(path) {
- // a forward leading slash indicates to go
- // to root, otherwise its relative to current
- // position
- var gotoRoot = path.substr(0, 1) === "/",
- j = 0,
- ds = this.store,
- tree = ds.tree,
- node, card, lastCard,
- pathArr, pathLn;
-
- if (gotoRoot) {
- path = path.substr(1);
- }
-
- pathArr = Ext.toArray(path.split('/'));
- pathLn = pathArr.length;
-
-
- if (gotoRoot) {
- // clear all but first item
- var items = this.items,
- itemsArray = this.items.items,
- i = items.length;
-
- for (; i > 1; i--) {
- this.remove(itemsArray[i - 1], true);
- }
-
- // verify last item left matches first item in pathArr
- // <debug>
- var rootNode = itemsArray[0].recordNode;
- if (rootNode.id !== pathArr[0]) {
- throw new Error("rootNode doesn't match!");
- }
- // </debug>
-
- // skip the 0 item rather than remove/add
- j = 1;
- }
-
-
- // loop through the path and add cards
- for (; j < pathLn; j++) {
- if (pathArr[j] !== "") {
- node = tree.getNodeById(pathArr[j]);
-
- // currently adding cards and not verifying
- // that they are true child nodes of the current parent
- // this would be some good debug tags.
- card = this.addNextCard(node);
-
- // leaf nodes may or may not have a card
- // therefore we need a temp var (lastCard)
- if (card) {
- lastCard = card;
- }
- }
- }
-
- // <debug>
- if (!lastCard) {
- throw new Error("Card was not found when trying to add to NestedList.");
- }
- // </debug>
-
- this.setActiveItem(lastCard, false);
- this.fireEvent('listchange', this, lastCard);
- this.syncToolbar();
- },
-
- syncToolbar: function(card) {
- var list = card || this.getActiveItem(),
- depth = this.items.indexOf(list),
- recordNode = list.recordNode,
- parentNode = recordNode ? recordNode.parentNode : null,
- backBtn = this.backButton,
- backBtnText = this.useTitleAsBackText && parentNode ? this.renderTitleText(parentNode) : this.backText,
- backToggleMth = (depth !== 0) ? 'show' : 'hide';
-
-
- if (backBtn) {
- backBtn[backToggleMth]();
- if (parentNode) {
- backBtn.setText(backBtnText);
- }
- }
-
-
- if (this.toolbar && this.updateTitleText) {
- this.toolbar.setTitle(recordNode && recordNode.getRecord() ? this.renderTitleText(recordNode) : this.title || '');
- this.toolbar.doLayout();
- }
- },
-
- /**
- * Called when an list item has been tapped
- * @param {Ext.List} subList The subList the item is on
- * @param {Number} subIdx The id of the item tapped
- * @param {Ext.Element} el The element of the item tapped
- * @param {Ext.EventObject} e The event
- */
- onItemTap: function(subList, subIdx, el, e) {
- var store = subList.getStore(),
- record = store.getAt(subIdx),
- recordNode = record.node,
- parentNode = recordNode ? recordNode.parentNode : null,
- displayField = this.displayField,
- backToggleMth,
- nextDepth,
- nextList;
-
- nextList = this.addNextCard(recordNode);
-
- if (recordNode.leaf) {
- this.fireEvent("leafitemtap", subList, subIdx, el, e, nextList);
- }
-
- if (nextList) {
- // depth should be based off record
- // and TreeStore rather than items.
- nextDepth = this.items.indexOf(nextList);
-
- this.setActiveItem(nextList, {
- type: this.cardSwitchAnimation
- });
- this.syncToolbar(nextList);
- }
- },
-
- /**
- * Called when the {@link #backButton} has been tapped
- */
- onBackTap: function() {
- var currList = this.getActiveItem(),
- currIdx = this.items.indexOf(currList);
-
- if (currIdx != 0) {
- var prevDepth = currIdx - 1,
- prevList = this.items.getAt(prevDepth),
- recordNode = prevList.recordNode,
- record = recordNode.getRecord(),
- parentNode = recordNode ? recordNode.parentNode : null,
- backBtn = this.backButton,
- backToggleMth = (prevDepth !== 0) ? 'show' : 'hide',
- backBtnText;
-
- this.on('cardswitch', function(newCard, oldCard) {
- var selModel = prevList.getSelectionModel();
- this.remove(currList);
- if (this.clearSelectionDelay) {
- Ext.defer(selModel.deselectAll, this.clearSelectionDelay, selModel);
- }
- }, this, {single: true});
-
- this.setActiveItem(prevList, {
- type: this.cardSwitchAnimation,
- reverse: true,
- scope: this
- });
- this.syncToolbar(prevList);
- }
- }
-});
-Ext.reg('nestedlist', Ext.NestedList);
-/**
- * @class Ext.Picker
- * @extends Ext.Sheet
- *
- * <p>A general picker class. Slots are used to organize multiple scrollable slots into a single picker. {@link #slots} is
- * the only necessary property</p>
- *
- * <h2>Example usage:</h2>
- * <pre><code>
-var picker = new Ext.Picker({
- slots: [
- {
- name : 'limit_speed',
- title: 'Speed',
- data : [
- {text: '50 KB/s', value: 50},
- {text: '100 KB/s', value: 100},
- {text: '200 KB/s', value: 200},
- {text: '300 KB/s', value: 300}
- ]
- }
- ]
-});
-picker.show();
- * </code></pre>
- *
- * @constructor
- * Create a new List
- * @param {Object} config The config object
- * @xtype picker
- */
-Ext.Picker = Ext.extend(Ext.Sheet, {
- /**
- * @cfg {String} componentCls
- * The main component class
- */
- componentCls: 'x-picker',
-
- stretchX: true,
- stretchY: true,
- hideOnMaskTap: false,
-
- /**
- * @cfg {String/Mixed} doneButton
- * Can be either:<ul>
- * <li>A {String} text to be used on the Done button</li>
- * <li>An {Object} as config for {@link Ext.Button}</li>
- * <li>false or null to hide it</li></ul>
- *
- * Defaults to 'Done'.
- */
- doneButton: 'Done',
-
- /**
- * @cfg {String/Mixed} doneButton
- * Can be either:<ul>
- * <li>A {String} text to be used on the Done button</li>
- * <li>An {Object} as config for {@link Ext.Button}</li>
- * <li>false or null to hide it</li></ul>
- *
- * Defaults to 'Done'.
- */
- cancelButton: 'Cancel',
-
- /**
- * @cfg {Number} height
- * The height of the picker.
- * Defaults to 220
- */
- height: 220,
-
- /**
- * @cfg {Boolean} useTitles
- * Generate a title header for each individual slot and use
- * the title configuration of the slot.
- * Defaults to false.
- */
- useTitles: false,
-
- /**
- * @cfg {String} activeCls
- * CSS class to be applied to individual list items when they have
- * been chosen.
- */
- // activeCls: 'x-picker-active-item',
-
- /**
- * @cfg {Array} slots
- * An array of slot configurations.
- * <ul>
- * <li>name - {String} - Name of the slot</li>
- * <li>align - {String} - Alignment of the slot. left, right, or center</li>
- * <li>items - {Array} - An array of text/value pairs in the format {text: 'myKey', value: 'myValue'}</li>
- * <li>title - {String} - Title of the slot. This is used in conjunction with useTitles: true.</li>
- * </ul>
- */
- //
- // chosenCls: 'x-picker-chosen-item',
-
- // private
- defaultType: 'pickerslot',
-
- // private
- initComponent : function() {
-
- this.addEvents(
- /**
- * @event pick
- * Fired when a slot has been picked
- * @param {Ext.Picker} this This Picker
- * @param {Object} The values of this picker's slots, in {name:'value'} format
- * @param {Ext.Picker.Slot} slot An instance of Ext.Picker.Slot that has been picked
- */
- 'pick',
-
- /**
- * @event change
- * Fired when the picked value has changed
- * @param {Ext.Picker} this This Picker
- * @param {Object} The values of this picker's slots, in {name:'value'} format
- */
- 'change',
-
- /**
- * @event cancel
- * Fired when the cancel button is tapped and the values are reverted back to
- * what they were
- * @param {Ext.Picker} this This Picker
- */
- 'cancel'
- );
-
- this.layout = {
- type: 'hbox',
- align: 'stretch'
- };
-
- if (this.slots) {
- this.items = this.items ? (Ext.isArray(this.items) ? this.items : [this.items]) : [];
- this.items = this.items.concat(this.slots);
- }
-
- if (this.useTitles) {
- this.defaults = Ext.applyIf(this.defaults || {}, {
- title: ''
- });
- }
-
- this.on('slotpick', this.onSlotPick, this);
-
- if (this.doneButton || this.cancelButton) {
- var toolbarItems = [];
-
- if (this.cancelButton) {
- toolbarItems.push(
- Ext.apply(
- {
- ui: 'decline',
- handler: this.onCancelButtonTap,
- scope: this
- },
- ((Ext.isObject(this.cancelButton) ? this.cancelButton : { text: String(this.cancelButton) }))
- )
- );
- }
-
- toolbarItems.push({xtype: 'spacer'});
-
- if (this.doneButton) {
- toolbarItems.push(
- Ext.apply(
- {
- ui: 'action',
- handler: this.onDoneButtonTap,
- scope: this
- },
- ((Ext.isObject(this.doneButton) ? this.doneButton : { text: String(this.doneButton) }))
- )
- );
- }
-
- this.toolbar = new Ext.Toolbar(Ext.applyIf(this.buttonBar || {
- dock: 'top',
- items: toolbarItems,
- defaults: {
- xtype: 'button'
- }
- }));
-
- this.dockedItems = this.dockedItems ? (Ext.isArray(this.dockedItems) ? this.dockedItems : [this.dockedItems]) : [];
- this.dockedItems.push(this.toolbar);
- }
-
- Ext.Picker.superclass.initComponent.call(this);
- },
-
- // @private
- afterRender: function() {
- Ext.Picker.superclass.afterRender.apply(this, arguments);
-
- if (this.value) {
- this.setValue(this.value, false);
- }
- },
-
- /**
- * @private
- * Called when the done button has been tapped.
- */
- onDoneButtonTap : function() {
- var anim = this.animSheet('exit');
- Ext.apply(anim, {
- after: function() {
- this.fireEvent('change', this, this.getValue());
- },
- scope: this
- });
- this.hide(anim);
- },
-
- /**
- * @private
- * Called when the cancel button has been tapped.
- */
- onCancelButtonTap : function() {
- var anim = this.animSheet('exit');
- Ext.apply(anim, {
- after: function() {
- // Set the value back to what it was previously
- this.setValue(this.values);
- this.fireEvent('cancel', this);
- },
- scope: this
- });
- this.hide(anim);
- },
-
- /**
- * @private
- * Called when a slot has been picked.
- */
- onSlotPick: function(slot, value, node) {
- this.fireEvent('pick', this, this.getValue(), slot);
- return false;
- },
-
- /**
- * Sets the values of the pickers slots
- * @param {Object} values The values in a {name:'value'} format
- * @param {Boolean} animated True to animate setting the values
- * @return {Ext.Picker} this This picker
- */
- setValue: function(values, animated) {
- var slot,
- items = this.items.items,
- ln = items.length;
-
- // Value is an object with keys mapping to slot names
- if (!values) {
- for (var i = 0; i < ln; i++) {
- items[i].setSelectedNode(0);
- }
-
- return this;
- }
-
- Ext.iterate(values, function(key, value) {
- slot = this.child('[name=' + key + ']');
-
- if (slot) {
- slot.setValue(value, animated);
- }
- }, this);
-
- this.values = values;
-
- return this;
- },
-
- /**
- * Returns the values of each of the pickers slots
- * @return {Object} The values of the pickers slots
- */
- getValue: function() {
- var values = {},
- items = this.items.items,
- ln = items.length, item, i;
-
- for (i = 0; i < ln; i++) {
- item = items[i];
- values[item.name] = item.getValue();
- }
-
- return values;
- }
-});
-
-Ext.regModel('x-textvalue', {
- fields: ['text', 'value']
-});
-
-/**
- * @private
- * @class Ext.Picker.Slot
- * @extends Ext.DataView
- *
- * <p>A general picker slot class. Slots are used to organize multiple scrollable slots into a single picker
- * See also: {@link Ext.Picker}</p>
- *
- * @constructor
- * Create a new Picker Slot
- * @param {Object} config The config object
- * @xtype pickerslot
- */
-Ext.Picker.Slot = Ext.extend(Ext.DataView, {
- isSlot: true,
-
- flex: 1,
-
- /**
- * @cfg {String} name
- * The name of this slot. This config option is required.
- */
- name: null,
-
- /**
- * @cfg {String} displayField
- * The display field in the store.
- * Defaults to 'text'.
- */
- displayField: 'text',
-
- /**
- * @cfg {String} valueField
- * The value field in the store.
- * Defaults to 'value'.
- */
- valueField: 'value',
-
- /**
- * @cfg {String} align
- * The alignment of this slot.
- * Defaults to 'center'
- */
- align: 'center',
-
- /**
- * @hide
- * @cfg {String} itemSelector
- */
- itemSelector: 'div.x-picker-item',
-
- /**
- * @private
- * @cfg {String} componentCls
- * The main component class
- */
- componentCls: 'x-picker-slot',
-
- /**
- * @private
- * @cfg {Ext.Template/Ext.XTemplate/Array} renderTpl
- * The renderTpl of the slot.
- */
- renderTpl : [
- '<div class="x-picker-mask">',
- '<div class="x-picker-bar"></div>',
- '</div>'
- ],
-
- /**
- * @private
- * The current selectedIndex of the picker slot
- */
- selectedIndex: 0,
-
- /**
- * @private
- */
- getElConfig: function() {
- return {
- tag: 'div',
- id: this.id,
- cls: 'x-picker-' + this.align
- };
- },
-
- /**
- * @private
- */
- initComponent : function() {
- // <debug>
- if (!this.name) {
- throw new Error('Each picker slot is required to have a name.');
- }
- // </debug>
-
- Ext.apply(this.renderSelectors, {
- mask: '.x-picker-mask',
- bar: '.x-picker-bar'
- });
-
- this.scroll = {
- direction: 'vertical',
- useIndicators: false,
- friction: 0.7,
- acceleration: 25,
- snapDuration: 200,
- animationDuration: 200
- };
-
- this.tpl = new Ext.XTemplate([
- '<tpl for=".">',
- '<div class="x-picker-item {cls} <tpl if="extra">x-picker-invalid</tpl>">{' + this.displayField + '}</div>',
- '</tpl>'
- ]);
-
- var data = this.data,
- parsedData = [],
- ln = data && data.length,
- i, item, obj;
-
- if (data && Ext.isArray(data) && ln) {
- for (i = 0; i < ln; i++) {
- item = data[i];
- obj = {};
- if (Ext.isArray(item)) {
- obj[this.valueField] = item[0];
- obj[this.displayField] = item[1];
- }
- else if (Ext.isString(item)) {
- obj[this.valueField] = item;
- obj[this.displayField] = item;
- }
- else if (Ext.isObject(item)) {
- obj = item;
- }
- parsedData.push(obj);
- }
-
- this.store = new Ext.data.Store({
- model: 'x-textvalue',
- data: parsedData
- });
-
- this.tempStore = true;
- }
- else if (this.store) {
- this.store = Ext.StoreMgr.lookup(this.store);
- }
-
- this.enableBubble('slotpick');
-
- if (this.title) {
- this.title = new Ext.Component({
- dock: 'top',
- componentCls: 'x-picker-slot-title',
- html: this.title
- });
- this.dockedItems = this.title;
- }
-
- Ext.Picker.Slot.superclass.initComponent.call(this);
-
- if (this.value !== undefined) {
- this.setValue(this.value, false);
- }
- },
-
- /**
- * @private
- */
- setupBar: function() {
- this.el.setStyle({padding: ''});
-
- var padding = this.bar.getY() - this.el.getY();
- this.barHeight = this.bar.getHeight();
-
- this.el.setStyle({
- padding: padding + 'px 0'
- });
- this.slotPadding = padding;
- this.scroller.updateBoundary();
- this.scroller.setSnap(this.barHeight);
- this.setSelectedNode(this.selectedIndex, false);
- },
-
- /**
- * @private
- */
- afterComponentLayout: function() {
- // Dont call superclass afterComponentLayout since we dont want
- // the scroller to get a min-height
- Ext.defer(this.setupBar, 200, this);
- },
-
- /**
- * @private
- */
- initEvents: function() {
- this.mon(this.scroller, {
- scrollend: this.onScrollEnd,
- scope: this
- });
- },
-
- /**
- * @private
- */
- onScrollEnd: function(scroller, offset) {
- this.selectedNode = this.getNode(Math.round(offset.y / this.barHeight));
- this.selectedIndex = this.indexOf(this.selectedNode);
- this.fireEvent('slotpick', this, this.getValue(), this.selectedNode);
- },
-
- /**
- * @private
- */
- scrollToNode: function(node, animate) {
- var offsetsToBody = Ext.fly(node).getOffsetsTo(this.scrollEl)[1];
- this.scroller.scrollTo({
- y: offsetsToBody
- }, animate !== false ? true : false);
- },
-
- /**
- * @private
- * Called when an item has been tapped
- */
- onItemTap: function(node) {
- Ext.Picker.Slot.superclass.onItemTap.apply(this, arguments);
- this.setSelectedNode(node);
- },
-
- /**
- *
- */
- getSelectedNode: function() {
- return this.selectedNode;
- },
-
- /**
- *
- */
- setSelectedNode: function(selected, animate) {
- // If it is a number, we assume we are dealing with an index
- if (Ext.isNumber(selected)) {
- selected = this.getNode(selected);
- }
- else if (selected.isModel) {
- selected = this.getNode(this.store.indexOf(selected));
- }
-
- // If its not a model or a number, we assume its a node
- if (selected) {
- this.selectedNode = selected;
- this.selectedIndex = this.indexOf(selected);
- this.scrollToNode(selected, animate);
- }
- },
-
- /**
- *
- */
- getValue: function() {
- var record = this.store.getAt(this.selectedIndex);
- return record ? record.get(this.valueField) : null;
- },
-
- /**
- *
- */
- setValue: function(value, animate) {
- var index = this.store.find(this.valueField, value);
- if (index != -1) {
- if (!this.rendered) {
- this.selectedIndex = index;
- return;
- }
- this.setSelectedNode(index, animate);
- }
- },
-
- onDestroy: function() {
- if (this.tempStore) {
- this.store.destroyStore();
- this.store = null;
- }
- Ext.Picker.Slot.superclass.onDestroy.call(this);
- }
-});
-
-Ext.reg('pickerslot', Ext.Picker.Slot);
-
-/**
- * @class Ext.DatePicker
- * @extends Ext.Picker
- *
- * <p>A date picker component which shows a DatePicker on the screen. This class extends from {@link Ext.Picker} and {@link Ext.Sheet} so it is a popup.</p>
- * <p>This component has no required properties.</p>
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #yearFrom}</li>
- * <li>{@link #yearTo}</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.DatePicker/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- *
- * <pre><code>
-var datePicker = new Ext.DatePicker();
-datePicker.show();
- * </code></pre>
- *
- * <p>you may want to adjust the {@link #yearFrom} and {@link #yearTo} properties:
- * <pre><code>
-var datePicker = new Ext.DatePicker({
- yearFrom: 2000,
- yearTo : 2015
-});
-datePicker.show();
- * </code></pre>
- *
- * @constructor
- * Create a new List
- * @param {Object} config The config object
- * @xtype datepicker
- */
-Ext.DatePicker = Ext.extend(Ext.Picker, {
- /**
- * @cfg {Number} yearFrom
- * The start year for the date picker. Defaults to 1980
- */
- yearFrom: 1980,
-
- /**
- * @cfg {Number} yearTo
- * The last year for the date picker. Defaults to the current year.
- */
- yearTo: new Date().getFullYear(),
-
- /**
- * @cfg {String} monthText
- * The label to show for the month column. Defaults to 'Month'.
- */
- monthText: 'Month',
-
- /**
- * @cfg {String} dayText
- * The label to show for the day column. Defaults to 'Day'.
- */
- dayText: 'Day',
-
- /**
- * @cfg {String} yearText
- * The label to show for the year column. Defaults to 'Year'.
- */
- yearText: 'Year',
-
- /**
- * @cfg {Object/Date} value
- * Default value for the field and the internal {@link Ext.DatePicker} component. Accepts an object of 'year',
- * 'month' and 'day' values, all of which should be numbers, or a {@link Date}.
- *
- * Examples:
- * {year: 1989, day: 1, month: 5} = 1st May 1989.
- * new Date() = current date
- */
-
- /**
- * @cfg {Array} slotOrder
- * An array of strings that specifies the order of the slots. Defaults to <tt>['month', 'day', 'year']</tt>.
- */
- slotOrder: ['month', 'day', 'year'],
-
- initComponent: function() {
- var yearsFrom = this.yearFrom,
- yearsTo = this.yearTo,
- years = [],
- days = [],
- months = [],
- ln, tmp, i,
- daysInMonth;
-
- // swap values if user mixes them up.
- if (yearsFrom > yearsTo) {
- tmp = yearsFrom;
- yearsFrom = yearsTo;
- yearsTo = tmp;
- }
-
- for (i = yearsFrom; i <= yearsTo; i++) {
- years.push({
- text: i,
- value: i
- });
- }
-
- daysInMonth = this.getDaysInMonth(1, new Date().getFullYear());
-
- for (i = 0; i < daysInMonth; i++) {
- days.push({
- text: i + 1,
- value: i + 1
- });
- }
-
- for (i = 0, ln = Date.monthNames.length; i < ln; i++) {
- months.push({
- text: Date.monthNames[i],
- value: i + 1
- });
- }
-
- this.slots = [];
-
- this.slotOrder.forEach(function(item){
- this.slots.push(this.createSlot(item, days, months, years));
- }, this);
-
- Ext.DatePicker.superclass.initComponent.call(this);
- },
-
- afterRender: function() {
- Ext.DatePicker.superclass.afterRender.apply(this, arguments);
-
- this.setValue(this.value);
- },
-
- createSlot: function(name, days, months, years){
- switch (name) {
- case 'year':
- return {
- name: 'year',
- align: 'center',
- data: years,
- title: this.useTitles ? this.yearText : false,
- flex: 3
- };
- case 'month':
- return {
- name: name,
- align: 'right',
- data: months,
- title: this.useTitles ? this.monthText : false,
- flex: 4
- };
- case 'day':
- return {
- name: 'day',
- align: 'center',
- data: days,
- title: this.useTitles ? this.dayText : false,
- flex: 2
- };
- }
- },
-
- // @private
- onSlotPick: function(slot, value) {
- var name = slot.name,
- date, daysInMonth, daySlot;
-
- if (name === "month" || name === "year") {
- daySlot = this.child('[name=day]');
- date = this.getValue();
- daysInMonth = this.getDaysInMonth(date.getMonth()+1, date.getFullYear());
- daySlot.store.clearFilter();
- daySlot.store.filter({
- fn: function(r) {
- return r.get('extra') === true || r.get('value') <= daysInMonth;
- }
- });
- daySlot.scroller.updateBoundary(true);
- }
-
- Ext.DatePicker.superclass.onSlotPick.apply(this, arguments);
- },
-
- /**
- * Gets the current value as a Date object
- * @return {Date} value
- */
- getValue: function() {
- var value = Ext.DatePicker.superclass.getValue.call(this),
- daysInMonth = this.getDaysInMonth(value.month, value.year),
- day = Math.min(value.day, daysInMonth);
-
- return new Date(value.year, value.month-1, day);
- },
-
- /**
- * Sets the values of the DatePicker's slots
- * @param {Date/Object} value The value either in a {day:'value', month:'value', year:'value'} format or a Date
- * @param {Boolean} animated True for animation while setting the values
- * @return {Ext.DatePicker} this This DatePicker
- */
- setValue: function(value, animated) {
- if (!Ext.isDate(value) && !Ext.isObject(value)) {
- value = null;
- }
-
- if (Ext.isDate(value)) {
- this.value = {
- day : value.getDate(),
- year: value.getFullYear(),
- month: value.getMonth() + 1
- };
- } else {
- this.value = value;
- }
-
- return Ext.DatePicker.superclass.setValue.call(this, this.value, animated);
- },
-
- // @private
- getDaysInMonth: function(month, year) {
- var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
- return month == 2 && this.isLeapYear(year) ? 29 : daysInMonth[month-1];
- },
-
- // @private
- isLeapYear: function(year) {
- return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
- }
-});
-
-Ext.reg('datepicker', Ext.DatePicker);
-/**
- * @class Ext.Media
- * @extends Ext.Container
- *
- * <p>Provides a base class for audio/visual controls. Should not be used directly.</p>
- * @xtype media
- */
-Ext.Media = Ext.extend(Ext.Component, {
- /**
- * @constructor
- * @param {Object} config
- * Create a new Media component.
- */
-
- /**
- * @cfg {String} url
- * Location of the media to play.
- */
- url: '',
-
- /**
- * @cfg {Boolean} enableControls
- * Set this to false to turn off the native media controls
- * (Defaults to true).
- */
- enableControls: true,
-
- /**
- * @cfg {Boolean} autoResume
- * Will automatically start playing the media when the container is activated.
- * (Defaults to false)
- */
- autoResume: false,
-
- /**
- * @cfg {Boolean} autoPause
- * Will automatically pause the media when the container is deactivated.
- * (Defaults to true)
- */
- autoPause: true,
-
- /**
- * @cfg {Boolean} preload
- * Will begin preloading the media immediately.
- * (Defaults to true)
- */
- preload: true,
-
- // @private
- playing: false,
-
- // @private
- afterRender : function() {
- var cfg = this.getConfiguration();
- Ext.apply(cfg, {
- src: this.url,
- preload: this.preload ? 'auto' : 'none'
- });
- if(this.enableControls){
- cfg.controls = 'controls';
- }
- if(this.loop){
- cfg.loop = 'loop';
- }
- /**
- * A reference to the underlying audio/video element.
- * @property media
- * @type Ext.Element
- */
- this.media = this.el.createChild(cfg);
- Ext.Media.superclass.afterRender.call(this);
-
- this.on({
- scope: this,
- activate: this.onActivate,
- beforedeactivate: this.onDeactivate
- });
- },
-
- // @private
- onActivate: function(){
- if (this.autoResume && !this.playing) {
- this.play();
- }
- },
-
- // @private
- onDeactivate: function(){
- if (this.autoPause && this.playing) {
- this.pause();
- }
- },
-
- /**
- * Starts or resumes media playback
- */
- play : function() {
- this.media.dom.play();
- this.playing = true;
- },
-
- /**
- * Pauses media playback
- */
- pause : function() {
- this.media.dom.pause();
- this.playing = false;
- },
-
- /**
- * Toggles the media playback state
- */
- toggle : function() {
- if(this.playing){
- this.pause();
- }
- else {
- this.play();
- }
- }
-});
-
-Ext.reg('media', Ext.Media);
-/**
- * @class Ext.Video
- * @extends Ext.Media
- *
- * <p>Provides a simple Container for HTML5 Video.</p>
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #url}</li>
- * <li>{@link #autoPause}</li>
- * <li>{@link #autoResume}</li>
- * </ul>
- *
- * <h2>Useful Methods</h2>
- * <ul class="list">
- * <li>{@link #pause}</li>
- * <li>{@link #play}</li>
- * <li>{@link #toggle}</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.Video/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <pre><code>
-var pnl = new Ext.Panel({
- fullscreen: true,
- items: [
- {
- xtype : 'video',
- x : 600,
- y : 300,
- width : 175,
- height : 98,
- url : "porsche911.mov",
- posterUrl: 'porsche.png'
- }
- ]
-});</code></pre>
- * @xtype video
- */
-Ext.Video = Ext.extend(Ext.Media, {
- /**
- * @constructor
- * @param {Object} config
- * Create a new Video Panel.
- */
-
- /**
- * @cfg {String} url
- * Location of the video to play. This should be in H.264 format and in a
- * .mov file format.
- */
-
- /**
- * @cfg {String} posterUrl
- * Location of a poster image to be shown before showing the video.
- */
- posterUrl: '',
-
- // private
- componentCls: 'x-video',
-
- afterRender : function() {
- Ext.Video.superclass.afterRender.call(this);
- if (this.posterUrl) {
- this.media.hide();
- this.ghost = this.el.createChild({
- cls: 'x-video-ghost',
- style: 'width: 100%; height: 100%; background: #000 url(' + this.posterUrl + ') center center no-repeat; -webkit-background-size: 100% auto;'
- });
- this.ghost.on('tap', this.onGhostTap, this, {single: true});
- }
- },
-
- onGhostTap: function(){
- this.media.show();
- this.ghost.hide();
- this.play();
- },
-
- // private
- getConfiguration: function(){
- return {
- tag: 'video',
- width: '100%',
- height: '100%'
- };
- }
-});
-
-Ext.reg('video', Ext.Video);
-/**
- * @class Ext.Audio
- * @extends Ext.Media
- *
- * <p>Provides a simple container for HTML5 Audio.</p>
- * <p><i>Recommended types: Uncompressed WAV and AIF audio, MP3 audio, and AAC-LC or HE-AAC audio</i></p>
- *
- * <h2>Useful Properties</h2>
- * <ul class="list">
- * <li>{@link #url}</li>
- * <li>{@link #autoPause}</li>
- * <li>{@link #autoResume}</li>
- * </ul>
- *
- * <h2>Useful Methods</h2>
- * <ul class="list">
- * <li>{@link #pause}</li>
- * <li>{@link #play}</li>
- * <li>{@link #toggle}</li>
- * </ul>
- *
- * <h2>Screenshot:</h2>
- * <p><img src="doc_resources/Ext.Audio/screenshot.png" /></p>
- *
- * <h2>Example code:</h2>
- * <pre><code>
-var pnl = new Ext.Panel({
- fullscreen: true,
- items: [
- {
- xtype: 'audio',
- url : "who-goingmobile.mp3"
- }
- ]
-});</code></pre>
- * @xtype audio
- */
-Ext.Audio = Ext.extend(Ext.Media, {
- /**
- * @constructor
- * @param {Object} config
- * Create a new Audio container.
- */
-
- /**
- * @cfg {String} url
- * Location of the audio to play.
- */
-
- componentCls: 'x-audio',
-
- // @private
- onActivate: function(){
- Ext.Audio.superclass.onActivate.call(this);
- if (Ext.is.Phone) {
- this.media.show();
- }
- },
-
- // @private
- onDeactivate: function(){
- Ext.Audio.superclass.onDeactivate.call(this);
- if (Ext.is.Phone) {
- this.media.hide();
- }
- },
-
- // @private
- getConfiguration: function(){
- var hidden = !this.enableControls;
- if (!Ext.supports.AudioTag) {
- return {
- tag: 'embed',
- type: 'audio/mpeg',
- target: 'myself',
- controls: 'true',
- hidden: hidden
- };
- } else {
- return {
- tag: 'audio',
- hidden: hidden
- };
- }
- }
-});
-
-Ext.reg('audio', Ext.Audio);
-/**
- * @class Ext.MessageBox
- * @extends Ext.Sheet
- *
- * <p>Utility class for generating different styles of message boxes. The framework provides a global singleton {@link Ext.Msg} for common usage.<p/>
- * <p>Note that the MessageBox is asynchronous. Unlike a regular JavaScript <code>alert</code> (which will halt
- * browser execution), showing a MessageBox will not cause the code to stop. For this reason, if you have code
- * that should only run <em>after</em> some user feedback from the MessageBox, you must use a callback function
- * (see the <code>fn</code> configuration option parameter for the {@link #show show} method for more details).</p>
- *
- * <h2>Screenshot</h2>
- * <p><img src="doc_resources/Ext.MessageBox/screenshot.png" /></p>
- *
- * <h2>Example usage:</h2>
- * <pre><code>
-// Basic alert:
-Ext.Msg.alert('Title', 'The quick brown fox jumped over the lazy dog.', Ext.emptyFn);
-
-// Prompt for user data and process the result using a callback:
-Ext.Msg.prompt('Name', 'Please enter your name:', function(text) {
- // process text value and close...
-});
-
-// Confirmation alert
-Ext.Msg.confirm("Confirmation", "Are you sure you want to do that?", Ext.emptyFn);
- * </code></pre>
- * @xtype messagebox
- */
-Ext.MessageBox = Ext.extend(Ext.Sheet, {
- // Inherited from Ext.Sheet
- centered: true,
-
- // Inherited
- renderHidden: true,
-
- // Inherited
- ui: 'dark',
-
- /**
- * @cfg {String} componentCls
- * Component's Base CSS class
- */
- componentCls: 'x-msgbox',
-
- /**
- * @cfg {String/Mixed} enterAnimation effect when the message box is being displayed (defaults to 'pop')
- */
- enterAnimation: 'pop',
-
- /**
- * @cfg {String/Mixed} exitAnimation effect when the message box is being hidden (defaults to 'pop')
- */
- exitAnimation: 'pop',
-
-
-// Not sure what good this does, so I just comment it out for now
-// autoHeight : true,
-
- /**
- * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
- * @cfg {Number} defaultTextHeight
- */
- defaultTextHeight : 75,
-
- constructor : function(config) {
-
- config = config || {};
-
- var ui = config.ui || this.ui || '',
- baseCls = config.componentCls || this.componentCls;
-
- delete config.html;
-
- this.titleBar = Ext.create({
- xtype : 'toolbar',
- ui : ui,
- dock : 'top',
- cls : baseCls + '-title',
- title : ' '
- });
-
- this.buttonBar = Ext.create({
- xtype : 'toolbar',
- ui : ui,
- dock : 'bottom',
- layout: { pack: 'center' },
- cls : baseCls + '-buttons'
- });
-
- config = Ext.apply({
- ui : ui,
- dockedItems : [this.titleBar, this.buttonBar],
- renderSelectors : {
- body : '.' + baseCls + '-body',
- iconEl : '.' + baseCls + '-icon',
- msgContentEl : '.' + baseCls + '-content',
- msgEl : '.' + baseCls + '-text',
- inputsEl : '.' + baseCls + '-inputs',
- inputEl : '.' + baseCls + '-input-single',
- multiLineEl : '.' + baseCls + '-input-textarea'
- }
- }, config || {});
-
- Ext.MessageBox.superclass.constructor.call(this, config);
- },
-
- renderTpl: [
- '<div class="{componentCls}-body"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
- '<div class="{componentCls}-icon x-hidden-display"></div>',
- '<div class="{componentCls}-content">',
- '<div class="{componentCls}-text"></div>',
- '<div class="{componentCls}-inputs x-hidden-display">',
- '<input type="text" class="{componentCls}-input {componentCls}-input-single" />',
- '<textarea class="{componentCls}-input {componentCls}-input-textarea"></textarea>',
- '</div>',
- '</div>',
- '</div>'
- ],
-
- // @private
- onClick : function(button) {
- if (button) {
- var config = button.config || {};
-
- if (typeof config.fn == 'function') {
- config.fn.call(
- config.scope || null,
- button.itemId || button.text,
- config.input ? config.input.dom.value : null,
- config
- );
- }
-
- if (config.cls) {
- this.el.removeCls(config.cls);
- }
-
- if (config.input) {
- config.input.dom.blur();
- }
- }
-
- this.hide();
- },
-
- /**
- * Displays a new message box, or reinitializes an existing message box, based on the config options
- * passed in. All display functions (e.g. prompt, alert, etc.) on MessageBox call this function internally,
- * although those calls are basic shortcuts and do not support all of the config options allowed here.
- * @param {Object} config The following config options are supported: <ul>
- * <li><b>buttons</b> : Object/Array<div class="sub-desc">A button config object or Array of the same(e.g., Ext.MessageBox.OKCANCEL or {text:'Foo',
- * itemId:'cancel'}), or false to not show any buttons (defaults to false)</div></li>
- * <li><b>cls</b> : String<div class="sub-desc">A custom CSS class to apply to the message box's container element</div></li>
- * <li><b>defaultTextHeight</b> : Number<div class="sub-desc">The default height in pixels of the message box's multiline textarea
- * if displayed (defaults to 75)</div></li>
- * <li><b>fn</b> : Function<div class="sub-desc">A callback function which is called when the dialog is dismissed
- * by clicking on the configured buttons.
- * <p>Parameters passed:<ul>
- * <li><b>buttonId</b> : String<div class="sub-desc">The itemId of the button pressed, one of:<div class="sub-desc"><ul>
- * <li><tt>ok</tt></li>
- * <li><tt>yes</tt></li>
- * <li><tt>no</tt></li>
- * <li><tt>cancel</tt></li>
- * </ul></div></div></li>
- * <li><b>value</b> : String<div class="sub-desc">Value of the input field if either <tt><a href="#show-option-prompt" ext:member="show-option-prompt" ext:cls="Ext.MessageBox">prompt</a></tt>
- * or <tt><a href="#show-option-multiLine" ext:member="show-option-multiLine" ext:cls="Ext.MessageBox">multiLine</a></tt> is true</div></li>
- * <li><b>opt</b> : Object<div class="sub-desc">The config object passed to show.</div></li>
- * </ul></p></div></li>
- * <li><b>width</b> : Number<div class="sub-desc">A fixed width for the MessageBox (defaults to 'auto')</div></li>
- * <li><b>height</b> : Number<div class="sub-desc">A fixed height for the MessageBox (defaults to 'auto')</div></li>
- * <li><b>scope</b> : Object<div class="sub-desc">The scope of the callback function</div></li>
- * <li><b>icon</b> : String<div class="sub-desc">A CSS class that provides a background image to be used as the body icon for the
- * dialog (e.g. Ext.MessageBox.WARNING or 'custom-class') (defaults to '')</div></li>
- * <li><b>modal</b> : Boolean<div class="sub-desc">False to allow user interaction with the page while the message box is
- * displayed (defaults to true)</div></li>
- * <li><b>msg</b> : String<div class="sub-desc">A string that will replace the existing message box body text (defaults to the
- * XHTML-compliant non-breaking space character '&#160;')</div></li>
- * <li><a id="show-option-multiline"></a><b>multiLine</b> : Boolean<div class="sub-desc">
- * True to prompt the user to enter multi-line text (defaults to false)</div></li>
- * <li><a id="show-option-prompt"></a><b>prompt</b> : Boolean<div class="sub-desc">True to prompt the user to enter single-line text (defaults to false)</div></li>
- * <li><b>title</b> : String<div class="sub-desc">The title text</div></li>
- * <li><b>value</b> : String<div class="sub-desc">The string value to set into the active textbox element if displayed</div></li>
- * </ul>
- * Example usage:
- * <pre><code>
-Ext.Msg.show({
- title: 'Address',
- msg: 'Please enter your address:',
- width: 300,
- buttons: Ext.MessageBox.OKCANCEL,
- multiLine: true,
- prompt : { maxlength : 180, autocapitalize : true },
- fn: saveAddress,
- icon: Ext.MessageBox.INFO
-});
-</code></pre>
- * @return {Ext.MessageBox} this
- */
- show : function(config) {
- var attrib,
- attrName,
- attribs = {
- autocomplete : 'off',
- autocapitalize : 'off',
- autocorrect : 'off',
- maxlength : 0,
- autofocus : true,
- placeholder : '',
- type : 'text'
- },
- assert = /true|on/i;
-
- this.rendered || this.render(document.body);
-
- config = Ext.applyIf(
- config || {}, {
- multiLine : false,
- prompt : false,
- value : '',
- modal : true
- }
- );
-
- if (config.title) {
- this.titleBar.setTitle(config.title);
- this.titleBar.show();
- } else {
- this.titleBar.hide();
- }
-
- if (this.inputsEl && (config.multiLine || config.prompt)) {
- this.inputsEl.show();
-
- if (this.multiLineEl && config.multiLine) {
- this.inputEl && this.inputEl.hide();
- this.multiLineEl.show().setHeight(Ext.isNumber(config.multiLine) ? parseFloat(config.multiLine) : this.defaultTextHeight);
- config.input = this.multiLineEl;
- } else if (this.inputEl) {
- this.inputEl.show();
- this.multiLineEl && this.multiLineEl.hide();
- config.input = this.inputEl;
- }
-
- // Assert/default HTML5 input attributes
- if (Ext.isObject(config.prompt)) {
- Ext.apply(attribs, config.prompt);
- }
-
- for (attrName in attribs) {
- if (attribs.hasOwnProperty(attrName)) {
- attrib = attribs[attrName];
- config.input.dom.setAttribute(
- attrName.toLowerCase(),
- /^auto/i.test(attrName) ? (assert.test(attrib+'') ? 'on' : 'off' ) : attrib
- );
- }
- }
-
- } else {
- this.inputsEl && this.inputsEl.hide();
- }
-
- this.setIcon(config.icon || '', false);
- this.updateText(config.msg, false);
-
- if (config.cls) {
- this.el.addCls(config.cls);
- }
-
- this.modal = !!config.modal;
-
- var bbar = this.buttonBar,
- bs = [];
-
- bbar.removeAll();
-
- Ext.each([].concat(config.buttons || Ext.MessageBox.OK), function(button) {
- if (button) {
- bs.push(
- Ext.applyIf({
- config : config,
- scope : this,
- handler : this.onClick
- }, button)
- );
- }
- }, this);
-
- bbar.add(bs);
-
- if (bbar.rendered) {
- bbar.doLayout();
- }
-
- Ext.MessageBox.superclass.show.call(this, config.animate);
-
- if (config.input) {
- config.input.dom.value = config.value || '';
- // For browsers without 'autofocus' attribute support
- if (assert.test(attribs.autofocus+'') && !('autofocus' in config.input.dom)) {
- config.input.dom.focus();
- }
- }
-
- return this;
- },
-
- // @private
- onOrientationChange : function() {
- this.doComponentLayout();
-
- Ext.MessageBox.superclass.onOrientationChange.apply(this, arguments);
- },
-
- // @private
- adjustScale : function(){
- Ext.apply(this,{
- maxWidth : window.innerWidth,
- maxHeight : window.innerHeight,
- minWidth : window.innerWidth * .5,
- minHeight : window.innerHeight * .5
- });
- },
-
- // @private
- doComponentLayout : function() {
- this.adjustScale();
-
- return Ext.MessageBox.superclass.doComponentLayout.apply(this, arguments);
- },
-
- /**
- * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt).
- * If a callback function is passed it will be called after the user clicks the button, and the
- * itemId of the button that was clicked will be passed as the only parameter to the callback
- * @param {String} title The title bar text
- * @param {String} msg The message box body text
- * @param {Function} fn (optional) The callback function invoked after the message box is closed
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
- * @return {Ext.MessageBox} this
- */
- alert : function(title, msg, fn, scope) {
- return this.show({
- title : title,
- msg : msg,
- buttons: Ext.MessageBox.OK,
- fn : fn,
- scope : scope,
- icon : Ext.MessageBox.INFO
- });
- },
-
- /**
- * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).
- * If a callback function is passed it will be called after the user clicks either button,
- * and the id of the button that was clicked will be passed as the only parameter to the callback
- * (could also be the top-right close button).
- * @param {String} title The title bar text
- * @param {String} msg The message box body text
- * @param {Function} fn (optional) The callback function invoked when user taps on the OK/Cancel button.
- * The button is passed as the first argument.
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
- * @return {Ext.MessageBox} this
- */
- confirm : function(title, msg, fn, scope) {
- return this.show({
- title : title,
- msg : msg,
- buttons: Ext.MessageBox.YESNO,
- fn: function(button) {
- fn.call(scope, button);
- },
- scope : scope,
- icon: Ext.MessageBox.QUESTION
- });
- },
-
- /**
- * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).
- * The prompt can be a single-line or multi-line textbox. If a callback function is passed it will be called after the user
- * clicks either button, and the id of the button that was clicked (could also be the top-right
- * close button) and the text that was entered will be passed as the two parameters to the callback.
- * @param {String} title The title bar text
- * @param {String} msg The message box body text
- * @param {Function} fn (optional) The callback function invoked when the user taps on the OK/Cancel button,
- * the button is passed as the first argument, the entered string value is passed as the second argument
- * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
- * @param {Boolean/Number} multiLine (optional) True to create a multiline textbox using the defaultTextHeight
- * property, or the height in pixels to create the textbox (defaults to false / single-line)
- * @param {String} value (optional) Default value of the text input element (defaults to '')
- * @param {Object} promptConfig <div class="sub-desc">(optional) A hash collection of input attribute values.<div class="sub-desc">Specified values may include:<ul>
- * <li><tt>focus</tt> : Boolean <div class="sub-desc"><tt>true</tt> to assert initial input focus (defaults to false)</div></li>
- * <li><tt>placeholder</tt> : String <div class="sub-desc">String value rendered when the input field is empty (defaults to empty string)</div></li>
- * <li><tt>autocapitalize</tt> : String/Boolean <div class="sub-desc"><tt>true/on</tt> to capitalize the first letter of each word in the input value (defaults to 'off')</div></li>
- * <li><tt>autocorrect</tt> : String/Boolean <div class="sub-desc"><tt>true/on</tt> to enable spell-checking/autocorrect features if supported by the browser (defaults to 'off')</div></li>
- * <li><tt>autocomplete</tt> : String/Boolean <div class="sub-desc"><tt>true/on</tt> to enable autoCompletion of supplied text input values if supported by the browser (defaults to 'off')</div></li>
- * <li><tt>maxlength</tt> : Number <div class="sub-desc">Maximum number of characters allowed in the input if supported by the browser (defaults to 0)</div></li>
- * <li><tt>type</tt> : String <div class="sub-desc">The type of input field. Possible values (if supported by the browser) may include (text, search, number, range, color, tel, url, email, date, month, week, time, datetime) (defaults to 'text')</div></li>
- * </ul></div></div>
- * Example usage:
- * <pre><code>
- Ext.Msg.prompt(
- 'Welcome!',
- 'What\'s your name going to be today?',
- function(value){
- console.log(value)
- },
- null,
- false,
- null,
- { autocapitalize : true, placeholder : 'First-name please...' }
- );
- * </code></pre>
- * @return {Ext.MessageBox} this
- */
- prompt : function(title, msg, fn, scope, multiLine, value, promptConfig) {
- return this.show({
- title : title,
- msg : msg,
- buttons: Ext.MessageBox.OKCANCEL,
- fn: function(button, inputValue) {
- fn.call(scope, button, inputValue);
- },
- scope : scope,
- icon : Ext.MessageBox.QUESTION,
- prompt: promptConfig || true,
- multiLine: multiLine,
- value: value
- });
- },
-
- /**
- * Updates the message box body text
- * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
- * the XHTML-compliant non-breaking space character '&#160;')
- * @return {Ext.MessageBox} this
- */
- updateText : function(text, doLayout) {
- if(this.msgEl) {
- this.msgEl.update(text ? String(text) : ' ');
- if(doLayout !== false) {
- this.doComponentLayout();
- }
- }
- return this;
- },
-
- /**
- * Adds the specified icon to the dialog. By default, the class 'x-msgbox-icon' is applied for default
- * styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')
- * to clear any existing icon. This method must be called before the MessageBox is shown.
- * The following built-in icon classes are supported, but you can also pass in a custom class name:
- * <pre>
-Ext.MessageBox.INFO
-Ext.MessageBox.WARNING
-Ext.MessageBox.QUESTION
-Ext.MessageBox.ERROR
- *</pre>
- * @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon
- * @return {Ext.MessageBox} this
- */
- setIcon : function(icon, doLayout) {
- if (icon) {
- this.iconEl.show();
- this.iconEl.replaceCls(this.iconCls, icon);
- } else {
- this.iconEl.replaceCls(this.iconCls, 'x-hidden-display');
- }
-
- if (doLayout !== false) {
- this.doComponentLayout();
- }
-
- this.iconCls = icon;
- return this;
- }
-});
-
-(function(){
- var B = Ext.MessageBox;
-
- Ext.apply(B, {
- OK : {text : 'OK', itemId : 'ok', ui : 'action' },
- CANCEL : {text : 'Cancel', itemId : 'cancel'},
- YES : {text : 'Yes', itemId : 'yes', ui : 'action' },
- NO : {text : 'No', itemId : 'no'},
- // Add additional(localized) button configs here
-
- // ICON CSS Constants
- INFO : 'x-msgbox-info',
- WARNING : 'x-msgbox-warning',
- QUESTION : 'x-msgbox-question',
- ERROR : 'x-msgbox-error'
- });
-
- Ext.apply(B, {
- OKCANCEL : [B.CANCEL, B.OK],
- YESNOCANCEL : [B.CANCEL, B.NO, B.YES],
- YESNO : [B.NO, B.YES]
- // Add additional button collections here
- });
-
-})();
-
-Ext.reg('messagebox', Ext.MessageBox);
-
-//DEPRECATED - remove this in 1.0. See RC1 Release Notes for details
-Ext.reg('msgbox', Ext.MessageBox);
-
-/**
- * @class Ext.Msg
- *
- * <p>A global shared singleton instance of the {@link Ext.MessageBox} class. See {@link Ext.MessageBox} for documentation.</p>
- *
- * @singleton
- */
-Ext.Msg = new Ext.MessageBox();
-/**
- * @class Ext.form.FormPanel
- * @extends Ext.Panel
- * <p>Simple form panel which enables easy getting and setting of field values. Can load model instances. Example usage:</p>
-<pre><code>
-var form = new Ext.form.FormPanel({
- items: [
- {
- xtype: 'textfield',
- name : 'first',
- label: 'First name'
- },
- {
- xtype: 'textfield',
- name : 'last',
- label: 'Last name'
- },
- {
- xtype: 'numberfield',
- name : 'age',
- label: 'Age'
- },
- {
- xtype: 'urlfield',
- name : 'url',
- label: 'Website'
- }
- ]
-});
-</code></pre>
- * <p>Loading model instances:</p>
-<pre><code>
-Ext.regModel('User', {
- fields: [
- {name: 'first', type: 'string'},
- {name: 'last', type: 'string'},
- {name: 'age', type: 'int'},
- {name: 'url', type: 'string'}
- ]
-});
-
-var user = Ext.ModelMgr.create({
- first: 'Ed',
- last : 'Spencer',
- age : 24,
- url : 'http://extjs.com'
-}, 'User');
-
-form.load(user);
-</code></pre>
- * @xtype form
- */
-Ext.form.FormPanel = Ext.extend(Ext.Panel, {
- /**
- * @cfg {Boolean} standardSubmit
- * Wether or not we want to perform a standard form submit. Defaults to false
- */
- standardSubmit: false,
-
- componentCls: 'x-form',
-
- /**
- * @cfg {String} url
- * The default Url for submit actions
- */
- url: undefined,
-
- /**
- * @cfg {Object} baseParams
- * Optional hash of params to be sent (when standardSubmit configuration is false) on every submit.
- */
- baseParams : undefined,
-
- /**
- * @cfg {Object} waitTpl
- * The defined {@link #waitMsg} template. Used for precise control over the masking agent used
- * to mask the FormPanel (or other Element) during form Ajax/submission actions. For more options, see
- * {@link #showMask} method.
- */
- waitTpl: new Ext.XTemplate(
- '<div class="{cls}">{message}…</div>'
- ),
-
- /**
- * @cfg {Object} submitOnAction
- * When this is set to true, the form will automatically submit itself whenever the 'action'
- * event fires on a field in this form. The action event usually fires whenever you press
- * go or enter inside a textfield.
- */
- submitOnAction : true,
-
- getElConfig: function() {
- return Ext.apply(Ext.form.FormPanel.superclass.getElConfig.call(this), {
- tag: 'form'
- });
- },
-
- // @private
- initComponent : function() {
- this.addEvents(
- /**
- * @event submit
- * Fires upon successful (Ajax-based) form submission
- * @param {Ext.FormPanel} this This FormPanel
- * @param {Object} result The result object as returned by the server
- */
- 'submit',
- /**
- * @event beforesubmit
- * Fires immediately preceding any Form submit action.
- * Implementations may adjust submitted form values or options prior to execution.
- * A return value of <tt>false</tt> from this listener will abort the submission
- * attempt (regardless of standardSubmit configuration)
- * @param {Ext.FormPanel} this This FormPanel
- * @param {Object} values A hash collection of the qualified form values about to be submitted
- * @param {Object} options Submission options hash (only available when standardSubmit is false)
- */
- 'beforesubmit',
- /**
- * @event exception
- * Fires when either the Ajax HTTP request reports a failure OR the server returns a success:false
- * response in the result payload.
- * @param {Ext.FormPanel} this This FormPanel
- * @param {Object} result Either a failed Ext.data.Connection request object or a failed (logical) server
- * response payload.
- */
- 'exception'
- );
-
- Ext.form.FormPanel.superclass.initComponent.call(this);
-
- this.on('action', this.onFieldAction, this);
- },
-
- // @private
- afterRender : function() {
- Ext.form.FormPanel.superclass.afterRender.call(this);
- this.el.on('submit', this.onSubmit, this);
- },
-
- // @private
- onSubmit : function(e, t) {
- if (!this.standardSubmit || this.fireEvent('beforesubmit', this, this.getValues(true)) === false) {
- if (e) {
- e.stopEvent();
- }
- }
- },
-
- // @private
- onFieldAction : function(field) {
- if (this.submitOnAction) {
- field.blur();
- this.submit();
- }
- },
-
- /**
- * Performs a Ajax-based submission of form values (if standardSubmit is false) or otherwise
- * executes a standard HTML Form submit action.
- * @param {Object} options Unless otherwise noted, options may include the following:
- * <ul>
- * <li><b>url</b> : String
- * <div class="sub-desc">
- * The url for the action (defaults to the form's {@link #url url}.)
- * </div></li>
- *
- * <li><b>method</b> : String
- * <div class="sub-desc">
- * The form method to use (defaults to the form's method, or POST if not defined)
- * </div></li>
- *
- * <li><b>params</b> : String/Object
- * <div class="sub-desc">
- * The params to pass
- * (defaults to the FormPanel's baseParams, or none if not defined)
- * Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
- * </div></li>
- *
- * <li><b>headers</b> : Object
- * <div class="sub-desc">
- * Request headers to set for the action
- * (defaults to the form's default headers)
- * </div></li>
- *
- * <li><b>autoAbort</b> : Boolean
- * <div class="sub-desc">
- * <tt>true</tt> to abort any pending Ajax request prior to submission (defaults to false)
- * Note: Has no effect when standardSubmit is enabled.
- * </div></li>
- *
- * <li><b>submitDisabled</b> : Boolean
- * <div class="sub-desc">
- * <tt>true</tt> to submit all fields regardless of disabled state (defaults to false)
- * Note: Has no effect when standardSubmit is enabled.
- * </div></li>
- *
- * <li><b>waitMsg</b> : String/Config
- * <div class="sub-desc">
- * If specified, the value is applied to the {@link #waitTpl} if defined, and rendered to the
- * {@link #waitMsgTarget} prior to a Form submit action.
- * </div></li>
- *
- * <li><b>success</b> : Function
- * <div class="sub-desc">
- * The callback that will be invoked after a successful response. A response is successful if
- * a response is received from the server and is a JSON object where the success property is set
- * to true, {"success": true}
- *
- * The function is passed the following parameters:
- * <ul>
- * <li>form : Ext.FormPanel The form that requested the action</li>
- * <li>result : The result object returned by the server as a result of the submit request.</li>
- * </ul>
- * </div></li>
- *
- * <li><b>failure</b> : Function
- * <div class="sub-desc">
- * The callback that will be invoked after a
- * failed transaction attempt. The function is passed the following parameters:
- * <ul>
- * <li>form : The Ext.FormPanel that requested the submit.</li>
- * <li>result : The failed response or result object returned by the server which performed the operation.</li>
- * </ul>
- * </div></li>
- *
- * <li><b>scope</b> : Object
- * <div class="sub-desc">
- * The scope in which to call the callback functions (The this reference for the callback functions).
- * </div></li>
- * </ul>
- *
- * @return {Ext.data.Connection} request Object
- */
-
- submit: function(options) {
- var form = this.el.dom || {},
- formValues
-
- options = Ext.apply({
- url : this.url || form.action,
- submitDisabled : false,
- method : form.method || 'post',
- autoAbort : false,
- params : null,
- waitMsg : null,
- headers : null,
- success : null,
- failure : null
- }, options || {});
-
- formValues = this.getValues(this.standardSubmit || !options.submitDisabled);
-
- if (this.standardSubmit) {
- if (form) {
- if (options.url && Ext.isEmpty(form.action)) {
- form.action = options.url;
- }
-
- form.method = (options.method || form.method).toLowerCase();
-
- if (this.fireEvent('beforesubmit', this, formValues, options) !== false) {
- form.submit();
- }
- }
- return null;
- }
-
- if (this.fireEvent('beforesubmit', this, formValues, options ) !== false) {
- if (options.waitMsg) {
- this.showMask(options.waitMsg);
- }
-
- return Ext.Ajax.request({
- url : options.url,
- method : options.method,
- rawData : Ext.urlEncode(Ext.apply(
- Ext.apply({}, this.baseParams || {}),
- options.params || {},
- formValues
- )),
- autoAbort : options.autoAbort,
- headers : Ext.apply(
- {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
- options.headers || {}),
- scope : this,
- callback : function(callbackOptions, success, response) {
- var responseText = response.responseText;
- this.hideMask();
-
- if (success) {
-
- response = Ext.decode(responseText);
- success = !!response.success;
-
- if (success) {
- if (Ext.isFunction(options.success)) {
- options.success.call(options.scope || this, this, response, responseText);
- }
-
- this.fireEvent('submit', this, response);
- return;
- }
- }
-
- if (Ext.isFunction(options.failure)) {
- options.failure.call(options.scope || this, this, response, responseText);
- }
-
- this.fireEvent('exception', this, response);
- }
- });
- }
- },
-
- /**
- * Loads matching fields from a model instance into this form
- * @param {Ext.data.Model} instance The model instance
- * @return {Ext.form.FormPanel} this
- */
- loadRecord: function(instance) {
- if (instance && instance.data) {
- this.setValues(instance.data);
-
- /**
- * The Model instance currently loaded into this form (if any). Read only
- * @property record
- * @type Ext.data.Model
- */
- this.record = instance;
- }
-
- return this;
- },
-
- /**
- * @private
- * Backwards-compatibility for a poorly-named function
- */
- loadModel: function() {
- return this.loadRecord.apply(this, arguments);
- },
-
- /**
- * Returns the Model instance currently loaded into this form (if any)
- * @return {Ext.data.Model} The Model instance
- */
- getRecord: function() {
- return this.record;
- },
-
- /**
- * Updates a model instance with the current values of this form
- * @param {Ext.data.Model} instance The model instance
- * @param {Boolean} enabled <tt>true</tt> to update the Model with values from enabled fields only
- * @return {Ext.form.FormPanel} this
- */
- updateRecord: function(instance, enabled) {
- var fields, values, name;
-
- if(instance && (fields = instance.fields)){
- values = this.getValues(enabled);
- for (name in values) {
- if(values.hasOwnProperty(name) && fields.containsKey(name)){
- instance.set(name, values[name]);
- }
- }
- }
- return this;
- },
-
-
- /**
- * Sets the values of form fields in bulk. Example usage:
-<pre><code>
-myForm.setValues({
- name: 'Ed',
- crazy: true,
- username: 'edspencer'
-});
-</code></pre>
- If there groups of checkbox fields with the same name, pass their values in an array. For example:
-
-<pre><code>
-myForm.setValues({
- name: 'Jacky',
- crazy: false,
- hobbies: [
- 'reading',
- 'cooking',
- 'gaming'
- ]
-});
-</code></pre>
-
- * @param {Object} values field name => value mapping object
- * @return {Ext.form.FormPanel} this
- */
- setValues: function(values) {
- var fields = this.getFields(),
- name,
- field,
- value;
-
- values = values || {};
-
- for (name in values) {
- if (values.hasOwnProperty(name)) {
- field = fields[name];
- value = values[name];
- if (field) {
- if (Ext.isArray(field)) {
- field.forEach(function(field){
- if (Ext.isArray(values[name])) {
- field.setChecked((value.indexOf(field.getValue()) != -1));
- } else {
- field.setChecked((value == field.getValue()));
- }
- });
- } else {
- if (field.setChecked) {
- field.setChecked(value);
- } else {
- field.setValue(value);
- }
- }
- }
- }
- }
-
- return this;
- },
-
- /**
- * Returns an object containing the value of each field in the form, keyed to the field's name.
- * For groups of checkbox fields with the same name, it will be arrays of values. For examples:
-
-<pre><code>
- {
- name: "Jacky Nguyen", // From a TextField
- favorites: [
- 'pizza',
- 'noodle',
- 'cake'
- ]
- }
-</code></pre>
-
- * @param {Boolean} enabled <tt>true</tt> to return only enabled fields
- * @return {Object} Object mapping field name to its value
- */
- getValues: function(enabled) {
- var fields = this.getFields(),
- field,
- values = {},
- name;
-
- for (name in fields) {
- if (fields.hasOwnProperty(name)) {
- if (Ext.isArray(fields[name])) {
- values[name] = [];
-
- fields[name].forEach(function(field) {
- if (field.isChecked() && !(enabled && field.disabled)) {
- if (field instanceof Ext.form.Radio) {
- values[name] = field.getValue();
- } else {
- values[name].push(field.getValue());
- }
- }
- });
- } else {
- field = fields[name];
-
- if (!(enabled && field.disabled)) {
- if (field instanceof Ext.form.Checkbox) {
- values[name] = (field.isChecked()) ? field.getValue() : null;
- } else {
- values[name] = field.getValue();
- }
- }
- }
- }
- }
-
- return values;
- },
-
- /**
- * Resets all fields in the form back to their original values
- * @return {Ext.form.FormPanel} this This form
- */
- reset: function() {
- this.getFieldsAsArray().forEach(function(field) {
- field.reset();
- });
-
- return this;
- },
-
- /**
- * A convenient method to enable all fields in this forms
- * @return {Ext.form.FormPanel} this This form
- */
- enable: function() {
- this.getFieldsAsArray().forEach(function(field) {
- field.enable();
- });
-
- return this;
- },
-
- /**
- * A convenient method to disable all fields in this forms
- * @return {Ext.form.FormPanel} this This form
- */
- disable: function() {
- this.getFieldsAsArray().forEach(function(field) {
- field.disable();
- });
-
- return this;
- },
-
- getFieldsAsArray: function() {
- var fields = [];
-
- var getFieldsFrom = function(item) {
- if (item.isField) {
- fields.push(item);
- }
-
- if (item.isContainer) {
- item.items.each(getFieldsFrom);
- }
- };
-
- this.items.each(getFieldsFrom);
-
- return fields;
- },
-
- /**
- * @private
- * Returns all {@link Ext.Field field} instances inside this form
- * @param byName return only fields that match the given name, otherwise return all fields.
- * @return {Object/Array} All field instances, mapped by field name; or an array if byName is passed
- */
- getFields: function(byName) {
- var fields = {},
- itemName;
-
- var getFieldsFrom = function(item) {
- if (item.isField) {
- itemName = item.getName();
-
- if ((byName && itemName == byName) || typeof byName == 'undefined') {
- if (fields.hasOwnProperty(itemName)) {
- if (!Ext.isArray(fields[itemName])) {
- fields[itemName] = [fields[itemName]];
- }
-
- fields[itemName].push(item);
- } else {
- fields[itemName] = item;
- }
- }
-
- }
-
- if (item.isContainer) {
- item.items.each(getFieldsFrom);
- }
- };
-
- this.items.each(getFieldsFrom);
-
- return (byName) ? (fields[byName] || []) : fields;
- },
-
- getFieldsFromItem: function() {
-
- },
- /**
- * Shows a generic/custom mask over a designated Element.
- * @param {String/Object} cfg Either a string message or a configuration object supporting
- * the following options:
-<pre><code>
- {
- message : 'Please Wait',
- transparent : false,
- target : Ext.getBody(), //optional target Element
- cls : 'form-mask',
- customImageUrl : 'trident.jpg'
- }
-</code></pre>This object is passed to the {@link #waitTpl} for use with a custom masking implementation.
- * @param {String/Element} target The target Element instance or Element id to use
- * as the masking agent for the operation (defaults the container Element of the component)
- * @return {Ext.form.FormPanel} this
- */
- showMask : function(cfg, target) {
- cfg = Ext.isString(cfg) ? {message : cfg} : cfg;
-
- if (cfg && this.waitTpl) {
- this.maskTarget = target = Ext.get(target || cfg.target) || this.el;
- target && target.mask(this.waitTpl.apply(cfg));
- }
- return this;
- },
-
- /**
- * Hides a previously shown wait mask (See {@link #showMask})
- * @return {Ext.form.FormPanel} this
- */
- hideMask : function(){
- if(this.maskTarget){
- this.maskTarget.unmask();
- delete this.maskTarget;
- }
- return this;
- }
-});
-
-/**
- * (Shortcut to {@link #loadRecord} method) Loads matching fields from a model instance into this form
- * @param {Ext.data.Model} instance The model instance
- * @return {Ext.form.FormPanel} this
- */
-Ext.form.FormPanel.prototype.load = Ext.form.FormPanel.prototype.loadModel;
-
-Ext.reg('formpanel', Ext.form.FormPanel);
-
-//DEPRECATED - remove this in 1.0. See RC1 Release Notes for details
-Ext.reg('form', Ext.form.FormPanel);
-
-/**
- * @class Ext.form.FieldSet
- * @extends Ext.Container
- * <p>Simple FieldSet, can contain fields as items. FieldSets do not add any behavior, other than an optional title, and
- * are just used to group similar fields together. Example usage (within a form):</p>
-<pre><code>
-new Ext.form.FormPanel({
- items: [
- {
- xtype: 'fieldset',
- title: 'About Me',
- items: [
- {
- xtype: 'textfield',
- name : 'firstName',
- label: 'First Name'
- },
- {
- xtype: 'textfield',
- name : 'lastName',
- label: 'Last Name'
- }
- ]
- }
- ]
-});
-</code></pre>
- * @xtype fieldset
- */
-Ext.form.FieldSet = Ext.extend(Ext.Panel, {
- componentCls: 'x-form-fieldset',
-
- // @private
- initComponent : function() {
- this.componentLayout = this.getLayout();
- Ext.form.FieldSet.superclass.initComponent.call(this);
- },
-
-
- /**
- * @cfg {String} title Optional fieldset title, rendered just above the grouped fields
- */
-
- /**
- * @cfg {String} instructions Optional fieldset instructions, rendered just below the grouped fields
- */
-
- // @private
- afterLayout : function(layout) {
- Ext.form.FieldSet.superclass.afterLayout.call(this, layout);
-
- if (this.title && !this.titleEl) {
- this.setTitle(this.title);
- } else if (this.titleEl) {
- this.el.insertFirst(this.titleEl);
- }
-
- if (this.instructions && !this.instructionsEl) {
- this.setInstructions(this.instructions);
- } else if (this.instructionsEl) {
- this.el.appendChild(this.instructionsEl);
- }
- },
-
- /**
- * Sets the title of the current fieldset.
- * @param {String} title The new title
- * @return {Ext.form.FieldSet} this
- */
- setTitle: function(title){
- if (this.rendered) {
- if (!this.titleEl) {
- this.titleEl = this.el.insertFirst({
- cls: this.componentCls + '-title'
- });
- }
- this.titleEl.setHTML(title);
- } else {
- this.title = title;
- }
- return this;
- },
-
- /**
- * Sets the instructions of the current fieldset.
- * @param {String} instructions The new instructions
- * @return {Ext.form.FieldSet} this
- */
- setInstructions: function(instructions){
- if (this.rendered) {
- if (!this.instructionsEl) {
- this.instructionsEl = this.el.createChild({
- cls: this.componentCls + '-instructions'
- });
- }
- this.instructionsEl.setHTML(instructions);
- } else {
- this.instructions = instructions;
- }
- return this;
- }
-});
-
-Ext.reg('fieldset', Ext.form.FieldSet);
-/**
- * @class Ext.form.Field
- * @extends Ext.Container
- * <p>Base class for form fields that provides default event handling, sizing, value handling and other functionality. Ext.form.Field
- * is not used directly in applications, instead the subclasses such as {@link Ext.form.Text} should be used.</p>
- * @constructor
- * Creates a new Field
- * @param {Object} config Configuration options
- * @xtype field
- */
-Ext.form.Field = Ext.extend(Ext.Component, {
- /**
- * Set to true on all Ext.form.Field subclasses. This is used by {@link Ext.form.FormPanel#getValues} to determine which
- * components inside a form are fields.
- * @property isField
- * @type Boolean
- */
- isField: true,
-
- /**
- * <p>The label Element associated with this Field. <b>Only available if a {@link #label} is specified.</b></p>
- * @type Ext.Element
- * @property labelEl
- */
-
- /**
- * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
- * not those which are built via applyTo (defaults to undefined).
- */
-
- /**
- * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
- */
-
- /**
- * @cfg {String} name The field's HTML name attribute (defaults to '').
- * <b>Note</b>: this property must be set if this field is to be automatically included with
- * {@link Ext.form.FormPanel#submit form submit()}.
- */
-
- /**
- * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
- */
-
- /**
- * @cfg {String} fieldCls The default CSS class for the field (defaults to 'x-form-field')
- */
- fieldCls: 'x-form-field',
-
- baseCls: 'x-field',
-
- /**
- * @cfg {String} inputCls Optional CSS class that will be added to the actual <input> element (or whichever different element is
- * defined by {@link inputAutoEl}). Defaults to undefined.
- */
- inputCls: undefined,
-
- /**
- * @cfg {Boolean} disabled True to disable the field (defaults to false).
- * <p>Be aware that conformant with the <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1">HTML specification</a>,
- * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
- */
- disabled: false,
-
- renderTpl: [
- '<tpl if="label">',
- '<div class="x-form-label"><span>{label}</span></div>',
- '</tpl>',
- '<tpl if="fieldEl">',
- '<div class="x-form-field-container"><input id="{inputId}" type="{inputType}" name="{name}" class="{fieldCls}"',
- '<tpl if="tabIndex">tabIndex="{tabIndex}" </tpl>',
- '<tpl if="placeHolder">placeholder="{placeHolder}" </tpl>',
- '<tpl if="style">style="{style}" </tpl>',
- '<tpl if="maxlength">maxlength="{maxlength}" </tpl>',
- '<tpl if="autoComplete">autocomplete="{autoComplete}" </tpl>',
- '<tpl if="autoCapitalize">autocapitalize="{autoCapitalize}" </tpl>',
- '<tpl if="autoCorrect">autocorrect="{autoCorrect}" </tpl> />',
- '<tpl if="useMask"><div class="x-field-mask"></div></tpl>',
- '</div>',
- '<tpl if="useClearIcon"><div class="x-field-clear-container"><div class="x-field-clear x-hidden-visibility">×</div></div></tpl>',
- '</tpl>'
- ],
-
- // @private
- isFormField: true,
-
- /**
- * @cfg {Boolean} autoCreateField True to automatically create the field input element on render.
- * This is true by default, but should be set to false for any Ext.Field subclasses that don't
- * need an HTML input (e.g. Ext.Slider and similar)
- */
- autoCreateField: true,
-
- /**
- * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
- * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
- * no separate Ext components for those. Note that if you use <tt>inputType:'file'</tt>, {@link #emptyText}
- * is not supported and should be avoided.
- */
- inputType: 'text',
-
- /**
- * @cfg {String} label The label to associate with this field. Defaults to <tt>null</tt>.
- */
- label: null,
-
- /**
- * @cfg {Mixed} labelWidth The width of the label, can be any valid CSS size. E.g '20%', '6em', '100px'.
- * Defaults to <tt>'30%'</tt>
- */
- labelWidth: '30%',
-
- /**
- * @cfg {String} labelAlign The location to render the label of the field. Acceptable values are 'top' and 'left'.
- * Defaults to <tt>'left'</tt>
- */
- labelAlign: 'left',
-
- /**
- * @cfg {Boolean} required True to make this field required. Note: this only causes a visual indication.
- * Doesn't prevent user from submitting the form.
- */
- required: false,
-
- // @private
- useMask: false,
-
- // @private
- initComponent: function() {
-
- Ext.form.Field.superclass.initComponent.call(this);
- },
-
- /**
- * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
- * attribute of the field if available.
- * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
- */
- getName: function() {
- return this.name || this.id || '';
- },
-
- /**
- * @private
- */
- applyRenderSelectors: function() {
- this.renderSelectors = Ext.applyIf(this.renderSelectors || {}, {
- mask: '.x-field-mask',
- labelEl: '.x-form-label',
- fieldEl: '.' + Ext.util.Format.trim(this.renderData.fieldCls).replace(/ /g, '.')
- });
-
- Ext.form.Field.superclass.applyRenderSelectors.call(this);
- },
-
- /**
- * @private
- */
- initRenderData: function() {
- Ext.form.Field.superclass.initRenderData.apply(this, arguments);
-
- Ext.applyIf(this.renderData, {
- disabled : this.disabled,
- fieldCls : 'x-input-' + this.inputType + (this.inputCls ? ' ' + this.inputCls: ''),
- fieldEl : !this.fieldEl && this.autoCreateField,
- inputId : Ext.id(),
- label : this.label,
- labelAlign : 'x-label-align-' + this.labelAlign,
- name : this.getName(),
- required : this.required,
- style : this.style,
- tabIndex : this.tabIndex,
- inputType : this.inputType,
- useMask : this.useMask
- });
-
- return this.renderData;
- },
-
- // @private
- initEvents: function() {
- Ext.form.Field.superclass.initEvents.apply(this, arguments);
-
- if (this.fieldEl) {
- if (this.useMask && this.mask) {
- this.mon(this.mask, {
- tap: this.onMaskTap,
- scope: this
- });
- }
- }
- },
-
- /**
- * @private
- */
- onRender: function() {
- Ext.form.Field.superclass.onRender.apply(this, arguments);
-
- var cls = [];
-
- if (this.required) {
- cls.push('x-field-required');
- }
- if (this.label) {
- cls.push('x-label-align-' + this.labelAlign);
- }
-
- this.el.addCls(cls);
- },
-
- /**
- * @private
- */
- afterRender: function() {
- Ext.form.Field.superclass.afterRender.apply(this, arguments);
-
- if (this.label) {
- this.setLabelWidth(this.labelWidth);
- }
-
- this.initValue();
- },
-
- isDisabled: function() {
- return this.disabled;
- },
-
- // @private
- onEnable: function() {
- this.fieldEl.dom.disabled = false;
- },
-
- // @private
- onDisable: function() {
- this.fieldEl.dom.disabled = true;
- },
-
- // @private
- initValue: function() {
- this.setValue(this.value || '', true);
-
- /**
- * The original value of the field as configured in the {@link #value} configuration, or
- * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
- * setting is <code>true</code>.
- * @type mixed
- * @property originalValue
- */
- this.originalValue = this.getValue();
- },
-
- /**
- * <p>Returns true if the value of this Field has been changed from its original value.
- * Will return false if the field is disabled or has not been rendered yet.</p>
- * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
- * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
- * then the <i>original value</i> is updated when the values are loaded by
- * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
- * @return {Boolean} True if this field has been changed from its original value (and
- * is not disabled), false otherwise.
- */
- isDirty: function() {
- if (this.disabled || !this.rendered) {
- return false;
- }
-
- return String(this.getValue()) !== String(this.originalValue);
- },
-
- // @private
- onMaskTap: function(e) {
- if (this.disabled) {
- return false;
- }
-
- if (Ext.is.iOS && e.browserEvent && !e.browserEvent.isSimulated && !e.browserEvent.isSimulated) {
- console.log('onMaskTap prevented');
- e.preventDefault();
- e.stopPropagation();
- return false;
- }
-
- return true;
- },
-
- // @private
- showMask: function(e) {
- if (this.mask) {
- this.mask.setStyle('display', 'block');
- }
- },
-
- hideMask: function(e) {
- if (this.mask) {
- this.mask.setStyle('display', 'none');
- }
- },
-
- /**
- * Resets the current field value to the originally loaded value and clears any validation messages.
- * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
- */
- reset: function() {
- this.setValue(this.originalValue);
- },
-
- /**
- * Returns the field data value
- * @return {Mixed} value The field value
- */
- getValue: function(){
- if (!this.rendered || !this.fieldEl) {
- return this.value;
- }
-
- return this.fieldEl.getValue();
- },
-
- /**
- * Set the field data value
- * @param {Mixed} value The value to set
- * @return {Ext.form.Field} this
- */
- setValue: function(value){
- this.value = value;
-
- if (this.rendered && this.fieldEl) {
- this.fieldEl.dom.value = (Ext.isEmpty(value) ? '' : value);
- }
-
- return this;
- },
-
- /**
- * Set the label width
- * @param {Mixed} width The width of the label, can be any valid CSS size. E.g '20%', '6em', '100px'
- * @return {Ext.form.Field} this
- */
- setLabelWidth: function(width) {
- if (this.labelEl) {
- this.labelEl.setWidth(width);
- }
-
- return this;
- }
-});
-
-Ext.reg('field', Ext.form.Field);
-
-/**
- * @class Ext.form.Slider
- * @extends Ext.form.Field
- * <p>Form component allowing a user to move a 'thumb' along a slider axis to choose a value. Sliders can equally be used outside
- * of the context of a form. Example usage:</p>
- <pre><code>
-new Ext.form.FormPanel({
- items: [
- {
- xtype : 'sliderfield',
- label : 'Volume',
- value : 5,
- minValue: 0,
- maxValue: 10
- }
- ]
-});
- </code></pre>
- * Or as a standalone component:
- <pre><code>
-var slider = new Ext.form.Slider({
- value: 5,
- minValue: 0,
- maxValue: 10
-});
-
-slider.setValue(8); //will update the value and move the thumb;
-slider.getValue(); //returns 8
- </code></pre>
- * @xtype sliderfield
- */
-Ext.form.Slider = Ext.extend(Ext.form.Field, {
- ui: 'slider',
- /**
- * @cfg {Boolean} useClearIcon @hide
- */
-
- /**
- * @cfg {String} inputCls Overrides {@link Ext.form.Field}'s inputCls. Defaults to 'x-slider'
- */
- inputCls: 'x-slider',
-
- inputType: 'slider',
-
- /**
- * @cfg {Number} minValue The lowest value any thumb on this slider can be set to (defaults to 0)
- */
- minValue: 0,
-
- /**
- * @cfg {Number} maxValue The highest value any thumb on this slider can be set to (defaults to 100)
- */
- maxValue: 100,
-
- /**
- * @cfg {Number} animationDuration When set to a number greater than 0, it will be the animation duration in ms, defaults to 200
- */
- animationDuration: 200,
-
- /**
- * @cfg {Number} value The value to initialize the thumb at (defaults to 0)
- */
- value: 0,
-
- /**
- * @private
- * @cfg {Number} trackWidth The current track width. Used when the field is hidden so setValue will continue to work (needs
- * the fieldEls width).
- */
- trackWidth: null,
-
- monitorOrientation: true,
-
- renderTpl: [
- '<tpl if="label">',
- '<div class="x-form-label"><span>{label}</span></div>',
- '</tpl>',
- '<tpl if="fieldEl">',
- '<div id="{inputId}" name="{name}" class="{fieldCls}"',
- '<tpl if="tabIndex">tabIndex="{tabIndex}"</tpl>',
- '<tpl if="style">style="{style}" </tpl>',
- '/></tpl>'
- ],
-
- /**
- * @cfg {Number} increment The increment by which to snap each thumb when its value changes. Defaults to 1. Any thumb movement
- * will be snapped to the nearest value that is a multiple of the increment (e.g. if increment is 10 and the user tries to move
- * the thumb to 67, it will be snapped to 70 instead)
- */
- increment: 1,
-
-
- /**
- * @cfg {Array} values The values to initialize each thumb with. One thumb will be created for each value. This configuration
- * should always be defined but if it is not then it will be treated as though [0] was passed.
- *
- * This is intentionally doc'd as private and is not fully supported/implemented yet.
- * @private
- */
-
- /**
- * @cfg {Array} thumbs Optional array of Ext.form.Slider.Thumb instances. Usually {@link values} should be used instead
- */
-
- // @private
- constructor: function(config) {
- this.addEvents(
- /**
- * @event beforechange
- * Fires before the value of a thumb is changed. Return false to cancel the change
- * @param {Ext.form.Slider} slider The slider instance
- * @param {Ext.form.Slider.Thumb} thumb The thumb instance
- * @param {Number} newValue The value that the thumb will be set to
- * @param {Number} oldValue The previous value
- */
- 'beforechange',
-
- /**
- * @event change
- * Fires when the value of a thumb is changed.
- * @param {Ext.form.Slider} slider The slider instance
- * @param {Ext.form.Slider.Thumb} thumb The thumb instance
- * @param {Number} newValue The value that the thumb will be set to
- * @param {Number} oldValue The previous value
- */
- 'change',
- /**
- * @event drag
- * Fires while the thumb is actively dragging.
- * @param {Ext.form.Slider} slider The slider instance
- * @param {Ext.form.Slider.Thumb} thumb The thumb instance
- * @param {Number} value The value of the thumb.
- */
- 'drag',
- /**
- * @event dragend
- * Fires when the thumb is finished dragging.
- * @param {Ext.form.Slider} slider The slider instance
- * @param {Ext.form.Slider.Thumb} thumb The thumb instance
- * @param {Number} value The value of the thumb.
- */
- 'dragend'
- );
-
- Ext.form.Slider.superclass.constructor.call(this, config);
- },
-
- // @private
- initComponent: function() {
- this.tabIndex = -1;
-
- if (this.increment == 0) {
- this.increment = 1;
- }
-
- this.increment = Math.abs(this.increment);
-
- //TODO: This will be removed once multi-thumb support is in place - at that point a 'values' config will be accepted
- //to create the multiple thumbs
- this.values = [this.value];
-
- Ext.form.Slider.superclass.initComponent.apply(this, arguments);
-
- if (this.thumbs == undefined) {
- var thumbs = [],
- values = this.values,
- length = values.length,
- i,
- Thumb = this.getThumbClass();
-
- for (i = 0; i < length; i++) {
- thumbs[thumbs.length] = new Thumb({
- value: values[i],
- slider: this,
-
- listeners: {
- scope : this,
- drag : this.onDrag,
- dragend: this.onThumbDragEnd
- }
- });
- }
-
- this.thumbs = thumbs;
- }
- },
-
- // @private
- initValue: function() {
- var thumb = this.getThumb();
-
- if (thumb.dragObj) {
- thumb.dragObj.updateBoundary();
- }
-
- Ext.form.Slider.superclass.initValue.apply(this, arguments);
- },
-
- onOrientationChange: function() {
- Ext.form.Slider.superclass.onOrientationChange.apply(this, arguments);
-
- var thumb = this.getThumb();
-
- if (thumb.dragObj) {
- thumb.dragObj.updateBoundary();
- this.moveThumb(thumb, this.getPixelValue(thumb.getValue(), thumb), 0);
- }
- },
-
- getThumbClass: function() {
- return Ext.form.Slider.Thumb;
- },
-
- /**
- * Sets the new value of the slider, constraining it within {@link minValue} and {@link maxValue}, and snapping to the nearest
- * {@link increment} if set
- * @param {Number} value The new value
- * @param {Number} animationDuration Animation duration, 0 for no animation
- * @param {Boolean} moveThumb Whether or not to move the thumb as well. Defaults to true
- * @return {Ext.form.Slider} this This Slider
- */
- setValue: function(value, animationDuration, moveThumb) {
- if (typeof moveThumb == 'undefined') {
- moveThumb = true;
- }
-
- moveThumb = !!moveThumb;
-
- //TODO: this should accept a second argument referencing which thumb to move
- var thumb = this.getThumb(),
- oldValue = thumb.getValue(),
- newValue = this.constrain(value);
-
- if (this.fireEvent('beforechange', this, thumb, newValue, oldValue) !== false) {
- if (moveThumb) {
- this.moveThumb(thumb, this.getPixelValue(newValue, thumb), animationDuration);
- }
-
- thumb.setValue(newValue);
- this.doComponentLayout();
-
- this.fireEvent('change', this, thumb, newValue, oldValue);
- }
-
- return this;
- },
-
- /**
- * @private
- * Takes a desired value of a thumb and returns the nearest snap value. e.g if minValue = 0, maxValue = 100, increment = 10 and we
- * pass a value of 67 here, the returned value will be 70. The returned number is constrained within {@link minValue} and {@link maxValue},
- * so in the above example 68 would be returned if {@link maxValue} was set to 68.
- * @param {Number} value The value to snap
- * @return {Number} The snapped value
- */
- constrain: function(value) {
- var remainder = value % this.increment;
-
- value -= remainder;
-
- if (Math.abs(remainder) >= (this.increment / 2)) {
- value += (remainder > 0) ? this.increment : -this.increment;
- }
-
- value = Math.max(this.minValue, value);
- value = Math.min(this.maxValue, value);
-
- return value;
- },
-
- /**
- * Returns the current value of the Slider's thumb
- * @return {Number} The thumb value
- */
- getValue: function() {
- //TODO: should return values from multiple thumbs
- return this.getThumb().getValue();
- },
-
- /**
- * Returns the Thumb instance bound to this Slider
- * @return {Ext.form.Slider.Thumb} The thumb instance
- */
- getThumb: function() {
- //TODO: This function is implemented this way to make the addition of multi-thumb support simpler. This function
- //should be updated to accept a thumb index
- return this.thumbs[0];
- },
-
- /**
- * @private
- * Maps a pixel value to a slider value. If we have a slider that is 200px wide, where minValue is 100 and maxValue is 500,
- * passing a pixelValue of 38 will return a mapped value of 176
- * @param {Number} pixelValue The pixel value, relative to the left edge of the slider
- * @return {Number} The value based on slider units
- */
- getSliderValue: function(pixelValue, thumb) {
- var trackWidth = thumb.dragObj.offsetBoundary.right,
- range = this.maxValue - this.minValue,
- ratio;
-
- this.trackWidth = (trackWidth > 0) ? trackWidth : this.trackWidth;
- ratio = range / this.trackWidth;
-
- return this.minValue + (ratio * (pixelValue));
- },
-
- /**
- * @private
- * might represent), this returns the pixel on the rendered slider that the thumb should be positioned at
- * @param {Number} value The internal slider value
- * @return {Number} The pixel value, rounded and relative to the left edge of the scroller
- */
- getPixelValue: function(value, thumb) {
- var trackWidth = thumb.dragObj.offsetBoundary.right,
- range = this.maxValue - this.minValue,
- ratio;
-
- this.trackWidth = (trackWidth > 0) ? trackWidth : this.trackWidth;
- ratio = this.trackWidth / range;
-
- return (ratio * (value - this.minValue));
- },
-
- /**
- * @private
- * Creates an Ext.form.Slider.Thumb instance for each configured {@link values value}. Assumes that this.el is already present
- */
- renderThumbs: function() {
- var thumbs = this.thumbs,
- length = thumbs.length,
- i;
-
- for (i = 0; i < length; i++) {
- thumbs[i].render(this.fieldEl);
- }
- },
-
- /**
- * @private
- * Updates a thumb after it has been dragged
- */
- onThumbDragEnd: function(draggable) {
- var value = this.getThumbValue(draggable);
-
- this.setValue(value);
- this.fireEvent('dragend', this, draggable.thumb, this.constrain(value));
- },
-
- /**
- * @private
- * Get the value for a draggable thumb.
- */
- getThumbValue: function(draggable) {
- var thumb = draggable.thumb;
-
- return this.getSliderValue(-draggable.getOffset().x, thumb);
- },
-
- /**
- * @private
- * Fires drag events so the user can interact.
- */
- onDrag: function(draggable){
- var value = this.getThumbValue(draggable);
- this.fireEvent('drag', this, draggable.thumb, this.constrain(value));
- },
-
- /**
- * @private
- * Updates the value of the nearest thumb on tap events
- */
- onTap: function(e) {
- if (!this.disabled) {
- var sliderBox = this.fieldEl.getPageBox(),
- leftOffset = e.pageX - sliderBox.left,
- thumb = this.getNearest(leftOffset),
- halfThumbWidth = thumb.dragObj.size.width / 2;
-
- this.setValue(this.getSliderValue(leftOffset - halfThumbWidth, thumb), this.animationDuration, true);
- }
- },
-
- /**
- * @private
- * Moves the thumb element. Should only ever need to be called from within {@link setValue}
- * @param {Ext.form.Slider.Thumb} thumb The thumb to move
- * @param {Number} pixel The pixel the thumb should be centered on
- * @param {Boolean} animationDuration True to animationDuration the movement
- */
- moveThumb: function(thumb, pixel, animationDuration) {
- thumb.dragObj.setOffset(new Ext.util.Offset(pixel, 0), animationDuration);
- },
-
- // inherit docs
- afterRender: function(ct) {
- var me = this;
-
- me.renderThumbs();
-
- Ext.form.Slider.superclass.afterRender.apply(me, arguments);
-
- me.fieldEl.on({
- scope: me,
- tap : me.onTap
- });
- },
-
- /**
- * @private
- * Finds and returns the nearest {@link Ext.form.Slider.Thumb thumb} to the given value.
- * @param {Number} value The value
- * @return {Ext.form.Slider.Thumb} The nearest thumb
- */
- getNearest: function(value) {
- //TODO: Implemented this way to enable multi-thumb support later
- return this.thumbs[0];
- },
-
- /**
- * @private
- * Loops through each of the sliders {@link #thumbs} and calls disable/enable on each of them depending
- * on the param specified.
- * @param {Boolean} disable True to disable, false to enable
- */
- setThumbsDisabled: function(disable) {
- var thumbs = this.thumbs,
- ln = thumbs.length,
- i = 0;
-
- for (; i < ln; i++) {
- thumbs[i].dragObj[disable ? 'disable' : 'enable']();
- }
- },
-
- /**
- * Disables the slider by calling the internal {@link #setThumbsDisabled} method
- */
- disable: function() {
- Ext.form.Slider.superclass.disable.call(this);
- this.setThumbsDisabled(true);
- },
-
- /**
- * Enables the slider by calling the internal {@link #setThumbsDisabled} method.
- */
- enable: function() {
- Ext.form.Slider.superclass.enable.call(this);
- this.setThumbsDisabled(false);
- }
-});
-
-Ext.reg('sliderfield', Ext.form.Slider);
-
-/**
- * @class Ext.form.Slider.Thumb
- * @extends Ext.form.Field
- * @xtype thumb
- * @ignore
- * Utility class used by Ext.form.Slider - should never need to be used directly.
- */
-Ext.form.Slider.Thumb = Ext.extend(Ext.form.Field, {
- isField: false,
- baseCls: 'x-thumb',
- autoCreateField: false,
- draggable: true,
-
- /**
- * @cfg {Number} value The value to initialize this thumb with (defaults to 0)
- */
- value: 0,
-
- /**
- * @cfg {Ext.form.Slider} slider The Slider that this thumb is attached to. Required
- */
-
- // inherit docs
- onRender: function() {
- this.draggable = {
- direction: 'horizontal',
- constrain: this.slider.fieldEl,
- revert: false,
- thumb: this
- };
-
- Ext.form.Slider.Thumb.superclass.onRender.apply(this, arguments);
- },
-
- // inherit docs
- setValue: function(newValue) {
- this.value = newValue;
-
- return this;
- },
-
- // inherit docs
- getValue: function() {
- return this.value;
- }
-});
-
-Ext.reg('sliderthumb', Ext.form.Slider.Thumb);
-
-
-/**
- * @class Ext.form.Toggle
- * @extends Ext.form.Slider
- * <p>Specialized Slider with a single thumb and only two values. By default the toggle component can
- * be switched between the values of 0 and 1.</p>
- * @xtype togglefield
- */
-Ext.form.Toggle = Ext.extend(Ext.form.Slider, {
- minValue: 0,
-
- maxValue: 1,
-
- ui: 'toggle',
-
- inputType: 'toggle',
-
- /**
- * @cfg {Boolean} useClearIcon @hide
- */
-
- /**
- * @cfg {String} minValueCls CSS class added to the field when toggled to its minValue
- */
- minValueCls: 'x-toggle-off',
-
- /**
- * @cfg {String} maxValueCls CSS class added to the field when toggled to its maxValue
- */
- maxValueCls: 'x-toggle-on',
-
- // Inherited
- animationDuration: 70,
-
- /**
- * Toggles between the minValue (0 by default) and the maxValue (1 by default)
- */
- toggle: function() {
- var thumb = this.thumbs[0],
- value = thumb.getValue();
-
- this.setValue(value == this.minValue ? this.maxValue : this.minValue, this.animationDuration);
- },
-
- // inherit docs
- setValue: function(value) {
- Ext.form.Toggle.superclass.setValue.call(this, value, this.animationDuration);
-
- var fieldEl = this.fieldEl;
-
- if (this.constrain(value) === this.minValue) {
- fieldEl.addCls(this.minValueCls);
- fieldEl.removeCls(this.maxValueCls);
- }
- else {
- fieldEl.addCls(this.maxValueCls);
- fieldEl.removeCls(this.minValueCls);
- }
- },
-
- /**
- * @private
- * Listener to the tap event, just toggles the value
- */
- onTap: function() {
- if (!this.disabled) {
- this.toggle();
- }
- },
-
- getThumbClass: function() {
- return Ext.form.Toggle.Thumb;
- }
-});
-
-Ext.reg('togglefield', Ext.form.Toggle);
-
-
-/**
- * @class Ext.form.Toggle.Thumb
- * @extends Ext.form.Slider.Thumb
- * @private
- * @ignore
- */
-Ext.form.Toggle.Thumb = Ext.extend(Ext.form.Slider.Thumb, {
- onRender: function() {
- Ext.form.Toggle.Thumb.superclass.onRender.apply(this, arguments);
- Ext.DomHelper.append(this.el, [{
- cls: 'x-toggle-thumb-off',
- html: '<span>OFF</span>'
- },{
- cls: 'x-toggle-thumb-on',
- html: '<span>ON</span>'
- },{
- cls: 'x-toggle-thumb-thumb'
- }]);
- }
-});
-/**
- * @class Ext.form.Text
- * @extends Ext.form.Field
- * <p>Simple text input field. See {@link Ext.form.FormPanel FormPanel} for example usage.</p>
- * @xtype textfield
- */
-Ext.form.Text = Ext.extend(Ext.form.Field, {
- ui: 'text',
-
- /**
- * @cfg {String} focusCls The CSS class to use when the field receives focus (defaults to 'x-field-focus')
- */
- focusCls: 'x-field-focus',
-
- /**
- * @cfg {Integer} maxLength The maximum number of permitted input characters (defaults to 0).
- */
- maxLength: 0,
-
- /**
- * @cfg {String} placeHolder A string value displayed in the input (if supported) when the control is empty.
- */
- placeHolder: undefined,
-
- /**
- * True to set the field's DOM element autocomplete attribute to "on", false to set to "off". Defaults to undefined, leaving the attribute unset
- * @cfg {Boolean} autoComplete
- */
- autoComplete: undefined,
-
- /**
- * True to set the field's DOM element autocapitalize attribute to "on", false to set to "off". Defaults to undefined, leaving the attribute unset
- * @cfg {Boolean} autoCapitalize
- */
- autoCapitalize: undefined,
-
- /**
- * True to set the field DOM element autocorrect attribute to "on", false to set to "off". Defaults to undefined, leaving the attribute unset.
- * @cfg {Boolean} autoCorrect
- */
- autoCorrect: undefined,
-
- /**
- * @cfg {Integer} maxLength Maximum number of character permit by the input.
- */
-
- /**
- * @property {Boolean} <tt>True</tt> if the field currently has focus.
- */
- isFocused: false,
-
- // @private
- isClearIconVisible: false,
-
- useMask: Ext.is.iOS,
-
- initComponent: function() {
- this.addEvents(
- /**
- * @event focus
- * Fires when this field receives input focus.
- * @param {Ext.form.Text} this This field
- * @param {Ext.EventObject} e
- */
- 'focus',
- /**
- * @event blur
- * Fires when this field loses input focus.
- * @param {Ext.form.Text} this This field
- * @param {Ext.EventObject} e
- */
- 'blur',
- /**
- * @event keyup
- * Fires when a key is released on the input element.
- * @param {Ext.form.Text} this This field
- * @param {Ext.EventObject} e
- */
- 'keyup',
- /**
- * @event change
- * Fires just before the field blurs if the field value has changed.
- * @param {Ext.form.Text} this This field
- * @param {Mixed} newValue The new value
- * @param {Mixed} oldValue The original value
- */
- 'change',
- /**
- * @event action
- * Fires whenever the return key or go is pressed. FormPanel listeners
- * for this event, and submits itself whenever it fires. Also note
- * that this event bubbles up to parent containers.
- * @param {Ext.form.Text} this This field
- * @param {Mixed} e The key event object
- */
- 'action'
- );
-
- this.enableBubble('action');
-
- Ext.form.Text.superclass.initComponent.apply(this, arguments);
- },
-
- applyRenderSelectors: function() {
- this.renderSelectors = Ext.applyIf(this.renderSelectors || {}, {
- clearIconEl: '.x-field-clear',
- clearIconContainerEl: '.x-field-clear-container'
- });
-
- Ext.form.Text.superclass.applyRenderSelectors.call(this);
- },
-
- initRenderData: function() {
- var renderData = Ext.form.Text.superclass.initRenderData.call(this),
- autoComplete = this.autoComplete,
- autoCapitalize = this.autoCapitalize,
- autoCorrect = this.autoCorrect;
-
- Ext.applyIf(renderData, {
- placeHolder : this.placeHolder,
- maxlength : this.maxLength,
- useClearIcon : this.useClearIcon
- });
-
- var testArray = [true, 'on'];
-
- if (autoComplete !== undefined) {
- renderData.autoComplete = (testArray.indexOf(autoComplete) !== -1) ? 'on': 'off';
- }
-
- if (autoCapitalize !== undefined) {
- renderData.autoCapitalize = (testArray.indexOf(autoCapitalize) !== -1) ? 'on': 'off';
- }
-
- if (autoCorrect !== undefined) {
- renderData.autoCorrect = (testArray.indexOf(autoCorrect) !== -1) ? 'on': 'off';
- }
-
- this.renderData = renderData;
-
- return renderData;
- },
-
- initEvents: function() {
- Ext.form.Text.superclass.initEvents.call(this);
-
- if (this.fieldEl) {
- this.mon(this.fieldEl, {
- focus: this.onFocus,
- blur: this.onBlur,
- keyup: this.onKeyUp,
- paste: this.updateClearIconVisibility,
- mousedown: this.onBeforeFocus,
- scope: this
- });
-
- if (this.clearIconEl){
- this.mon(this.clearIconContainerEl, {
- scope: this,
- tap: this.onClearIconTap
- });
- }
- }
- },
-
- // @private
- onEnable: function() {
- Ext.form.Text.superclass.onEnable.apply(this, arguments);
-
- this.disabled = false;
-
- this.updateClearIconVisibility();
- },
-
- // @private
- onDisable: function() {
- Ext.form.Text.superclass.onDisable.apply(this, arguments);
-
- this.blur();
-
- this.hideClearIcon();
- },
-
- // @private
- onClearIconTap: function() {
- if (!this.disabled) {
- this.setValue('');
- }
- },
-
- // @private
- updateClearIconVisibility: function() {
- var value = this.getValue();
-
- if (!value) {
- value = '';
- }
-
- if (value.length < 1){
- this.hideClearIcon();
- }
- else {
- this.showClearIcon();
- }
-
- return this;
- },
-
- // @private
- showClearIcon: function() {
- if (!this.disabled && this.fieldEl && this.clearIconEl && !this.isClearIconVisible) {
- this.isClearIconVisible = true;
- this.fieldEl.addCls('x-field-clearable');
- this.clearIconEl.removeCls('x-hidden-visibility');
- }
-
- return this;
- },
-
- // @private
- hideClearIcon: function() {
- if (this.fieldEl && this.clearIconEl && this.isClearIconVisible) {
- this.isClearIconVisible = false;
- this.fieldEl.removeCls('x-field-clearable');
- this.clearIconEl.addCls('x-hidden-visibility');
- }
-
- return this;
- },
-
- // @private
- afterRender: function() {
- Ext.form.Text.superclass.afterRender.call(this);
- this.updateClearIconVisibility();
- },
- // @private
- onBeforeFocus: function(e) {
- this.fireEvent('beforefocus', e);
- },
-
- // @private
- beforeFocus: Ext.emptyFn,
-
- // @private
- onMaskTap: function(e) {
- if (Ext.form.Text.superclass.onMaskTap.apply(this, arguments) !== true) {
- return false;
- }
-
- this.maskCorrectionTimer = Ext.defer(this.showMask, 1000, this);
- this.hideMask();
- },
-
- // @private
- onFocus: function(e) {
- if (this.mask) {
- if (this.maskCorrectionTimer) {
- clearTimeout(this.maskCorrectionTimer);
- }
-
- this.hideMask();
- }
-
- this.beforeFocus();
-
- if (this.focusCls) {
- this.el.addCls(this.focusCls);
- }
-
- if (!this.isFocused) {
- this.isFocused = true;
- /**
- * <p>The value that the Field had at the time it was last focused. This is the value that is passed
- * to the {@link #change} event which is fired if the value has been changed when the Field is blurred.</p>
- * <p><b>This will be undefined until the Field has been visited.</b> Compare {@link #originalValue}.</p>
- * @type mixed
- * @property startValue
- */
- this.startValue = this.getValue();
- this.fireEvent('focus', this, e);
- }
-
- Ext.currentlyFocusedField = this;
- },
-
- // @private
- beforeBlur: Ext.emptyFn,
-
- // @private
- onBlur: function(e) {
- this.beforeBlur();
-
- if (this.focusCls) {
- this.el.removeCls(this.focusCls);
- }
-
- this.isFocused = false;
-
- var value = this.getValue();
-
- if (String(value) != String(this.startValue)){
- this.fireEvent('change', this, value, this.startValue);
- }
-
- this.fireEvent('blur', this, e);
-
- this.updateClearIconVisibility();
-
- this.showMask();
-
- this.afterBlur();
-
- Ext.currentlyFocusedField = null;
- },
-
- // @private
- afterBlur: Ext.emptyFn,
-
- /**
- * Attempts to set the field as the active input focus.
- * @return {Ext.form.Text} this
- */
- focus: function(){
- if (this.rendered && this.fieldEl && this.fieldEl.dom.focus) {
- this.fieldEl.dom.focus();
- }
-
- return this;
- },
-
- /**
- * Attempts to forcefully blur input focus for the field.
- * @return {Ext.form.Text} this
- */
- blur: function(){
- if(this.rendered && this.fieldEl && this.fieldEl.dom.blur) {
- this.fieldEl.dom.blur();
- }
- return this;
- },
-
- // Inherited docs
- setValue: function() {
- Ext.form.Text.superclass.setValue.apply(this, arguments);
-
- this.updateClearIconVisibility();
- },
-
- // @private
- onKeyUp: function(e) {
- this.updateClearIconVisibility();
-
- this.fireEvent('keyup', this, e);
-
- if (e.browserEvent.keyCode === 13) {
- this.blur();
- this.fireEvent('action', this, e);
- }
- }
-});
-
-Ext.reg('textfield', Ext.form.Text);
-
-/**
- * @class Ext.form.TextField
- * @extends Ext.form.Text
- * @private
- * @hidden
- * DEPRECATED - remove this in 1.0. See RC1 Release Notes for details
- */
-Ext.form.TextField = Ext.extend(Ext.form.Text, {
- constructor: function() {
- console.warn("Ext.form.TextField has been deprecated and will be removed in Sencha Touch 1.0. Please use Ext.form.Text instead");
- Ext.form.TextField.superclass.constructor.apply(this, arguments);
- }
-});
-
-/**
- * @class Ext.form.Password
- * @extends Ext.form.Text
- * <p>Wraps an HTML5 password field. See {@link Ext.form.FormPanel FormPanel} for example usage.</p>
- * @xtype passwordfield
- */
-Ext.form.Password = Ext.extend(Ext.form.Text, {
- inputType: 'password',
- autoCapitalize : false
-});
-
-Ext.reg('passwordfield', Ext.form.Password);
-
-/**
- * @class Ext.form.Email
- * @extends Ext.form.Text
- * <p>Wraps an HTML5 email field. See {@link Ext.form.FormPanel FormPanel} for example usage.</p>
- * @xtype emailfield
- */
-Ext.form.Email = Ext.extend(Ext.form.Text, {
- inputType: 'email',
-
- autoCapitalize: false
-});
-
-Ext.reg('emailfield', Ext.form.Email);
-
-/**
- * @class Ext.form.Url
- * @extends Ext.form.Text
- * Wraps an HTML5 url field. See {@link Ext.form.FormPanel FormPanel} for example usage.
- * @xtype urlfield
- */
-Ext.form.Url = Ext.extend(Ext.form.Text, {
- inputType: 'url',
-
- autoCapitalize: false
-});
-
-Ext.reg('urlfield', Ext.form.Url);
-
-/**
- * @class Ext.form.Search
- * @extends Ext.form.Text
- * Wraps an HTML5 search field. See {@link Ext.form.FormPanel FormPanel} for example usage.
- * @xtype searchfield
- */
-Ext.form.Search = Ext.extend(Ext.form.Text, {
- inputType: 'search'
- /**
- * @cfg {Boolean} useClearIcon @hide
- */
-});
-
-Ext.reg('searchfield', Ext.form.Search);
-
-/**
- * @class Ext.form.Number
- * @extends Ext.form.Text
- * <p>Wraps an HTML5 number field. See {@link Ext.form.FormPanel FormPanel} for example usage.</p>
- * @xtype numberfield
- */
-Ext.form.Number = Ext.extend(Ext.form.Text, {
- ui: 'number',
-
- inputType: 'number',
-
- minValue : undefined,
-
- maxValue : undefined,
-
- stepValue : undefined,
-
- renderTpl: [
- '<tpl if="label"><div class="x-form-label"><span>{label}</span></div></tpl>',
- '<tpl if="fieldEl"><div class="x-form-field-container">',
- '<input id="{inputId}" type="{inputType}" name="{name}" class="{fieldCls}"',
- '<tpl if="tabIndex">tabIndex="{tabIndex}" </tpl>',
- '<tpl if="placeHolder">placeholder="{placeHolder}" </tpl>',
- '<tpl if="style">style="{style}" </tpl>',
- '<tpl if="minValue != undefined">min="{minValue}" </tpl>',
- '<tpl if="maxValue != undefined">max="{maxValue}" </tpl>',
- '<tpl if="stepValue != undefined">step="{stepValue}" </tpl>',
- '<tpl if="autoComplete">autocomplete="{autoComplete}" </tpl>',
- '<tpl if="autoCapitalize">autocapitalize="{autoCapitalize}" </tpl>',
- '<tpl if="autoFocus">autofocus="{autoFocus}" </tpl>',
- '/>',
- '<tpl if="useMask"><div class="x-field-mask"></div></tpl>',
- '</div></tpl>',
- '<tpl if="useClearIcon"><div class="x-field-clear-container"><div class="x-field-clear x-hidden-visibility">×</div><div></tpl>'
- ],
-
- // @private
- onRender : function() {
- Ext.apply(this.renderData, {
- maxValue : this.maxValue,
- minValue : this.minValue,
- stepValue : this.stepValue
- });
-
- Ext.form.Number.superclass.onRender.apply(this, arguments);
- }
-});
-
-Ext.reg('numberfield', Ext.form.Number);
-
-
-/**
- * @class Ext.form.Spinner
- * @extends Ext.form.Number
- * <p>Wraps an HTML5 number field. Example usage:
- * <pre><code>
-new Ext.form.Spinner({
- minValue: 0,
- maxValue: 100,
- incrementValue: 2,
- cycle: true
-});
-</code></pre>
- * @xtype spinnerfield
- */
-Ext.form.Spinner = Ext.extend(Ext.form.Number, {
-
- /**
- * @cfg {Boolean} useClearIcon @hide
- */
- componentCls: 'x-spinner',
-
- /**
- * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
- */
- minValue: Number.NEGATIVE_INFINITY,
- /**
- * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
- */
- maxValue: Number.MAX_VALUE,
- /**
- * @cfg {Number} incrementValue Value that is added or subtracted from the current value when a spinner is used.
- * Defaults to <tt>1</tt>.
- */
- incrementValue: 1,
- /**
- * @cfg {Boolean} accelerateOnTapHold True if autorepeating should start slowly and accelerate.
- * Defaults to <tt>true</tt>.
- */
- accelerateOnTapHold: true,
-
- // @private
- defaultValue: 0,
-
- /**
- * @cfg {Boolean} cycle When set to true, it will loop the values of a minimum or maximum is reached.
- * If the maximum value is reached, the value will be set to the minimum.
- * If the minimum value is reached, the value will be set to the maximum.
- * Defaults to <tt>false</tt>.
- */
- cycle: false,
-
- /**
- * @cfg {Boolean} disableInput True to disable the input field, meaning that only the spinner buttons
- * can be used. Defaults to <tt>false</tt>.
- */
- disableInput: false,
-
- /**
- * @cfg {Boolean} useClearIcon @hide
- */
- useClearIcon: false,
-
- /**
- * @cfg {Boolean} autoCapitalize @hide
- */
- autoCapitalize: false,
-
- renderTpl: [
- '<tpl if="label"><div class="x-form-label"><span>{label}</span></div></tpl>',
- '<tpl if="fieldEl">',
- '<div class="{componentCls}-body">',
- '<div class="{componentCls}-down"><span>-</span></div>',
- '<div class="x-form-field-container">',
- '<input id="{inputId}" type="{type}" name="{name}" class="{fieldCls}"',
- '<tpl if="tabIndex">tabIndex="{tabIndex}" </tpl>',
- '<tpl if="placeHolder">placeholder="{placeHolder}" </tpl>',
- '<tpl if="style">style="{style}" </tpl>',
- '<tpl if="minValue != undefined">min="{minValue}" </tpl>',
- '<tpl if="maxValue != undefined">max="{maxValue}" </tpl>',
- '<tpl if="stepValue != undefined">step="{stepValue}" </tpl>',
- '<tpl if="autoComplete">autocomplete="{autoComplete}" </tpl>',
- '<tpl if="autoFocus">autofocus="{autoFocus}" </tpl>',
- '/>',
- '<tpl if="useMask"><div class="x-field-mask"></div></tpl>',
- '</div>',
- '<div class="{componentCls}-up"><span>+</span></div>',
- '</div>',
- '</tpl>'
- ],
-
- initComponent: function() {
-
- this.addEvents(
- /**
- * @event spin
- * Fires when the value is changed via either spinner buttons
- * @param {Ext.form.Spinner} this
- * @param {Number} value
- * @param {String} direction 'up' or 'down'
- */
- 'spin',
- /**
- * @event spindown
- * Fires when the value is changed via the spinner down button
- * @param {Ext.form.Spinner} this
- * @param {Number} value
- */
- 'spindown',
- /**
- * @event spinup
- * Fires when the value is changed via the spinner up button
- * @param {Ext.form.Spinner} this
- * @param {Number} value
- */
- 'spinup'
- );
-
- Ext.form.Spinner.superclass.initComponent.call(this);
- },
-
- // @private
- onRender: function() {
- this.renderData.disableInput = this.disableInput;
-
- Ext.applyIf(this.renderSelectors, {
- spinUpEl: '.x-spinner-up',
- spinDownEl: '.x-spinner-down'
- });
-
- Ext.form.Spinner.superclass.onRender.apply(this, arguments);
-
- this.downRepeater = this.createRepeater(this.spinDownEl, this.onSpinDown);
- this.upRepeater = this.createRepeater(this.spinUpEl, this.onSpinUp);
- },
-
- initValue: function() {
- if (isNaN(this.defaultValue)) {
- this.defaultValue = 0;
- }
-
- if (!this.value) {
- this.value = this.defaultValue;
- }
-
- Ext.form.Spinner.superclass.initValue.apply(this, arguments);
- },
-
- // @private
- createRepeater: function(el, fn){
- var repeat = new Ext.util.TapRepeater(el, {
- accelerate: this.accelerateOnTapHold
- });
-
- this.mon(repeat, {
- tap: fn,
- touchstart: this.onTouchStart,
- touchend: this.onTouchEnd,
- preventDefault: true,
- scope: this
- });
-
- return repeat;
- },
-
- // @private
- onSpinDown: function() {
- if (!this.disabled) {
- this.spin(true);
- }
- },
-
- // @private
- onSpinUp: function() {
- if (!this.disabled) {
- this.spin(false);
- }
- },
-
- onKeyUp: function(e) {
-// var value = parseInt(this.getValue());
-//
-// if (isNaN(value)) {
-// value = this.defaultValue;
-// }
-//
-// this.setValue(value);
-
- Ext.form.Spinner.superclass.onKeyUp.apply(this, arguments);
- },
-
- // @private
- onTouchStart: function(btn) {
- if (!this.disabled) {
- btn.el.addCls('x-button-pressed');
- }
- },
-
- // @private
- onTouchEnd: function(btn) {
- btn.el.removeCls('x-button-pressed');
- },
-
- setValue: function(value) {
- value = parseFloat(value);
-
- if (isNaN(value)) {
- value = this.defaultValue;
- }
-
- Ext.form.Spinner.superclass.setValue.call(this, value);
- },
-
- // @private
- spin: function(down) {
- var value = parseFloat(this.getValue()),
- increment = this.incrementValue,
- cycle = this.cycle,
- min = this.minValue,
- max = this.maxValue,
- direction = down ? 'down': 'up';
-
- if (down){
- value -= increment;
- }
- else{
- value += increment;
- }
-
- value = (isNaN(value)) ? this.defaultValue: value;
-
- if (value < min) {
- value = cycle ? max: min;
- }
- else if (value > max) {
- value = cycle ? min: max;
- }
-
- this.setValue(value);
-
- this.fireEvent('spin' + direction, this, value);
- this.fireEvent('spin', this, value, direction);
- },
-
- // @private
- destroy: function() {
- Ext.destroy(this.downRepeater, this.upRepeater);
- Ext.form.Spinner.superclass.destroy.call(this, arguments);
- }
-});
-
-Ext.reg('spinnerfield', Ext.form.Spinner);
-
-/**
- * @class Ext.form.Hidden
- * @extends Ext.form.Field
- * <p>Wraps a hidden field. See {@link Ext.form.FormPanel FormPanel} for example usage.</p>
- * @xtype hiddenfield
- */
-Ext.form.Hidden = Ext.extend(Ext.form.Field, {
- ui: 'hidden',
-
- inputType: 'hidden',
-
- tabIndex: -1
-});
-
-Ext.reg('hiddenfield', Ext.form.Hidden);
-
-
-/**
- * @class Ext.form.HiddenField
- * @extends Ext.form.Hidden
- * @private
- * @hidden
- * DEPRECATED - remove this in 1.0. See RC1 Release Notes for details
- */
-Ext.form.HiddenField = Ext.extend(Ext.form.Hidden, {
-
- constructor: function() {
- console.warn("Ext.form.HiddenField has been deprecated and will be removed in Sencha Touch 1.0. Please use Ext.form.Hidden instead");
- Ext.form.HiddenField.superclass.constructor.apply(this, arguments);
- }
-});
-
-/**
- * @class Ext.form.Checkbox
- * @extends Ext.form.Field
- * Simple Checkbox class. Can be used as a direct replacement for traditional checkbox fields.
- * @constructor
- * @param {Object} config Optional config object
- * @xtype checkboxfield
- */
-Ext.form.Checkbox = Ext.extend(Ext.form.Field, {
- ui: 'checkbox',
-
- inputType: 'checkbox',
-
- /**
- * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
- */
- checked: false,
-
- /**
- * @cfg {String} value The string value to submit if the item is in a checked state.
- */
- value: '',
-
- // @private
- constructor: function(config) {
- this.addEvents(
- /**
- * @event check
- * Fires when the checkbox is checked.
- * @param {Ext.form.Checkbox} this This checkbox
- */
- 'check',
-
- /**
- * @event uncheck
- * Fires when the checkbox is unchecked.
- * @param {Ext.form.Checkbox} this This checkbox
- */
- 'uncheck'
- );
-
- Ext.form.Checkbox.superclass.constructor.call(this, config);
- },
-
- renderTpl: [
- '<tpl if="label"><div class="x-form-label"><span>{label}</span></div></tpl>',
- '<tpl if="fieldEl"><input id="{inputId}" type="{inputType}" name="{name}" class="{fieldCls}" tabIndex="-1" ',
- '<tpl if="checked"> checked </tpl>',
- '<tpl if="style">style="{style}" </tpl> value="{inputValue}" />',
- '</tpl>'
- ],
-
- // @private
- onRender: function() {
- var isChecked = this.getBooleanIsChecked(this.checked);
-
- Ext.apply(this.renderData, {
- inputValue : String(this.value),
- checked : isChecked
- });
-
- Ext.form.Checkbox.superclass.onRender.apply(this, arguments);
-
- if (this.fieldEl) {
- this.mon(this.fieldEl, {
- click: this.onChange,
- scope: this
- });
-
- this.setChecked(isChecked);
- this.originalState = this.isChecked();
- }
- },
-
- // @private
- onChange: function(e) {
- if (e) {
- if (e.browserEvent) {
- e = e.browserEvent;
- }
-
- if (!e.isSimulated) {
- e.preventDefault();
- e.stopPropagation();
- return;
- }
- }
-
- if (this.isChecked()) {
- this.fireEvent('check', this);
- } else {
- this.fireEvent('uncheck', this);
- }
- },
-
- /**
- * Returns the checked state of the checkbox.
- * @return {Boolean} True if checked, else otherwise
- */
- isChecked: function() {
- if (this.rendered) {
- return this.fieldEl.dom.checked || false;
- } else {
- return !!this.checked;
- }
- },
-
- /**
- * Set the checked state of the checkbox.
- * @return {Ext.form.Checkbox} this This checkbox
- */
- setChecked: function(checked) {
- var newState = this.getBooleanIsChecked(checked),
- rendered = this.rendered,
- currentState,
- field;
-
- if (rendered) {
- field = this.fieldEl.dom;
- currentState = field.checked;
- } else {
- currentState = !!this.checked;
- }
-
- if (currentState != newState) {
- if (rendered) {
- field.checked = newState;
- } else {
- this.checked = newState;
- }
- this.onChange();
- }
- return this;
- },
-
- /**
- * Set the checked state of the checkbox to true
- * @return {Ext.form.Checkbox} this This checkbox
- */
- check: function() {
- return this.setChecked(true);
- },
-
- /**
- * Set the checked state of the checkbox to false
- * @return {Ext.form.Checkbox} this This checkbox
- */
- uncheck: function() {
- return this.setChecked(false);
- },
-
- // Inherited
- reset: function() {
- Ext.form.Checkbox.superclass.reset.apply(this, arguments);
-
- this.setChecked(this.originalState);
-
- return this;
- },
-
- //@private
- getBooleanIsChecked: function(value) {
- return /^(true|1|on)/i.test(String(value));
- },
-
- getSameGroupFields: function() {
- var parent = this.el.up('form'),
- formComponent = Ext.getCmp(parent.id),
- fields = [];
-
- if (formComponent) {
- fields = formComponent.getFields(this.getName());
- }
-
- return fields;
- },
-
- /**
- * Returns an array of values from the checkboxes in the group that are checked,
- * @return {Array}
- */
- getGroupValues: function() {
- var values = [];
-
- this.getSameGroupFields().forEach(function(field) {
- if (field.isChecked()) {
- values.push(field.getValue());
- }
- });
-
- return values;
- },
-
- /**
- * Set the status of all matched checkboxes in the same group to checked
- * @param {Array} values An array of values
- * @return {Ext.form.Checkbox} This checkbox
- */
- setGroupValues: function(values) {
- this.getSameGroupFields().forEach(function(field) {
- field.setChecked((values.indexOf(field.getValue()) !== -1));
- });
-
- return this;
- },
-
- //Inherited docs
- setValue: function(value) {
- value = String(value);
-
- Ext.form.Checkbox.superclass.setValue.call(this, value);
- }
-});
-
-Ext.reg('checkboxfield', Ext.form.Checkbox);
-
-/**
- * @class Ext.form.Radio
- * @extends Ext.form.Checkbox
- * <p>Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
- * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.</p>
- * @constructor
- * Creates a new Radio
- * @param {Object} config Configuration options
- * @xtype radiofield
- */
-Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
- inputType: 'radio',
-
- ui: 'radio',
-
- /**
- * @cfg {Boolean} useClearIcon @hide
- */
-
- /**
- * Returns the selected value if this radio is part of a group (other radio fields with the same name, in the same FormPanel),
- * @return {String}
- */
- getGroupValue: function() {
- var field,
- fields = this.getSameGroupFields();
-
- for (var i=0; i<fields.length; i++) {
- field = fields[i];
-
- if (field.isChecked()) {
- return field.getValue();
- }
- }
-
- return null;
- },
-
- /**
- * Set the matched radio field's status (that has the same value as the given string) to checked
- * @param {String} value The value of the radio field to check
- * @return {String}
- */
- setGroupValue: function(value) {
- var field,
- fields = this.getSameGroupFields(),
- i = 0,
- len = fields.length;
-
- for (; i < len; i++) {
- field = fields[i];
-
- if (field.getValue() == value) {
- field.check();
- return;
- }
- }
- }
-});
-
-Ext.reg('radiofield', Ext.form.Radio);
-
-/**
- * @class Ext.form.Select
- * @extends Ext.form.Text
- * Simple Select field wrapper. Example usage:
-<pre><code>
-new Ext.form.Select({
- options: [
- {text: 'First Option', value: 'first'},
- {text: 'Second Option', value: 'second'},
- {text: 'Third Option', value: 'third'}
- ]
-});
-</code></pre>
- * @xtype selectfield
- */
-Ext.form.Select = Ext.extend(Ext.form.Text, {
- ui: 'select',
- /**
- * @cfg {Boolean} useClearIcon @hide
- */
-
- /**
- * @cfg {String/Integer} valueField The underlying {@link Ext.data.Field#name data value name} (or numeric Array index) to bind to this
- * Select control. (defaults to 'value')
- */
- valueField: 'value',
-
- /**
- * @cfg {String/Integer} displayField The underlying {@link Ext.data.Field#name data value name} (or numeric Array index) to bind to this
- * Select control. This resolved value is the visibly rendered value of the available selection options.
- * (defaults to 'text')
- */
- displayField: 'text',
-
- /**
- * @cfg {Ext.data.Store} store (Optional) store instance used to provide selection options data.
- */
-
- /**
- * @cfg {Array} options (Optional) An array of select options.
-<pre><code>
- [
- {text: 'First Option', value: 'first'},
- {text: 'Second Option', value: 'second'},
- {text: 'Third Option', value: 'third'}
- ]
-</code></pre>
- * Note: option object member names should correspond with defined {@link #valueField valueField} and {@link #displayField displayField} values.
- * This config will be ignore if a {@link #store store} instance is provided
- */
-
- /**
- * @cfg {String} hiddenName Specify a hiddenName if you're using the {@link Ext.form.FormPanel#standardSubmit standardSubmit} option.
- * This name will be used to post the underlying value of the select to the server.
- */
-
- // @cfg {Number} tabIndex @hide
- tabIndex: -1,
-
- // @cfg {Boolean} useMask @hide
- useMask: true,
-
- monitorOrientation: true,
-
- // @private
- initComponent: function() {
- var options = this.options;
-
- if (this.store) {
- this.store = Ext.StoreMgr.lookup(this.store);
- }
- else {
- this.store = new Ext.data.Store({
- fields: [this.valueField, this.displayField]
- });
-
- if (options && Ext.isArray(options) && options.length > 0) {
- this.setOptions(this.options);
- }
- }
-
- Ext.form.Select.superclass.initComponent.call(this);
-
- this.addEvents(
- /**
- * @event change
- * Fires when an option selection has changed
- * @param {Ext.form.Select} this
- * @param {Mixed} value
- */
- 'change'
- );
- },
-
- // @private
- onRender: function(){
- Ext.form.Select.superclass.onRender.apply(this, arguments);
-
- var name = this.hiddenName;
- if (name) {
- this.hiddenField = this.el.insertSibling({
- name: name,
- tag: 'input',
- type: 'hidden'
- }, 'after');
- }
- },
-
- // @private
- getPicker: function() {
- if (!this.picker) {
- this.picker = new Ext.Picker({
- slots: [{
- align : 'center',
- name : this.name,
- valueField : this.valueField,
- displayField: this.displayField,
- value : this.getValue(),
- store : this.store
- }],
- listeners: {
- change: this.onPickerChange,
- scope: this
- }
- });
- }
-
- return this.picker;
- },
-
- // @private
- getListPanel: function() {
- if (!this.listPanel) {
- this.listPanel = new Ext.Panel({
- floating : true,
- stopMaskTapEvent : false,
- hideOnMaskTap : true,
- cls : 'x-select-overlay',
- scroll : 'vertical',
- items: {
- xtype: 'list',
- store: this.store,
- itemId: 'list',
- scroll: false,
- itemTpl : [
- '<span class="x-list-label">{' + this.displayField + '}</span>',
- '<span class="x-list-selected"></span>'
- ],
- listeners: {
- select : this.onListSelect,
- scope : this
- }
- }
- });
- }
-
- return this.listPanel;
- },
-
- // @private
- onOrientationChange: function() {
- if (this.isActive && !Ext.is.Phone) {
- this.listPanel.showBy(this.el, false, false);
- }
- },
-
- // @private
- onMaskTap: function() {
- if (this.disabled) {
- return;
- }
-
- this.showComponent();
- },
-
- // @private
- showComponent: function() {
- if (Ext.is.Phone) {
- this.getPicker().show();
- }
- else {
- var listPanel = this.getListPanel(),
- index = this.store.findExact(this.valueField, this.value);
-
- listPanel.showBy(this.el, 'fade', false);
- listPanel.down('#list').getSelectionModel().select(index != -1 ? index: 0, false, true);
- }
-
- this.isActive = true;
- },
-
- // @private
- onListSelect: function(selModel, selected) {
- if (selected) {
- this.setValue(selected.get(this.valueField));
- this.fireEvent('change', this, this.getValue());
- }
-
- this.listPanel.hide({
- type: 'fade',
- out: true,
- scope: this
- });
-
- this.isActive = false;
- },
-
- // @private
- onPickerChange: function(picker, value) {
- var currentValue = this.getValue(),
- newValue = value[this.name];
-
- if (newValue != currentValue) {
- this.setValue(newValue);
- this.fireEvent('change', this, newValue);
- }
-
- this.isActive = false;
- },
-
- // Inherited docs
- setValue: function(value) {
- var idx = 0,
- hiddenField = this.hiddenField,
- record;
-
- if (value) {
- idx = this.store.findExact(this.valueField, value)
- }
- record = this.store.getAt(idx);
-
- if (record && this.rendered) {
- this.fieldEl.dom.value = record.get(this.displayField);
- this.value = record.get(this.valueField);
- if (hiddenField) {
- hiddenField.dom.value = this.value;
- }
- } else {
- if (this.rendered) {
- this.fieldEl.dom.value = value;
- }
- this.value = value;
- }
-
- // Temporary fix, the picker should sync with the store automatically by itself
- if (this.picker) {
- var pickerValue = {};
- pickerValue[this.name] = this.value;
- this.picker.setValue(pickerValue);
- }
-
- return this;
- },
-
- // Inherited docs
- getValue: function(){
- return this.value;
- },
-
- /**
- * Updates the underlying <options> list with new values.
- * @param {Array} options An array of options configurations to insert or append.
- * @param {Boolean} append <tt>true</tt> to append the new options existing values.
-<pre><code>
-selectBox.setOptions(
- [ {text: 'First Option', value: 'first'},
- {text: 'Second Option', value: 'second'},
- {text: 'Third Option', value: 'third'}
- ]).setValue('third');
-</code></pre>
- * Note: option object member names should correspond with defined {@link #valueField valueField} and
- * {@link #displayField displayField} values.
- * @return {Ext.form.Select} this
- */
- setOptions: function(options, append) {
- if (!options) {
- this.store.clearData();
- this.setValue(null);
- }
- else {
- this.store.loadData(options, append);
- }
- },
-
- destroy: function() {
- Ext.form.Select.superclass.destroy.apply(this, arguments);
- Ext.destroy(this.listPanel, this.picker, this.hiddenField);
- }
-});
-
-Ext.reg('selectfield', Ext.form.Select);
-
-/**
- * @class Ext.form.TextArea
- * @extends Ext.form.Text
- * <p>Wraps a textarea. See {@link Ext.form.FormPanel FormPanel} for example usage.</p>
- * @xtype textareafield
- */
-Ext.form.TextArea = Ext.extend(Ext.form.Text, {
- ui: 'textarea',
-
- /**
- * @cfg {Integer} maxRows The maximum number of lines made visible by the input.
- */
- maxRows: undefined,
-
- autoCapitalize: false,
-
- renderTpl: [
- '<tpl if="label"><div class="x-form-label"><span>{label}</span></div></tpl>',
- '<tpl if="fieldEl"><div class="x-form-field-container">',
- '<textarea id="{inputId}" type="{type}" name="{name}" class="{fieldCls}"',
- '<tpl if="tabIndex">tabIndex="{tabIndex}" </tpl>',
- '<tpl if="placeHolder">placeholder="{placeHolder}" </tpl>',
- '<tpl if="style">style="{style}" </tpl>',
- '<tpl if="maxRows != undefined">rows="{maxRows}" </tpl>',
- '<tpl if="maxlength">maxlength="{maxlength}" </tpl>',
- '<tpl if="autoComplete">autocomplete="{autoComplete}" </tpl>',
- '<tpl if="autoCapitalize">autocapitalize="{autoCapitalize}" </tpl>',
- '<tpl if="autoFocus">autofocus="{autoFocus}" </tpl>',
- '></textarea>',
- '<tpl if="useMask"><div class="x-field-mask"></div></tpl>',
- '</div></tpl>'
- ],
-
- // @private
- onRender: function() {
- this.renderData.maxRows = this.maxRows;
-
- Ext.form.TextArea.superclass.onRender.apply(this, arguments);
- },
-
- onKeyUp: function(e) {
- this.fireEvent('keyup', this, e);
- }
-});
-
-Ext.reg('textareafield', Ext.form.TextArea);
-
-
-/**
- * @class Ext.form.DatePicker
- * @extends Ext.form.Field
- * <p>Specialized field which has a button which when pressed, shows a {@link Ext.DatePicker}.</p>
- * @xtype datepickerfield
- */
-Ext.form.DatePicker = Ext.extend(Ext.form.Field, {
- ui: 'select',
-
- /**
- * @cfg {Object/Ext.DatePicker} picker
- * An object that is used when creating the internal {@link Ext.DatePicker} component or a direct instance of {@link Ext.DatePicker}
- * Defaults to null
- */
- picker: null,
-
- /**
- * @cfg {Object/Date} value
- * Default value for the field and the internal {@link Ext.DatePicker} component. Accepts an object of 'year',
- * 'month' and 'day' values, all of which should be numbers, or a {@link Date}.
- *
- * Example: {year: 1989, day: 1, month: 5} = 1st May 1989 or new Date()
- */
-
- /**
- * @cfg {Boolean} destroyPickerOnHide
- * Whether or not to destroy the picker widget on hide. This save memory if it's not used frequently,
- * but increase delay time on the next show due to re-instantiation. Defaults to false
- */
- destroyPickerOnHide: false,
-
- // @cfg {Number} tabIndex @hide
-
- // @cfg {Boolean} useMask @hide
-
- // @private
- initComponent: function() {
- this.addEvents(
- /**
- * @event change
- * Fires when a date is selected
- * @param {Ext.form.DatePicker} this
- * @param {Date} date The new date
- */
- 'select'
- );
-
- this.tabIndex = -1;
- this.useMask = true;
-
- Ext.form.Text.superclass.initComponent.apply(this, arguments);
- },
-
- /**
- * Get an instance of the internal date picker; will create a new instance if not exist.
- * @return {Ext.DatePicker} datePicker
- */
- getDatePicker: function() {
- if (!this.datePicker) {
- if (this.picker instanceof Ext.DatePicker) {
- this.datePicker = this.picker;
- } else {
- this.datePicker = new Ext.DatePicker(Ext.apply(this.picker || {}));
- }
-
- this.datePicker.setValue(this.value || null);
-
- this.datePicker.on({
- scope : this,
- change: this.onPickerChange,
- hide : this.onPickerHide
- });
- }
-
- return this.datePicker;
- },
-
- /**
- * @private
- * Listener to the tap event of the mask element. Shows the internal {@link #datePicker} component when the button has been tapped.
- */
- onMaskTap: function() {
- if (Ext.form.DatePicker.superclass.onMaskTap.apply(this, arguments) !== true) {
- return false;
- }
-
- this.getDatePicker().show();
- },
-
- /**
- * Called when the picker changes its value
- * @param {Ext.DatePicker} picker The date picker
- * @param {Object} value The new value from the date picker
- * @private
- */
- onPickerChange : function(picker, value) {
- this.setValue(value);
- this.fireEvent('select', this, this.getValue());
- },
-
- /**
- * Destroys the picker when it is hidden, if
- * {@link Ext.form.DatePicker#destroyPickerOnHide destroyPickerOnHide} is set to true
- * @private
- */
- onPickerHide: function() {
- if (this.destroyPickerOnHide && this.datePicker) {
- this.datePicker.destroy();
- }
- },
-
- // inherit docs
- setValue: function(value, animated) {
- if (this.datePicker) {
- this.datePicker.setValue(value, animated);
- this.value = (value != null) ? this.datePicker.getValue() : null;
- } else {
- if (!Ext.isDate(value) && !Ext.isObject(value)) {
- value = null;
- }
-
- if (Ext.isObject(value)) {
- this.value = new Date(value.year, value.month-1, value.day);
- } else {
- this.value = value;
- }
- }
-
- if (this.rendered) {
- this.fieldEl.dom.value = this.getValue(true);
- }
-
- return this;
- },
-
- /**
- * Returns the value of the field, which will be a {@link Date} unless the <tt>format</tt> parameter is true.
- * @param {Boolean} format True to format the value with <tt>Ext.util.Format.defaultDateFormat</tt>
- */
- getValue: function(format) {
- var value = this.value || null;
- return (format && Ext.isDate(value)) ? value.format(Ext.util.Format.defaultDateFormat) : value;
- },
-
- // @private
- onDestroy: function() {
- if (this.datePicker) {
- this.datePicker.destroy();
- }
-
- Ext.form.DatePicker.superclass.onDestroy.call(this);
- }
-});
-
-Ext.reg('datepickerfield', Ext.form.DatePicker);
-
-
-/**
- * @class Ext.LayoutManager
- * <p>Provides a registry of all Layouts (instances of {@link Ext.layout.Layout} or any subclass
- * thereof) on a page.
- * @singleton
- */
-Ext.layout.LayoutManager = new Ext.AbstractManager({
- /**
- * Creates a new Component from the specified config object using the
- * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
- * @param {Object} config A configuration object for the Component you wish to create.
- * @param {Constructor} defaultType The constructor to provide the default Component type if
- * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
- * @return {Ext.Component} The newly instantiated Component.
- */
- create : function(config, defaultType) {
- if (!config) {
- config = defaultType;
- }
- if (Ext.isString(config)) {
- return new this.types[config || defaultType];
- }
- else if (Ext.isObject(config)) {
- if (config.isLayout) {
- return config;
- }
- else {
- return new this.types[config.type || defaultType](config);
- }
- }
- }
-});
-
-/**
- * Shorthand for {@link Ext.layout.LayoutManager#registerType}
- * @param {String} type The {@link Ext.layout.Layout#type mnemonic string} by which the Layout class
- * may be looked up.
- * @param {Constructor} cls The new Layout class.
- * @member Ext
- * @method regLayout
- */
-Ext.regLayout = function() {
- return Ext.layout.LayoutManager.registerType.apply(Ext.layout.LayoutManager, arguments);
-};
-/**
- * @class Ext.layout.Layout
- * @extends Object
- * Base Layout class - extended by ComponentLayout and ContainerLayout
- */
-
-Ext.layout.Layout = Ext.extend(Object, {
- isLayout: true,
- initialized: false,
-
- constructor : function(config) {
- this.id = Ext.id(null, 'ext-layout-' + this.type + '-');
- Ext.apply(this, config);
- },
-
- /**
- * @private
- */
- layout : function() {
- var me = this;
- me.layoutBusy = true;
- me.initLayout();
-
- if (me.beforeLayout.apply(me, arguments) !== false) {
- me.onLayout.apply(me, arguments);
- me.afterLayout();
- me.owner.needsLayout = false;
- me.layoutBusy = false;
- }
- },
-
- beforeLayout : function() {
- this.renderItems(this.getLayoutItems(), this.getTarget());
- return true;
- },
-
- /**
- * @private
- * Iterates over all passed items, ensuring they are rendered. If the items are already rendered,
- * also determines if the items are in the proper place dom.
- */
- renderItems : function(items, target) {
- var ln = items.length,
- i = 0,
- item;
-
- for (; i < ln; i++) {
- item = items[i];
- if (item && !item.rendered) {
- this.renderItem(item, i, target);
- }
- else if (!this.isValidParent(item, target)) {
- this.moveItem(item, i, target);
- }
- }
- },
-
- /**
- * @private
- * Renders the given Component into the target Element.
- * @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(item, position, target) {
- if (!item.rendered) {
- item.render(target, position);
- this.configureItem(item, position);
- this.childrenChanged = true;
- }
- },
-
- /**
- * @private
- * Moved Component to the provided target instead.
- */
- moveItem : function(item, position, target) {
- if (typeof position == 'number') {
- position = target.dom.childNodes[position];
- }
- // Make sure target is a dom element
- target = target.dom || target;
- target.insertBefore(item.el.dom, position || null);
- item.container = target;
- this.configureItem(item, position);
- this.childrenChanged = true;
- },
-
- /**
- * @private
- * Adds the layout's targetCls if necessary and sets
- * initialized flag when complete.
- */
- initLayout : function() {
- if (!this.initialized && !Ext.isEmpty(this.targetCls)) {
- this.getTarget().addCls(this.targetCls);
- }
- this.initialized = true;
- },
-
- // @private Sets the layout owner
- setOwner : function(owner) {
- this.owner = owner;
- },
-
- // @private - Returns empty array
- getLayoutItems : function() {
- return [];
- },
-
- // @private - Validates item is in the proper place in the dom.
- isValidParent : function(item, target) {
- var dom = item.el ? item.el.dom : Ext.getDom(item);
- return target && (dom.parentNode == (target.dom || target));
- },
-
- /**
- * @private
- * Applies itemCls
- */
- configureItem: function(item, position) {
- if (this.itemCls) {
- item.el.addCls(this.itemCls);
- }
- },
-
- // Placeholder empty functions for subclasses to extend
- onLayout : Ext.emptyFn,
- afterLayout : Ext.emptyFn,
- onRemove : Ext.emptyFn,
- onDestroy : Ext.emptyFn,
-
- /**
- * @private
- * Removes itemCls
- */
- afterRemove : function(item) {
- if (this.itemCls && item.rendered) {
- item.el.removeCls(this.itemCls);
- }
- },
-
- /*
- * 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() {
- if (!Ext.isEmpty(this.targetCls)) {
- var target = this.getTarget();
- if (target) {
- target.removeCls(this.targetCls);
- }
- }
- this.onDestroy();
- }
-});
-/**
-* @class Ext.layout.ComponentLayout
-* @extends Ext.layout.Layout
-* <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Component#componentLayout layout}</b></tt>
-* configuration property. See <tt><b>{@link Ext.Component#componentLayout}</b></tt> for additional details.</p>
-*/
-Ext.layout.ComponentLayout = Ext.extend(Ext.layout.Layout, {
- type: 'component',
-
- monitorChildren: true,
-
- beforeLayout : function(width, height) {
- Ext.layout.ComponentLayout.superclass.beforeLayout.call(this);
- var owner = this.owner,
- isVisible = owner.isVisible(),
- layoutCollection;
- // If an ownerCt is hidden, add my reference onto the layoutOnShow stack. Set the needsLayout flag.
- if (!isVisible && owner.hiddenOwnerCt) {
- layoutCollection = owner.hiddenOwnerCt.layoutOnShow;
- layoutCollection.remove(owner);
- layoutCollection.add(owner);
- owner.needsLayout = {
- width: width,
- height: height,
- isSetSize: false
- };
- }
-
- return isVisible && this.needsLayout(width, height);
- },
-
- /**
- * Check if the new size is different from the current size and only
- * trigger a layout if it is necessary.
- * @param {Mixed} width The new width to set.
- * @param {Mixed} height The new height to set.
- */
- needsLayout : function(width, height) {
- this.lastComponentSize = this.lastComponentSize || {
- width: -Infinity,
- height: -Infinity
- };
-
- var childrenChanged = this.childrenChanged;
- this.childrenChanged = false;
-
- return (childrenChanged || this.lastComponentSize.width !== width || this.lastComponentSize.height !== height);
- },
-
- /**
- * Set the size of any element supporting undefined, null, and values.
- * @param {Mixed} width The new width to set.
- * @param {Mixed} height The new height to set.
- */
- setElementSize: function(el, width, height) {
- if (width !== undefined && height !== undefined) {
- el.setSize(width, height);
- }
- else if (height !== undefined) {
- el.setHeight(height);
- }
- else if (width !== undefined) {
- el.setWidth(width);
- }
- },
-
- /**
- * Returns the owner component's resize element.
- * @return {Ext.Element}
- */
- getTarget : function() {
- return this.owner.el;
- },
-
- /**
- * Set the size of the target element.
- * @param {Mixed} width The new width to set.
- * @param {Mixed} height The new height to set.
- */
- setTargetSize : function(width, height) {
- this.setElementSize(this.owner.el, width, height);
- this.lastComponentSize = {
- width: width,
- height: height
- };
- },
-
- afterLayout : function() {
- var owner = this.owner,
- layout = owner.layout,
- ownerCt = owner.ownerCt,
- ownerCtSize, activeSize, ownerSize, width, height;
-
- owner.afterComponentLayout(this);
-
- // Run the container layout if it exists (layout for child items)
- if (layout && layout.isLayout) {
- layout.layout();
- }
-
- if (ownerCt && ownerCt.componentLayout && ownerCt.componentLayout.monitorChildren && !ownerCt.componentLayout.layoutBusy) {
- ownerCt.componentLayout.childrenChanged = true;
-
- // If the ownerCt isn't running a containerLayout, run doComponentLayout now.
- if (ownerCt.layout && !ownerCt.layout.layoutBusy) {
- if (ownerCt.layout.type == 'autocontainer') {
- ownerCt.doComponentLayout(width, height);
- }
- else {
- ownerCt.layout.layout();
- }
- }
- }
- }
-});
-
-/**
- * @class Ext.layout.AutoComponentLayout
- * @extends Ext.layout.ComponentLayout
- *
- * <p>The AutoLayout is the default layout manager delegated by {@link Ext.Component} to
- * render any child Elements when no <tt>{@link Ext.Component#layout layout}</tt> is configured.</p>
- */
-Ext.layout.AutoComponentLayout = Ext.extend(Ext.layout.ComponentLayout, {
- type: 'autocomponent',
-
- onLayout : function(width, height) {
- this.setTargetSize(width, height);
- }
-});
-
-Ext.regLayout('autocomponent', Ext.layout.AutoComponentLayout);
-
-/**
- * @class Ext.layout.DockLayout
- * @extends Ext.layout.ComponentLayout
- * This ComponentLayout handles docking for Panels. It takes care of panels that are
- * part of a ContainerLayout that sets this Panel's size and Panels that are part of
- * an AutoContainerLayout in which this panel get his height based of the CSS or
- * or its content.
- */
-Ext.layout.DockLayout = Ext.extend(Ext.layout.ComponentLayout, {
- type: 'dock',
-
- /**
- * @property itemCls
- * @type String
- * This class is automatically added to each docked item within this layout.
- * We also use this as a prefix for the position class e.g. x-docked-bottom
- */
- itemCls: 'x-docked',
-
- /**
- * @protected
- * @param {Ext.Component} owner The Panel that owns this DockLayout
- * @param {Ext.Element} target The target in which we are going to render the docked items
- * @param {Array} args The arguments passed to the ComponentLayout.layout method
- */
- onLayout: function(width, height) {
- var me = this,
- owner = me.owner,
- body = owner.body,
- ownerCt = owner.ownerCt,
- layout = owner.layout,
- collapsed = owner.collapsed,
- contracted = owner.contracted,
- expanded = owner.expanded,
- headerItem = me.headerItem,
- target = me.getTarget(),
- autoWidth = false,
- autoHeight = false,
- animTo;
-
- // We start of by resetting all the layouts info
- var info = me.info = {
- boxes: [],
- size: {
- width: width,
- height: height
- },
- padding: {
- top: target.getPadding('t'),
- right: target.getPadding('r'),
- bottom: target.getPadding('b'),
- left: target.getPadding('l')
- },
- border: {
- top: target.getBorderWidth('t'),
- right: target.getBorderWidth('r'),
- bottom: target.getBorderWidth('b'),
- left: target.getBorderWidth('l')
- },
- bodyMargin: {
- top: body.getMargin('t'),
- right: body.getMargin('r'),
- bottom: body.getMargin('b'),
- left: body.getMargin('l')
- },
- bodyBox: {}
- };
-
- // Determine if we have an autoHeight or autoWidth.
- if (height === undefined || height === null || width === undefined || width === null || contracted) {
- // Auto-everything, clear out any style height/width and read from css
- if ((height === undefined || height === null) && (width === undefined || width === null)) {
- autoHeight = true;
- autoWidth = true;
- if (!owner.animCollapse || (!expanded && !contracted)) {
- me.setTargetSize(null, null);
- }
- me.setBodyBox({width: null, height: null});
- }
- // Auto-height
- else if (height === undefined || height === null) {
- autoHeight = true;
- // Clear any sizing that we already set in a previous layout
- if (!owner.animCollapse || (!expanded && !contracted)) {
- me.setTargetSize(width, null);
- }
- me.setBodyBox({width: width, height: null});
- // Auto-width
- }
- else {
- autoWidth = true;
- // Clear any sizing that we already set in a previous layout
- if (!owner.animCollapse || (!expanded && !contracted)) {
- me.setTargetSize(null, height);
- }
- me.setBodyBox({width: null, height: height});
- }
-
- // Run the container
- if (!collapsed && layout && layout.isLayout) {
- layout.layout();
- }
-
- // The dockItems method will add all the top and bottom docked items height
- // to the info.panelSize height. Thats why we have to call setSize after
- // we dock all the items to actually set the panel's width and height.
- // We have to do this because the panel body and docked items will be position
- // absolute which doesnt stretch the panel.
- me.dockItems(autoWidth, autoHeight);
- if (collapsed) {
- if (headerItem) {
- if (headerItem.dock == 'top' || headerItem.dock == 'bottom') {
- info.size.height = headerItem.getHeight();
- }
- else {
- info.size.width = headerItem.getWidths();
- }
- } else {
- info.size.height = 0;
- }
- }
- if (expanded || contracted) {
- if (owner.animCollapse) {
- Ext.createDelegate(owner.animCollapseFn, owner, [info.size.width, info.size.height])();
- }
- else {
- Ext.createDelegate(owner['after' + (expanded ? 'Expand' : 'Collapse')], owner)();
- me.setTargetSize(info.size.width, info.size.height);
- }
- }
- else {
- me.setTargetSize(info.size.width, info.size.height);
- }
- }
- else {
- // If we get inside this else statement, it means that the Panel has been
- // given a size by its parents container layout. In this case we want to
- // actualy set the Panel's dimensions and dock all the items.
- if (expanded || contracted) {
- if (owner.animCollapse) {
- Ext.createDelegate(owner.animCollapseFn, owner, [width, height])();
- }
- else {
- Ext.createDelegate(owner['after' + (expanded ? 'Expand' : 'Collapse')], owner)();
- me.setTargetSize(width, height);
- }
- }
- else {
- me.setTargetSize(width, height);
- me.dockItems();
- }
- }
- Ext.layout.DockLayout.superclass.onLayout.call(me, width, height);
- },
-
- afterLayout : function() {
- Ext.layout.DockLayout.superclass.afterLayout.call(this);
- },
-
- /**
- * @protected
- * This method will first update all the information about the docked items,
- * body dimensions and position, the panel's total size. It will then
- * set all these values on the docked items and panel body.
- * @param {Array} items Array containing all the docked items
- * @param {Boolean} autoBoxes Set this to true if the Panel is part of an
- * AutoContainerLayout
- */
- dockItems : function(autoWidth, autoHeight) {
- this.calculateDockBoxes(autoWidth, autoHeight);
-
- // Both calculateAutoBoxes and calculateSizedBoxes are changing the
- // information about the body, panel size, and boxes for docked items
- // inside a property called info.
- var info = this.info,
- collapsed = this.owner.collapsed,
- boxes = info.boxes,
- ln = boxes.length,
- dock, i;
-
- // We are going to loop over all the boxes that were calculated
- // and set the position of each item the box belongs to.
- for (i = 0; i < ln; i++) {
- dock = boxes[i];
- if (collapsed === true && !dock.isHeader) {
- continue;
- }
- dock.item.setPosition(dock.x, dock.y);
- }
-
- // If the bodyBox has been adjusted because of the docked items
- // we will update the dimensions and position of the panel's body.
- if (autoWidth) {
- info.bodyBox.width = null;
- }
- if (autoHeight) {
- info.bodyBox.height = null;
- }
- this.setBodyBox(info.bodyBox);
- },
-
- /**
- * @protected
- * This method will set up some initial information about the panel size and bodybox
- * and then loop over all the items you pass it to take care of stretching, aligning,
- * dock position and all calculations involved with adjusting the body box.
- * @param {Array} items Array containing all the docked items we have to layout
- */
- calculateDockBoxes : function(autoWidth, autoHeight) {
- // We want to use the Panel's el width, and the Panel's body height as the initial
- // size we are going to use in calculateDockBoxes. We also want to account for
- // the border of the panel.
- var me = this,
- target = me.getTarget(),
- items = me.getLayoutItems(),
- owner = me.owner,
- contracted = owner.contracted,
- expanded = owner.expanded,
- bodyEl = owner.body,
- info = me.info,
- size = info.size,
- ln = items.length,
- padding = info.padding,
- border = info.border,
- item, i, box, w, h, itemEl, vis;
-
- // If this Panel is inside an AutoContainerLayout, we will base all the calculations
- // around the height of the body and the width of the panel.
- if (autoHeight) {
- size.height = bodyEl.getHeight() + padding.top + border.top + padding.bottom + border.bottom;
- }
- else {
- size.height = target.getHeight() - target.getMargin('tb');
- }
-
- if (autoWidth) {
- size.width = bodyEl.getWidth() + padding.left + border.left + padding.right + border.right;
- }
- else {
- size.width = target.getWidth() - target.getMargin('lr');
- }
-
- info.bodyBox = {
- x: border.left + padding.left,
- y: border.top + padding.top,
- width: size.width - padding.left - border.left - padding.right - border.right,
- height: size.height - border.top - padding.top - border.bottom - padding.bottom
- };
-
- // Loop over all the docked items
- for (i = 0; i < ln; i++) {
- item = items[i];
- if (item.isHeader) {
- me.headerItem = item;
- }
- // The initBox method will take care of stretching and alignment
- // In some cases it will also layout the dock items to be able to
- // get a width or height measurement
- box = me.initBox(item);
-
- if (autoHeight === true) {
- box = me.adjustAutoBox(box, i);
- }
- else {
- box = me.adjustSizedBox(box, i);
- }
-
- // Save our box. This allows us to loop over all docked items and do all
- // calculations first. Then in one loop we will actually size and position
- // all the docked items that have changed.
- info.boxes.push(box);
- }
- },
-
- /**
- * @protected
- * This method will adjust the position of the docked item and adjust the body box
- * accordingly.
- * @param {Object} box The box containing information about the width and height
- * of this docked item
- * @param {Number} index The index position of this docked item
- * @return {Object} The adjusted box
- */
- adjustSizedBox : function(box, index) {
- var bodyBox = this.info.bodyBox;
- switch (box.type) {
- case 'top':
- box.y = bodyBox.y;
- break;
-
- case 'left':
- box.x = bodyBox.x;
- break;
-
- case 'bottom':
- box.y = (bodyBox.y + bodyBox.height) - box.height;
- break;
-
- case 'right':
- box.x = (bodyBox.x + bodyBox.width) - box.width;
- break;
- }
-
- // If this is not an overlaying docked item, we have to adjust the body box
- if (!box.overlay) {
- switch (box.type) {
- case 'top':
- bodyBox.y += box.height;
- bodyBox.height -= box.height;
- break;
-
- case 'left':
- bodyBox.x += box.width;
- bodyBox.width -= box.width;
- break;
-
- case 'bottom':
- bodyBox.height -= box.height;
- break;
-
- case 'right':
- bodyBox.width -= box.width;
- break;
- }
- }
- return box;
- },
-
- /**
- * @protected
- * This method will adjust the position of the docked item inside an AutoContainerLayout
- * and adjust the body box accordingly.
- * @param {Object} box The box containing information about the width and height
- * of this docked item
- * @param {Number} index The index position of this docked item
- * @return {Object} The adjusted box
- */
- adjustAutoBox : function (box, index) {
- var info = this.info,
- bodyBox = info.bodyBox,
- size = info.size,
- boxes = info.boxes,
- pos = box.type,
- i, adjustBox;
-
- if (pos == 'top' || pos == 'bottom') {
- // This can affect the previously set left and right and bottom docked items
- for (i = 0; i < index; i++) {
- adjustBox = boxes[i];
- if (adjustBox.stretched && adjustBox.type == 'left' || adjustBox.type == 'right') {
- adjustBox.height += box.height;
- }
- else if (adjustBox.type == 'bottom') {
- adjustBox.y += box.height;
- }
- }
- }
-
- switch (pos) {
- case 'top':
- box.y = bodyBox.y;
- if (!box.overlay) {
- bodyBox.y += box.height;
- }
- size.height += box.height;
- break;
-
- case 'bottom':
- box.y = (bodyBox.y + bodyBox.height);
- size.height += box.height;
- break;
-
- case 'left':
- box.x = bodyBox.x;
- if (!box.overlay) {
- bodyBox.x += box.width;
- bodyBox.width -= box.width;
- }
- break;
-
- case 'right':
- if (!box.overlay) {
- bodyBox.width -= box.width;
- }
- box.x = (bodyBox.x + bodyBox.width);
- break;
- }
- return box;
- },
-
- /**
- * @protected
- * This method will create a box object, with a reference to the item, the type of dock
- * (top, left, bottom, right). It will also take care of stretching and aligning of the
- * docked items.
- * @param {Ext.Component} item The docked item we want to initialize the box for
- * @return {Object} The initial box containing width and height and other useful information
- */
- initBox : function(item) {
- var bodyBox = this.info.bodyBox,
- horizontal = (item.dock == 'top' || item.dock == 'bottom'),
- box = {
- item: item,
- overlay: item.overlay,
- type: item.dock
- };
- // First we are going to take care of stretch and align properties for all four dock scenarios.
- if (item.stretch !== false) {
- box.stretched = true;
- if (horizontal) {
- box.x = bodyBox.x;
- box.width = bodyBox.width;
- item.doComponentLayout(box.width - item.el.getMargin('lr'));
- }
- else {
- box.y = bodyBox.y;
- box.height = bodyBox.height;
- item.doComponentLayout(undefined, box.height - item.el.getMargin('tb'));
- }
- }
- else {
- item.doComponentLayout();
- box.width = item.getWidth();
- box.height = item.getHeight();
- if (horizontal) {
- box.x = (item.align == 'right') ? bodyBox.width - box.width : bodyBox.x;
- }
- }
-
- // If we havent calculated the width or height of the docked item yet
- // do so, since we need this for our upcoming calculations
- if (box.width == undefined) {
- box.width = item.getWidth() + item.el.getMargin('lr');
- }
- if (box.height == undefined) {
- box.height = item.getHeight() + item.el.getMargin('tb');
- }
-
- return box;
- },
-
- /**
- * @protected
- * Returns an array containing all the docked items inside this layout's owner panel
- * @return {Array} An array containing all the docked items of the Panel
- */
- getLayoutItems : function() {
- return this.owner.getDockedItems();
- },
-
- /**
- * @protected
- * This function will be called by the dockItems method. Since the body is positioned absolute,
- * we need to give it dimensions and a position so that it is in the middle surrounded by
- * docked items
- * @param {Object} box An object containing new x, y, width and height values for the
- * Panel's body
- */
- setBodyBox : function(box) {
- var me = this,
- owner = me.owner,
- body = owner.body,
- contracted = owner.contracted,
- expanded = owner.expanded,
- info = me.info,
- bodyMargin = info.bodyMargin,
- padding = info.padding,
- border = info.border;
-
- if (Ext.isNumber(box.width)) {
- box.width -= bodyMargin.left + bodyMargin.right;
- }
- if (Ext.isNumber(box.height)) {
- box.height -= bodyMargin.top + bodyMargin.bottom;
- }
-
- me.setElementSize(body, box.width, box.height);
- body.setLeft(box.x - padding.left - border.left);
- body.setTop(box.y - padding.top - border.top);
- },
-
- /**
- * @protected
- * We are overriding the Ext.layout.Layout configureItem method to also add a class that
- * indicates the position of the docked item. We use the itemCls (x-docked) as a prefix.
- * An example of a class added to a dock: right item is x-docked-right
- * @param {Ext.Component} item The item we are configuring
- */
- configureItem : function(item, pos) {
- Ext.layout.DockLayout.superclass.configureItem.call(this, item, pos);
-
- var el = item.el || Ext.get(item);
- if (this.itemCls) {
- el.addCls(this.itemCls + '-' + item.dock);
- }
- },
-
- afterRemove : function(item) {
- Ext.layout.DockLayout.superclass.afterRemove.call(this, item);
- if (this.itemCls) {
- item.el.removeCls(this.itemCls + '-' + item.dock);
- }
- var dom = item.el.dom;
-
- if (dom) {
- dom.parentNode.removeChild(dom);
- }
-
- this.childrenChanged = true;
- }
-});
-
-Ext.regLayout('dock', Ext.layout.DockLayout);
-/**
- * @class Ext.layout.FieldLayout
- * @extends Ext.layout.ComponentLayout
- *
- * <p>The FieldLayout is the default layout manager delegated by {@link Ext.Field} to
- * render field Elements.</p>
- */
-Ext.layout.FieldLayout = Ext.extend(Ext.layout.ComponentLayout, {
- type: 'field',
-
- // @private
- onLayout: function(width, height) {
- Ext.layout.FieldLayout.superclass.onLayout.call(this, owner, target);
-
- this.setTargetSize(width, height);
- //this.handleLabel();
- },
-
- // @private - Set width of the label
- handleLabel : function() {
- this.owner.labelEl.setWidth(this.owner.labelWidth);
- }
-});
-
-Ext.regLayout('field', Ext.layout.FieldLayout);
-
-/**
-* @class Ext.layout.ContainerLayout
-* @extends Ext.layout.Layout
-* <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
-* configuration property. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
-*/
-Ext.layout.ContainerLayout = Ext.extend(Ext.layout.Layout, {
- type: 'container',
-
- /**
- * @cfg {String} itemCls
- * <p>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.</p>
- * </p>
- */
-
- /**
- * Returns an array of child components.
- * @return {Array} of child components
- */
- getLayoutItems : function() {
- return this.owner && this.owner.items && this.owner.items.items || [];
- },
-
- afterLayout : function() {
- this.owner.afterLayout(this);
- },
- /**
- * Returns the owner component's resize element.
- * @return {Ext.Element}
- */
- getTarget : function() {
- return this.owner.getTargetEl();
- }
-});
-
-/**
- * @class Ext.layout.AutoContainerLayout
- * @extends Ext.layout.ContainerLayout
- *
- * <p>The AutoLayout is the default layout manager delegated by {@link Ext.Container} to
- * render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
- * a <tt>{@link Ext.Container Container}.</tt>. AutoLayout provides only a passthrough of any layout calls
- * to any child containers.</p>
- */
-Ext.layout.AutoContainerLayout = Ext.extend(Ext.layout.ContainerLayout, {
- type: 'autocontainer',
-
- // @private
- onLayout : function(owner, target) {
- var items = this.getLayoutItems(),
- ln = items.length, i;
- for (i = 0; i < ln; i++) {
- items[i].doComponentLayout();
- }
- }
-});
-
-Ext.regLayout('auto', Ext.layout.AutoContainerLayout);
-Ext.regLayout('autocontainer', Ext.layout.AutoContainerLayout);
-/**
- * @class Ext.layout.FitLayout
- * @extends Ext.layout.ContainerLayout
- * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's
- * container. This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}
- * config, and should generally not need to be created directly via the new keyword.</p>
- * <p>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.</p>
- */
-Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {
- itemCls: 'x-fit-item',
- targetCls: 'x-layout-fit',
- type: 'fit',
-
- // @private
- onLayout : function() {
- Ext.layout.FitLayout.superclass.onLayout.call(this);
-
- if (this.owner.items.length) {
- var box = this.getTargetBox(),
- item = this.owner.items.get(0);
-
- this.setItemBox(item, box);
- item.cancelAutoSize = true;
- }
- },
-
- getTargetBox : function() {
- var target = this.getTarget(),
- size = target.getSize(),
- padding = {
- left: target.getPadding('l'),
- right: target.getPadding('r'),
- top: target.getPadding('t'),
- bottom: target.getPadding('b')
- },
- border = {
- left: target.getBorderWidth('l'),
- right: target.getBorderWidth('r'),
- top: target.getBorderWidth('t'),
- bottom: target.getBorderWidth('b')
- };
-
- return {
- width: size.width- padding.left - padding.right - border.left - border.right,
- height: size.height - padding.top - padding.bottom - border.top - border.bottom,
- x: padding.left + border.left,
- y: padding.top + border.top
- };
- },
-
- // @private
- setItemBox : function(item, box) {
- if (item && box.height > 0) {
- box.width -= item.el.getMargin('lr');
- //box.width = null;
- box.height -= item.el.getMargin('tb');
- item.setCalculatedSize(box);
- item.setPosition(box);
- }
- }
-});
-
-Ext.regLayout('fit', Ext.layout.FitLayout);
-
-/**
- * @class Ext.layout.CardLayout
- * @extends Ext.layout.FitLayout
- * <p>This layout manages multiple child Components, each is fit 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.</p>
- * <p>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.</p>
- * <p>Containers that are configured with a card layout will have a method setActiveItem dynamically added to it.
- * <pre><code>
- var p = new Ext.Panel({
- fullscreen: true,
- layout: 'card',
- items: [{
- html: 'Card 1'
- },{
- html: 'Card 2'
- }]
- });
- p.setActiveItem(1);
- </code></pre>
- * </p>
- */
-
-Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {
- type: 'card',
-
- sizeAllCards: false,
- hideInactive: true,
-
- beforeLayout: function() {
- this.activeItem = this.getActiveItem();
- return Ext.layout.CardLayout.superclass.beforeLayout.apply(this, arguments);
- },
-
- onLayout: function() {
- Ext.layout.FitLayout.superclass.onLayout.apply(this, arguments);
-
- var activeItem = this.activeItem,
- items = this.getLayoutItems(),
- ln = items.length,
- targetBox = this.getTargetBox(),
- i,
- item;
-
- for (i = 0; i < ln; i++) {
- item = items[i];
- this.setItemBox(item, targetBox);
- }
-
- if (!this.firstActivated && activeItem) {
- if (activeItem.fireEvent('beforeactivate', activeItem) !== false) {
- activeItem.fireEvent('activate', activeItem);
- }
- this.firstActivated = true;
- }
- },
-
- /**
- * Return the active (visible) component in the layout.
- * @returns {Ext.Component}
- */
- getActiveItem: function() {
- if (!this.activeItem && this.owner) {
- this.activeItem = this.parseActiveItem(this.owner.activeItem);
- }
-
- if (this.activeItem && this.owner.items.items.indexOf(this.activeItem) != -1) {
- return this.activeItem;
- }
-
- return null;
- },
-
- // @private
- parseActiveItem: function(item) {
- if (item && item.isComponent) {
- return item;
- }
- else if (typeof item == 'number' || item == undefined) {
- return this.getLayoutItems()[item || 0];
- }
- else {
- return this.owner.getComponent(item);
- }
- },
-
- // @private
- configureItem: function(item, position) {
- Ext.layout.FitLayout.superclass.configureItem.call(this, item, position);
- if (this.hideInactive && this.activeItem !== item) {
- item.hide();
- }
- else {
- item.show();
- }
- },
-
- onRemove: function(component) {
- if (component === this.activeItem) {
- this.activeItem = null;
- if (this.owner.items.getCount() == 0) {
- this.firstActivated = false;
- }
- }
- },
-
- // @private
- getAnimation: function(newCard, owner) {
- var newAnim = (newCard || {}).cardSwitchAnimation;
- if (newAnim === false) {
- return false;
- }
- return newAnim || owner.cardSwitchAnimation;
- },
-
- /**
- * 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(newCard, animation) {
- var me = this,
- owner = me.owner,
- doc = Ext.getDoc(),
- oldCard = me.activeItem,
- newIndex;
-
- animation = (animation == undefined) ? this.getAnimation(newCard, owner) : animation;
-
- newCard = me.parseActiveItem(newCard);
- newIndex = owner.items.indexOf(newCard);
-
-
- // If the card is not a child of the owner, then add it
- if (newIndex == -1) {
- owner.add(newCard);
- }
-
- // Is this a valid, different card?
- if (newCard && oldCard != newCard && owner.onBeforeCardSwitch(newCard, oldCard, newIndex, !!animation) !== false) {
- // If the card has not been rendered yet, now is the time to do so.
- if (!newCard.rendered) {
- this.layout();
- }
-
- // Fire the beforeactivate and beforedeactivate events on the cards
- if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) {
- return false;
- }
- if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) {
- return false;
- }
-
- // Make sure the new card is shown
- if (newCard.hidden) {
- newCard.show();
- }
-
- me.activeItem = newCard;
-
- if (animation) {
- doc.on('click', Ext.emptyFn, me, {
- single: true,
- preventDefault: true
- });
-
- Ext.Anim.run(newCard, animation, {
- out: false,
- autoClear: true,
- scope: me,
- after: function() {
- Ext.defer(function() {
- doc.un('click', Ext.emptyFn, me);
- },
- 50, me);
-
- newCard.fireEvent('activate', newCard, oldCard);
-
- if (!oldCard) {
- // If there is no old card, the we have to make sure that we fire
- // onCardSwitch here.
- owner.onCardSwitch(newCard, oldCard, newIndex, true);
- }
- }
- });
-
- if (oldCard) {
- Ext.Anim.run(oldCard, animation, {
- out: true,
- autoClear: true,
- after: function() {
- oldCard.fireEvent('deactivate', oldCard, newCard);
- if (me.hideInactive && me.activeItem != oldCard) {
- oldCard.hide();
- }
-
- // We fire onCardSwitch in the after of the oldCard animation
- // because that is the last one to fire, and we want to make sure
- // both animations are finished before firing it.
- owner.onCardSwitch(newCard, oldCard, newIndex, true);
- }
- });
- }
- }
- else {
- newCard.fireEvent('activate', newCard, oldCard);
- if (oldCard) {
- oldCard.fireEvent('deactivate', oldCard, newCard);
- if (me.hideInactive) {
- oldCard.hide();
- }
- }
- owner.onCardSwitch(newCard, oldCard, newIndex, false);
- }
-
- return newCard;
- }
-
- return false;
- },
-
- /**
- * Return the active (visible) component in the layout to the next card, optional wrap parameter to wrap to the first
- * card when the end of the stack is reached.
- * @param {boolean} wrap Wrap to the first card when the end of the stack is reached.
- * @returns {Ext.Component}
- */
- getNext: function(wrap) {
- var items = this.getLayoutItems(),
- index = items.indexOf(this.activeItem);
- return items[index + 1] || (wrap ? items[0] : false);
- },
-
- /**
- * Sets the active (visible) component in the layout to the next card, optional wrap parameter to wrap to the first
- * card when the end of the stack is reached.
- * @param {Mixed} anim Animation to use for the card transition
- * @param {boolean} wrap Wrap to the first card when the end of the stack is reached.
- */
- next: function(anim, wrap) {
- return this.setActiveItem(this.getNext(wrap), anim);
- },
-
- /**
- * Return the active (visible) component in the layout to the previous card, optional wrap parameter to wrap to
- * the last card when the beginning of the stack is reached.
- * @param {boolean} wrap Wrap to the first card when the end of the stack is reached.
- * @returns {Ext.Component}
- */
- getPrev: function(wrap) {
- var items = this.getLayoutItems(),
- index = items.indexOf(this.activeItem);
- return items[index - 1] || (wrap ? items[items.length - 1] : false);
- },
-
- /**
- * Sets the active (visible) component in the layout to the previous card, optional wrap parameter to wrap to
- * the last card when the beginning of the stack is reached.
- * @param {Mixed} anim Animation to use for the card transition
- * @param {boolean} wrap Wrap to the first card when the end of the stack is reached.
- */
- prev: function(anim, wrap) {
- return this.setActiveItem(this.getPrev(wrap), anim);
- }
-});
-
-Ext.regLayout('card', Ext.layout.CardLayout);
-/**
- * @class Ext.layout.BoxLayout
- * @extends Ext.layout.ContainerLayout
- * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>
- */
-Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
- type: 'box',
-
- targetCls: 'x-layout-box',
- //wrapCls: 'x-layout-box-wrap',
- innerCls: 'x-layout-box-inner',
-
- // document these properties on their subclasses
- pack : 'start',
- align: 'center',
-
- /**
- * @cfg {String} direction Specifies the direction in which child components are laid out. Defaults
- * to <tt>'normal'</tt>, which means they are laid out in the order they are added. You can use the
- * <tt>'reverse'</tt> option to have them laid out in reverse.
- */
- direction: 'normal',
-
- /**
- * @private
- * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values
- * when laying out
- */
- onLayout: function() {
- Ext.layout.BoxLayout.superclass.onLayout.call(this);
-
- if (this.pack === 'left' || this.pack === 'top') {
- this.pack = 'start';
- } else if (this.pack === 'right' || this.pack === 'bottom') {
- this.pack = 'end';
- }
-
- var target = this.getTarget(),
- ct = target.parent(),
- targetWidth = (ct.getWidth() - ct.getPadding('lr') - ct.getBorderWidth('lr')) + 'px',
- targetHeight = (ct.getHeight() - ct.getPadding('tb') - ct.getBorderWidth('tb')) + 'px';
-
- target.setStyle({
- '-webkit-box-orient': this.orientation,
- '-webkit-box-direction': this.direction,
- '-webkit-box-pack': this.pack,
- '-webkit-box-align': this.align
- });
-
- if (this.orientation == 'horizontal') {
- target.setStyle({
- 'min-width': targetWidth,
- 'height': targetHeight
- });
- } else {
- target.setStyle({
- 'min-height': targetHeight,
- 'width': targetWidth
- });
- }
-
- this.prepareFlexedItems();
- this.setFlexedItems();
- },
-
- prepareFlexedItems : function() {
- var items = this.getLayoutItems(),
- ln = items.length,
- item, i;
-
- for (i = 0; i < ln; i++) {
- item = items[i];
- if (item.flex != undefined) {
- item.el.setStyle('position', 'absolute');
- item.boxEl = this.createBoxEl(item);
- } else {
- item.doComponentLayout();
- }
- }
- },
-
- setFlexedItems : function() {
- var items = this.getLayoutItems(),
- ln = items.length,
- item, i;
-
- for (i = 0; i < ln; i++) {
- item = items[i];
- if (item.flex != undefined) {
- item.boxSize = item.boxEl.getSize();
- }
- }
-
- for (i = 0; i < ln; i++) {
- item = items[i];
- if (item.flex != undefined) {
- item.el.setStyle('position', '');
- if (this.align == 'stretch') {
- item.setSize(item.boxSize);
- } else {
- if (this.orientation == 'horizontal') {
- item.setWidth(item.boxSize.width);
- } else {
- item.setHeight(item.boxSize.height);
- }
- }
- item.boxEl.remove();
- delete item.boxEl;
- delete item.boxSize;
- }
- }
- },
-
- getTarget : function() {
- var owner = this.owner,
- innerCt = this.innerCt;
-
- if (!innerCt) {
- if (owner.scrollEl) {
- innerCt = owner.scrollEl.addCls(this.innerCls);
- } else {
- innerCt = owner.getTargetEl().createChild({cls: this.innerCls});
- }
- this.innerCt = innerCt;
- }
-
- return innerCt;
- },
-
- createBoxEl : function(item) {
- var el = item.el;
- return el.insertSibling({
- style: 'margin-top: ' + el.getMargin('tb') + 'px; margin-left: ' + el.getMargin('lr') + 'px; -webkit-box-flex: ' + item.flex
- });
- }
-});
-
-/**
- * @class Ext.layout.HBoxLayout
- * @extends Ext.layout.BoxLayout
- * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
- * space between child items containing a numeric <code>flex</code> configuration. The flex option is a ratio that
- * distributes width after any items with explicit widths have been accounted for. In the code below, the width is calculated
- * as follows:
- * <ul>
- * <li>The fixed width item is subtracted, leaving us with 300 width</li>
- * <li>The total flex number is counted, in this case, it is 3</li>
- * <li>The ratio is then calculated, 300 / 3 = 100</li>
- * <li>The first item has a flex of 2, so it is set to 2 * 100</li>
- * <li>The other remaining item is set to 1 * 100</li>
- * </ul></p>
- * <pre><code>
-new Ext.Container({
- width: 400,
- height: 300,
- layout: {
- type: 'hbox',
- align: 'stretch'
- },
- items: [{
- flex: 2,
- html: 'First'
- },{
- width: 100,
- html: 'Second'
- },{
- flex: 1,
- html: 'Third'
- }]
-});
- * </code></pre>
- * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option.
- */
-Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
- orientation: 'horizontal'
-
- /**
- * @cfg {String} pack
- * Specifies the horizontal alignment of child components. Defaults to <tt>'start'</tt>. Acceptable values are:
- * <ul>
- * <li><b>center</b> : <div class="sub-desc">
- * Aligned to the center of the container.
- * </div></li>
- *
- * <li><b>end</b> : <div class="sub-desc">
- * Aligned to the right of the container.
- * </div></li>
- *
- * <li><b>justify</b> : <div class="sub-desc">
- * Justified with both the left and right of the container.
- * </div></li>
- *
- * <li><b>start</b> : <div class="sub-desc">
- * Aligned to the left of the container.
- * </div></li>
- * </ul>
- */
-
- /**
- * @cfg {String} align Specifies the vertical alignment of child components. Defaults to <tt>'center'</tt>. Acceptable values are:
- * <ul>
- * <li><b>center</b> : <div class="sub-desc">
- * Aligned to the center of the container.
- * </div></li>
- *
- * <li><b>end</b> : <div class="sub-desc">
- * Aligned to the bottom of the container.
- * </div></li>
- *
- * <li><b>start</b> : <div class="sub-desc">
- * Aligned to the top of the container.
- * </div></li>
- *
- * <li><b>stretch</b> : <div class="sub-desc">
- * Components are stretched vertically to fill the container.
- * </div></li>
- * </ul>
- */
-});
-
-Ext.regLayout('hbox', Ext.layout.HBoxLayout);
-
-/**
- * @class Ext.layout.VBoxLayout
- * @extends Ext.layout.BoxLayout
- * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical
- * space between child items containing a numeric <code>flex</code> configuration. The flex option is a ratio that
- * distributes height after any items with explicit heights have been accounted for. In the code below, the height is calculated
- * as follows:
- * <ul>
- * <li>The fixed height item is subtracted, leaving us with 300 height</li>
- * <li>The total flex number is counted, in this case, it is 3</li>
- * <li>The ratio is then calculated, 300 / 3 = 100</li>
- * <li>The first item has a flex of 2, so it is set to 2 * 100</li>
- * <li>The other remaining item is set to 1 * 100</li>
- * </ul></p>
- * <pre><code>
-new Ext.Container({
- width: 300,
- height: 400,
- layout: {
- type: 'vbox',
- align: 'stretch'
- },
- items: [{
- flex: 2,
- html: 'First'
- },{
- width: 100,
- html: 'Second'
- },{
- flex: 1,
- html: 'Third'
- }]
-});
- * </code></pre>
- * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option.
- */
-Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
- orientation: 'vertical'
-
- /**
- * @cfg {String} pack
- * Specifies the vertical alignment of child components. Defaults to <tt>'start'</tt>. Acceptable values are:
- * <ul>
- * <li><b>center</b> : <div class="sub-desc">
- * Aligned to the center of the container.
- * </div></li>
- *
- * <li><b>end</b> : <div class="sub-desc">
- * Aligned to the bottom of the container.
- * </div></li>
- *
- * <li><b>justify</b> : <div class="sub-desc">
- * Justified with both the top and bottom of the container.
- * </div></li>
- *
- * <li><b>start</b> : <div class="sub-desc">
- * Aligned to the top of the container.
- * </div></li>
- * </ul>
- */
-
- /**
- * @cfg {String} align Specifies the horizontal alignignment of child components. Defaults to <tt>'center'</tt>. Acceptable values are:
- * <ul>
- * <li><b>center</b> : <div class="sub-desc">
- * Aligned to the center of the container.
- * </div></li>
- *
- * <li><b>end</b> : <div class="sub-desc">
- * Aligned to the right of the container.
- * </div></li>
- *
- * <li><b>start</b> : <div class="sub-desc">
- * Aligned to the left of the container.
- * </div></li>
- *
- * <li><b>stretch</b> : <div class="sub-desc">
- * Components are stretched horizontally to fill the container.
- * </div></li>
- * </ul>
- */
-
-});
-
-Ext.regLayout('vbox', Ext.layout.VBoxLayout);
-
-
-
More information about the Commits
mailing list