+ if(this.recordType){
+ this.fields = this.recordType.prototype.fields;
+ }
+ this.modified = [];
+
+ this.addEvents({
+ /**
+ * @event datachanged
+ * Fires when the data cache has changed, and a widget which is using this Store
+ * as a Record cache should refresh its view.
+ * @param {Store} this
+ */
+ datachanged : true,
+ /**
+ * @event metachange
+ * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
+ * @param {Store} this
+ * @param {Object} meta The JSON metadata
+ */
+ metachange : true,
+ /**
+ * @event add
+ * Fires when Records have been added to the Store
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The array of Records added
+ * @param {Number} index The index at which the record(s) were added
+ */
+ add : true,
+ /**
+ * @event remove
+ * Fires when a Record has been removed from the Store
+ * @param {Store} this
+ * @param {Roo.data.Record} record The Record that was removed
+ * @param {Number} index The index at which the record was removed
+ */
+ remove : true,
+ /**
+ * @event update
+ * Fires when a Record has been updated
+ * @param {Store} this
+ * @param {Roo.data.Record} record The Record that was updated
+ * @param {String} operation The update operation being performed. Value may be one of:
+ * <pre><code>
+ Roo.data.Record.EDIT
+ Roo.data.Record.REJECT
+ Roo.data.Record.COMMIT
+ * </code></pre>
+ */
+ update : true,
+ /**
+ * @event clear
+ * Fires when the data cache has been cleared.
+ * @param {Store} this
+ */
+ clear : true,
+ /**
+ * @event beforeload
+ * Fires before a request is made for a new data object. If the beforeload handler returns false
+ * the load action will be canceled.
+ * @param {Store} this
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ */
+ beforeload : true,
+ /**
+ * @event beforeloadadd
+ * Fires after a new set of Records has been loaded.
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The Records that were loaded
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ */
+ beforeloadadd : true,
+ /**
+ * @event load
+ * Fires after a new set of Records has been loaded, before they are added to the store.
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The Records that were loaded
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ * @params {Object} return from reader
+ */
+ load : true,
+ /**
+ * @event loadexception
+ * Fires if an exception occurs in the Proxy during loading.
+ * Called with the signature of the Proxy's "loadexception" event.
+ * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
+ *
+ * @param {Proxy}
+ * @param {Object} return from JsonData.reader() - success, totalRecords, records
+ * @param {Object} load options
+ * @param {Object} jsonData from your request (normally this contains the Exception)
+ */
+ loadexception : true
+ });
+
+ if(this.proxy){
+ this.proxy = Roo.factory(this.proxy, Roo.data);
+ this.proxy.xmodule = this.xmodule || false;
+ this.relayEvents(this.proxy, ["loadexception"]);
+ }
+ this.sortToggle = {};
+ this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
+
+ Roo.data.Store.superclass.constructor.call(this);
+
+ if(this.inlineData){
+ this.loadData(this.inlineData);
+ delete this.inlineData;
+ }
+};
+
+Roo.extend(Roo.data.Store, Roo.util.Observable, {
+ /**
+ * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
+ * without a remote query - used by combo/forms at present.
+ */
+
+ /**
+ * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
+ */
+ /**
+ * @cfg {Array} data Inline data to be loaded when the store is initialized.
+ */
+ /**
+ * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
+ * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
+ */
+ /**
+ * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
+ * on any HTTP request
+ */
+ /**
+ * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
+ */
+ /**
+ * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
+ */
+ multiSort: false,
+ /**
+ * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
+ * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
+ */
+ remoteSort : false,
+
+ /**
+ * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
+ * loaded or when a record is removed. (defaults to false).
+ */
+ pruneModifiedRecords : false,
+
+ // private
+ lastOptions : null,
+
+ /**
+ * Add Records to the Store and fires the add event.
+ * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+ */
+ add : function(records){
+ records = [].concat(records);
+ for(var i = 0, len = records.length; i < len; i++){
+ records[i].join(this);
+ }
+ var index = this.data.length;
+ this.data.addAll(records);
+ this.fireEvent("add", this, records, index);
+ },
+
+ /**
+ * Remove a Record from the Store and fires the remove event.
+ * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
+ */
+ remove : function(record){
+ var index = this.data.indexOf(record);
+ this.data.removeAt(index);
+
+ if(this.pruneModifiedRecords){
+ this.modified.remove(record);
+ }
+ this.fireEvent("remove", this, record, index);
+ },
+
+ /**
+ * Remove all Records from the Store and fires the clear event.
+ */
+ removeAll : function(){
+ this.data.clear();
+ if(this.pruneModifiedRecords){
+ this.modified = [];
+ }
+ this.fireEvent("clear", this);
+ },
+
+ /**
+ * Inserts Records to the Store at the given index and fires the add event.
+ * @param {Number} index The start index at which to insert the passed Records.
+ * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+ */
+ insert : function(index, records){
+ records = [].concat(records);
+ for(var i = 0, len = records.length; i < len; i++){
+ this.data.insert(index, records[i]);
+ records[i].join(this);
+ }
+ this.fireEvent("add", this, records, index);
+ },
+
+ /**
+ * Get the index within the cache of the passed Record.
+ * @param {Roo.data.Record} record The Roo.data.Record object to 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);
+ },
+
+ /**
+ * Get the Record with the specified id.
+ * @param {String} id The id of the Record to find.
+ * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
+ */
+ getById : function(id){
+ return this.data.key(id);
+ },
+
+ /**
+ * Get the Record at the specified index.
+ * @param {Number} index The index of the Record to find.
+ * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
+ */
+ getAt : function(index){
+ return this.data.itemAt(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 {Roo.data.Record[]} An array of Records
+ */
+ getRange : function(start, end){
+ return this.data.getRange(start, end);
+ },
+
+ // private
+ storeOptions : function(o){
+ o = Roo.apply({}, o);
+ delete o.callback;
+ delete o.scope;
+ this.lastOptions = o;
+ },
+
+ /**
+ * Loads the Record cache from the configured Proxy using the configured Reader.
+ * <p>
+ * If using remote paging, then the first load call must specify the <em>start</em>
+ * and <em>limit</em> properties in the options.params property to establish the initial
+ * position within the dataset, and the number of Records to cache on each read from the Proxy.
+ * <p>
+ * <strong>It is important to note that for remote data sources, loading is asynchronous,
+ * and this call will return before the new data has been loaded. Perform any post-processing
+ * in a callback function, or in a "load" event handler.</strong>
+ * <p>
+ * @param {Object} options An object containing properties which control loading options:<ul>
+ * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
+ * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
+ * <pre>
+ {
+ data : data, // array of key=>value data like JsonReader
+ total : data.length,
+ success : true
+
+ }
+ </pre>
+ }.</li>
+ * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
+ * passed the following arguments:<ul>
+ * <li>r : Roo.data.Record[]</li>
+ * <li>options: Options object from the load call</li>
+ * <li>success: Boolean success indicator</li></ul></li>
+ * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
+ * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
+ * </ul>
+ */
+ load : function(options){
+ options = options || {};
+ if(this.fireEvent("beforeload", this, options) !== false){
+ this.storeOptions(options);
+ var p = Roo.apply(options.params || {}, this.baseParams);
+ // if meta was not loaded from remote source.. try requesting it.
+ if (!this.reader.metaFromRemote) {
+ p._requestMeta = 1;
+ }
+ if(this.sortInfo && this.remoteSort){
+ var pn = this.paramNames;
+ p[pn["sort"]] = this.sortInfo.field;
+ p[pn["dir"]] = this.sortInfo.direction;
+ }
+ if (this.multiSort) {
+ var pn = this.paramNames;
+ p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
+ }
+
+ this.proxy.load(p, this.reader, this.loadRecords, this, options);
+ }
+ },
+
+ /**
+ * Reloads the Record cache from the configured Proxy using the configured Reader and
+ * the options from the last load operation performed.
+ * @param {Object} options (optional) An object containing properties which may override the options
+ * used in the last load operation. See {@link #load} for details (defaults to null, in which case
+ * the most recently used options are reused).
+ */
+ reload : function(options){
+ this.load(Roo.applyIf(options||{}, this.lastOptions));
+ },
+
+ // private
+ // Called as a callback by the Reader during a load operation.
+ loadRecords : function(o, options, success){
+
+ if(!o){
+ if(success !== false){
+ this.fireEvent("load", this, [], options, o);
+ }
+ if(options.callback){
+ options.callback.call(options.scope || this, [], options, false);
+ }
+ return;
+ }
+ // if data returned failure - throw an exception.
+ if (o.success === false) {
+ // show a message if no listener is registered.
+ if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
+ }
+ // loadmask wil be hooked into this..
+ this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
+ return;
+ }
+ var r = o.records, t = o.totalRecords || r.length;
+
+ this.fireEvent("beforeloadadd", this, r, options, o);
+
+ if(!options || options.add !== true){
+ if(this.pruneModifiedRecords){
+ this.modified = [];
+ }
+ for(var i = 0, len = r.length; i < len; i++){
+ r[i].join(this);
+ }
+ if(this.snapshot){
+ this.data = this.snapshot;
+ delete this.snapshot;
+ }
+ this.data.clear();
+ this.data.addAll(r);
+ this.totalLength = t;
+ this.applySort();
+ this.fireEvent("datachanged", this);
+ }else{
+ this.totalLength = Math.max(t, this.data.length+r.length);
+ this.add(r);
+ }
+
+ if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
+
+ var e = new Roo.data.Record({});
+
+ e.set(this.parent.displayField, this.parent.emptyTitle);
+ e.set(this.parent.valueField, '');
+
+ this.insert(0, e);
+ }
+
+ this.fireEvent("load", this, r, options, o);
+ if(options.callback){
+ options.callback.call(options.scope || this, r, options, true);
+ }
+ },
+
+
+ /**
+ * Loads data from a passed data block. A Reader which understands the format of the data
+ * must have been configured in the constructor.
+ * @param {Object} data The data block from which to read the Records. The format of the data expected
+ * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
+ * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
+ */
+ loadData : function(o, append){
+ var r = this.reader.readRecords(o);
+ this.loadRecords(r, {add: append}, true);
+ },
+
+ /**
+ * using 'cn' the nested child reader read the child array into it's child stores.
+ * @param {Object} rec The record with a 'children array
+ */
+ loadDataFromChildren : function(rec)
+ {
+ this.loadData(this.reader.toLoadData(rec));
+ },
+
+
+ /**
+ * Gets the number of cached records.
+ * <p>
+ * <em>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 getTotalCount() function returns
+ * the data set size</em>
+ */
+ getCount : function(){
+ return this.data.length || 0;
+ },
+
+ /**
+ * Gets the total number of records in the dataset as returned by the server.
+ * <p>
+ * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
+ * the dataset size</em>
+ */
+ getTotalCount : function(){
+ return this.totalLength || 0;
+ },
+
+ /**
+ * Returns the sort state of the Store as an object with two properties:
+ * <pre><code>
+ field {String} The name of the field by which the Records are sorted
+ direction {String} The sort order, "ASC" or "DESC"
+ * </code></pre>
+ */
+ getSortState : function(){
+ return this.sortInfo;
+ },
+
+ // private
+ applySort : function(){
+ if(this.sortInfo && !this.remoteSort){
+ var s = this.sortInfo, f = s.field;
+ var st = this.fields.get(f).sortType;
+ var fn = function(r1, r2){
+ var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
+ return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
+ };
+ this.data.sort(s.direction, fn);
+ if(this.snapshot && this.snapshot != this.data){
+ this.snapshot.sort(s.direction, fn);
+ }
+ }
+ },
+
+ /**
+ * Sets the default sort column and order to be used by the next load operation.
+ * @param {String} fieldName The name of the field to sort by.
+ * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
+ */
+ setDefaultSort : function(field, dir){
+ this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
+ },
+
+ /**
+ * Sort the Records.
+ * If remote sorting is used, the sort is performed on the server, and the cache is
+ * reloaded. If local sorting is used, the cache is sorted internally.
+ * @param {String} fieldName The name of the field to sort by.
+ * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
+ */
+ sort : function(fieldName, dir){
+ var f = this.fields.get(fieldName);
+ if(!dir){
+ this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
+
+ if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
+ dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
+ }else{
+ dir = f.sortDir;
+ }
+ }
+ this.sortToggle[f.name] = dir;
+ this.sortInfo = {field: f.name, direction: dir};
+ if(!this.remoteSort){
+ this.applySort();
+ this.fireEvent("datachanged", this);
+ }else{
+ this.load(this.lastOptions);
+ }
+ },
+
+ /**
+ * Calls the specified function for each of the Records in the cache.
+ * @param {Function} fn The function to call. The Record is passed as the first parameter.
+ * Returning <em>false</em> aborts and exits the iteration.
+ * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
+ */
+ each : function(fn, scope){
+ this.data.each(fn, scope);
+ },
+
+ /**
+ * Gets all records modified since the last commit. Modified records are persisted across load operations
+ * (e.g., during paging).
+ * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
+ */
+ getModifiedRecords : function(){
+ return this.modified;
+ },
+
+ // private
+ createFilterFn : function(property, value, anyMatch){
+ if(!value.exec){ // not a regex
+ value = String(value);
+ if(value.length == 0){
+ return false;
+ }
+ value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
+ }
+ return function(r){
+ return value.test(r.data[property]);
+ };
+ },
+
+ /**
+ * Sums the value of <i>property</i> for each record between start and end and returns the result.
+ * @param {String} property A field on your records
+ * @param {Number} start The record index to start at (defaults to 0)
+ * @param {Number} end The last record index to include (defaults to length - 1)
+ * @return {Number} The sum
+ */
+ sum : function(property, start, end){
+ var rs = this.data.items, v = 0;
+ start = start || 0;
+ end = (end || end === 0) ? end : rs.length-1;
+
+ for(var i = start; i <= end; i++){
+ v += (rs[i].data[property] || 0);
+ }
+ return v;
+ },
+
+ /**
+ * Filter the records by a specified property.
+ * @param {String} field A field on your records
+ * @param {String/RegExp} value Either a string that the field
+ * should start with or a RegExp to test against the field
+ * @param {Boolean} anyMatch True to match any part not just the beginning
+ */
+ filter : function(property, value, anyMatch){
+ var fn = this.createFilterFn(property, value, anyMatch);
+ return fn ? this.filterBy(fn) : this.clearFilter();
+ },
+
+ /**
+ * Filter by a function. The specified function will be called with each
+ * record in this data source. If the function returns true the record is included,
+ * otherwise it is filtered.
+ * @param {Function} fn The function to be called, it will receive 2 args (record, id)
+ * @param {Object} scope (optional) The scope of the function (defaults to this)
+ */
+ filterBy : function(fn, scope){
+ this.snapshot = this.snapshot || this.data;
+ this.data = this.queryBy(fn, scope||this);
+ this.fireEvent("datachanged", this);
+ },
+
+ /**
+ * Query the records by a specified property.
+ * @param {String} field A field on your records
+ * @param {String/RegExp} value Either a string that the field
+ * should start with or a RegExp to test against the field
+ * @param {Boolean} anyMatch True to match any part not just the beginning
+ * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
+ */
+ query : function(property, value, anyMatch){
+ var fn = this.createFilterFn(property, value, anyMatch);
+ return fn ? this.queryBy(fn) : this.data.clone();
+ },
+
+ /**
+ * Query by a function. The specified function will be called with each
+ * record in this data source. If the function returns true the record is included
+ * in the results.
+ * @param {Function} fn The function to be called, it will receive 2 args (record, id)
+ * @param {Object} scope (optional) The scope of the function (defaults to this)
+ @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
+ **/
+ queryBy : function(fn, scope){
+ var data = this.snapshot || this.data;
+ return data.filterBy(fn, scope||this);
+ },
+
+ /**
+ * 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 d = (bypassFilter === true && this.snapshot) ?
+ this.snapshot.items : this.data.items;
+ var v, sv, r = [], l = {};
+ for(var i = 0, len = d.length; i < len; i++){
+ v = d[i].data[dataIndex];
+ sv = String(v);
+ if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
+ l[sv] = true;
+ r[r.length] = v;
+ }
+ }
+ return r;
+ },
+
+ /**
+ * Revert to a view of the Record cache with no filtering applied.
+ * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
+ */
+ clearFilter : function(suppressEvent){
+ if(this.snapshot && this.snapshot != this.data){
+ this.data = this.snapshot;
+ delete this.snapshot;
+ if(suppressEvent !== true){
+ this.fireEvent("datachanged", this);
+ }
+ }
+ },
+
+ // private
+ afterEdit : function(record){
+ if(this.modified.indexOf(record) == -1){
+ this.modified.push(record);
+ }
+ this.fireEvent("update", this, record, Roo.data.Record.EDIT);
+ },
+
+ // private
+ afterReject : function(record){
+ this.modified.remove(record);
+ this.fireEvent("update", this, record, Roo.data.Record.REJECT);
+ },
+
+ // private
+ afterCommit : function(record){
+ this.modified.remove(record);
+ this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
+ },
+
+ /**
+ * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
+ * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
+ */
+ commitChanges : function(){
+ var m = this.modified.slice(0);
+ this.modified = [];
+ for(var i = 0, len = m.length; i < len; i++){
+ m[i].commit();
+ }
+ },
+
+ /**
+ * Cancel outstanding changes on all changed records.
+ */
+ rejectChanges : function(){
+ var m = this.modified.slice(0);
+ this.modified = [];
+ for(var i = 0, len = m.length; i < len; i++){
+ m[i].reject();
+ }
+ },
+
+ onMetaChange : function(meta, rtype, o){
+ this.recordType = rtype;
+ this.fields = rtype.prototype.fields;
+ delete this.snapshot;
+ this.sortInfo = meta.sortInfo || this.sortInfo;
+ this.modified = [];
+ this.fireEvent('metachange', this, this.reader.meta);
+ },
+
+ moveIndex : function(data, type)
+ {
+ var index = this.indexOf(data);
+
+ var newIndex = index + type;
+
+ this.remove(data);
+
+ this.insert(newIndex, data);
+
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.SimpleStore
+ * @extends Roo.data.Store
+ * Small helper class to make creating Stores from Array data easier.
+ * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
+ * @cfg {Array} fields An array of field definition objects, or field name strings.
+ * @cfg {Object} an existing reader (eg. copied from another store)
+ * @cfg {Array} data The multi-dimensional array of data
+ * @cfg {Roo.data.DataProxy} proxy [not-required]
+ * @cfg {Roo.data.Reader} reader [not-required]
+ * @constructor
+ * @param {Object} config
+ */
+Roo.data.SimpleStore = function(config)
+{
+ Roo.data.SimpleStore.superclass.constructor.call(this, {
+ isLocal : true,
+ reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
+ id: config.id
+ },
+ Roo.data.Record.create(config.fields)
+ ),
+ proxy : new Roo.data.MemoryProxy(config.data)
+ });
+ this.load();
+};
+Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+/**
+ * @extends Roo.data.Store
+ * @class Roo.data.JsonStore
+ * Small helper class to make creating Stores for JSON data easier. <br/>
+<pre><code>
+var store = new Roo.data.JsonStore({
+ url: 'get-images.php',
+ root: 'images',
+ fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
+});
+</code></pre>
+ * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
+ * JsonReader and HttpProxy (unless inline data is provided).</b>
+ * @cfg {Array} fields An array of field definition objects, or field name strings.
+ * @constructor
+ * @param {Object} config
+ */
+Roo.data.JsonStore = function(c){
+ Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
+ proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
+ reader: new Roo.data.JsonReader(c, c.fields)
+ }));
+};
+Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+
+Roo.data.Field = function(config){
+ if(typeof config == "string"){
+ config = {name: config};
+ }
+ Roo.apply(this, config);
+
+ if(!this.type){
+ this.type = "auto";
+ }
+
+ var st = Roo.data.SortTypes;
+ // named sortTypes are supported, here we look them up
+ if(typeof this.sortType == "string"){
+ this.sortType = st[this.sortType];
+ }
+
+ // set default sortType for strings and dates
+ if(!this.sortType){
+ switch(this.type){
+ case "string":
+ this.sortType = st.asUCString;
+ break;
+ case "date":
+ this.sortType = st.asDate;
+ break;
+ default:
+ this.sortType = st.none;
+ }
+ }
+
+ // define once
+ var stripRe = /[\$,%]/g;
+
+ // prebuilt conversion function for this field, instead of
+ // switching every time we're reading a value
+ if(!this.convert){
+ var cv, dateFormat = this.dateFormat;
+ switch(this.type){
+ case "":
+ case "auto":
+ case undefined:
+ cv = function(v){ return v; };
+ break;
+ case "string":
+ cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
+ break;
+ case "int":
+ cv = function(v){
+ return v !== undefined && v !== null && v !== '' ?
+ parseInt(String(v).replace(stripRe, ""), 10) : '';
+ };
+ break;
+ case "float":
+ cv = function(v){
+ return v !== undefined && v !== null && v !== '' ?
+ parseFloat(String(v).replace(stripRe, ""), 10) : '';
+ };
+ break;
+ case "bool":
+ case "boolean":
+ cv = function(v){ return v === true || v === "true" || v == 1; };
+ break;
+ case "date":
+ cv = function(v){
+ if(!v){
+ return '';
+ }
+ if(v instanceof Date){
+ return v;
+ }
+ if(dateFormat){
+ if(dateFormat == "timestamp"){
+ return new Date(v*1000);
+ }
+ return Date.parseDate(v, dateFormat);
+ }
+ var parsed = Date.parse(v);
+ return parsed ? new Date(parsed) : null;
+ };
+ break;
+
+ }
+ this.convert = cv;
+ }
+};
+
+Roo.data.Field.prototype = {
+ dateFormat: null,
+ defaultValue: "",
+ mapping: null,
+ sortType : null,
+ sortDir : "ASC"
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+// Base class for reading structured data from a data source. This class is intended to be
+// extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
+
+/**
+ * @class Roo.data.DataReader
+ * @abstract
+ * Base class for reading structured data from a data source. This class is intended to be
+ * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
+ */
+
+Roo.data.DataReader = function(meta, recordType){
+
+ this.meta = meta;
+
+ this.recordType = recordType instanceof Array ?
+ Roo.data.Record.create(recordType) : recordType;
+};
+
+Roo.data.DataReader.prototype = {
+
+
+ readerType : 'Data',
+ /**
+ * Create an empty record
+ * @param {Object} data (optional) - overlay some values
+ * @return {Roo.data.Record} record created.
+ */
+ newRow : function(d) {
+ var da = {};
+ this.recordType.prototype.fields.each(function(c) {
+ switch( c.type) {
+ case 'int' : da[c.name] = 0; break;
+ case 'date' : da[c.name] = new Date(); break;
+ case 'float' : da[c.name] = 0.0; break;
+ case 'boolean' : da[c.name] = false; break;
+ default : da[c.name] = ""; break;
+ }
+
+ });
+ return new this.recordType(Roo.apply(da, d));
+ }
+
+
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.DataProxy
+ * @extends Roo.util.Observable
+ * @abstract
+ * This class is an abstract base class for implementations which provide retrieval of
+ * unformatted data objects.<br>
+ * <p>
+ * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
+ * (of the appropriate type which knows how to parse the data object) to provide a block of
+ * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
+ * <p>
+ * Custom implementations must implement the load method as described in
+ * {@link Roo.data.HttpProxy#load}.
+ */
+Roo.data.DataProxy = function(){
+ this.addEvents({
+ /**
+ * @event beforeload
+ * Fires before a network request is made to retrieve a data object.
+ * @param {Object} This DataProxy object.
+ * @param {Object} params The params parameter to the load function.
+ */
+ beforeload : true,
+ /**
+ * @event load
+ * Fires before the load method's callback is called.
+ * @param {Object} This DataProxy object.
+ * @param {Object} o The data object.
+ * @param {Object} arg The callback argument object passed to the load function.
+ */
+ load : true,
+ /**
+ * @event loadexception
+ * Fires if an Exception occurs during data retrieval.
+ * @param {Object} This DataProxy object.
+ * @param {Object} o The data object.
+ * @param {Object} arg The callback argument object passed to the load function.
+ * @param {Object} e The Exception.
+ */
+ loadexception : true
+ });
+ Roo.data.DataProxy.superclass.constructor.call(this);
+};
+
+Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
+
+ /**
+ * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
+ */
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.data.MemoryProxy
+ * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
+ * to the Reader when its load method is called.
+ * @constructor
+ * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
+ */
+Roo.data.MemoryProxy = function(data){
+ if (data.data) {
+ data = data.data;
+ }
+ Roo.data.MemoryProxy.superclass.constructor.call(this);
+ this.data = data;
+};
+
+Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
+
+ /**
+ * Load data from the requested source (in this case an in-memory
+ * data object passed to the constructor), read the data object into
+ * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params This parameter is not used by the MemoryProxy class.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ */
+ load : function(params, reader, callback, scope, arg){
+ params = params || {};
+ var result;
+ try {
+ result = reader.readRecords(params.data ? params.data :this.data);
+ }catch(e){
+ this.fireEvent("loadexception", this, arg, null, e);
+ callback.call(scope, null, arg, false);
+ return;
+ }
+ callback.call(scope, result, arg, true);
+ },
+
+ // private
+ update : function(params, records){
+
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.data.HttpProxy
+ * @extends Roo.data.DataProxy
+ * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
+ * configured to reference a certain URL.<br><br>
+ * <p>
+ * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
+ * from which the running page was served.<br><br>
+ * <p>
+ * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
+ * <p>
+ * Be aware that to enable the browser to parse an XML document, the server must set
+ * the Content-Type header in the HTTP response to "text/xml".
+ * @constructor
+ * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
+ * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
+ * will be used to make the request.
+ */
+Roo.data.HttpProxy = function(conn){
+ Roo.data.HttpProxy.superclass.constructor.call(this);
+ // is conn a conn config or a real conn?
+ this.conn = conn;
+ this.useAjax = !conn || !conn.events;
+
+};
+
+Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
+ // thse are take from connection...
+
+ /**
+ * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
+ */
+ /**
+ * @cfg {Object} extraParams (Optional) An object containing properties which are used as
+ * extra parameters to each request made by this object. (defaults to undefined)
+ */
+ /**
+ * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
+ * to each request made by this object. (defaults to undefined)
+ */
+ /**
+ * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
+ */
+ /**
+ * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+ */
+ /**
+ * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
+ * @type Boolean
+ */
+
+
+ /**
+ * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
+ * @type Boolean
+ */
+ /**
+ * Return the {@link Roo.data.Connection} object being used by this Proxy.
+ * @return {Connection} The Connection object. This object may be used to subscribe to events on
+ * a finer-grained basis than the DataProxy events.
+ */
+ getConnection : function(){
+ return this.useAjax ? Roo.Ajax : this.conn;
+ },
+
+ /**
+ * Load data from the configured {@link Roo.data.Connection}, read the data object into
+ * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params An object containing properties which are to be used as HTTP parameters
+ * for the request to the remote server.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.Records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ */
+ load : function(params, reader, callback, scope, arg){
+ if(this.fireEvent("beforeload", this, params) !== false){
+ var o = {
+ params : params || {},
+ request: {
+ callback : callback,
+ scope : scope,
+ arg : arg
+ },
+ reader: reader,
+ callback : this.loadResponse,
+ scope: this
+ };
+ if(this.useAjax){
+ Roo.applyIf(o, this.conn);
+ if(this.activeRequest){
+ Roo.Ajax.abort(this.activeRequest);
+ }
+ this.activeRequest = Roo.Ajax.request(o);
+ }else{
+ this.conn.request(o);
+ }
+ }else{
+ callback.call(scope||this, null, arg, false);
+ }
+ },
+
+ // private
+ loadResponse : function(o, success, response){
+ delete this.activeRequest;
+ if(!success){
+ this.fireEvent("loadexception", this, o, response);
+ o.request.callback.call(o.request.scope, null, o.request.arg, false);
+ return;
+ }
+ var result;
+ try {
+ result = o.reader.read(response);
+ }catch(e){
+ o.success = false;
+ o.raw = { errorMsg : response.responseText };
+ this.fireEvent("loadexception", this, o, response, e);
+ o.request.callback.call(o.request.scope, o, o.request.arg, false);
+ return;
+ }
+
+ this.fireEvent("load", this, o, o.request.arg);
+ o.request.callback.call(o.request.scope, result, o.request.arg, true);
+ },
+
+ // private
+ update : function(dataSet){
+
+ },
+
+ // private
+ updateResponse : function(dataSet){
+
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.ScriptTagProxy
+ * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
+ * other than the originating domain of the running page.<br><br>
+ * <p>
+ * <em>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, rather than DataProxy.</em><br><br>
+ * <p>
+ * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
+ * source code that is used as the source inside a <script> tag.<br><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 by the ScriptTagProxy.
+ * 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(");");
+}
+</pre></code>
+ *
+ * @constructor
+ * @param {Object} config A configuration object.
+ */
+Roo.data.ScriptTagProxy = function(config){
+ Roo.data.ScriptTagProxy.superclass.constructor.call(this);
+ Roo.apply(this, config);
+ this.head = document.getElementsByTagName("head")[0];
+};
+
+Roo.data.ScriptTagProxy.TRANS_ID = 1000;
+
+Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
+ /**
+ * @cfg {String} url The URL from which to request the data object.
+ */
+ /**
+ * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
+ */
+ timeout : 30000,
+ /**
+ * @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 {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
+ * name to the request.
+ */
+ nocache : true,
+
+ /**
+ * Load data from the configured URL, read the data object into
+ * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params An object containing properties which are to be used as HTTP parameters
+ * for the request to the remote server.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.Records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ */
+ load : function(params, reader, callback, scope, arg){
+ if(this.fireEvent("beforeload", this, params) !== false){
+
+ var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
+
+ var url = this.url;
+ url += (url.indexOf("?") != -1 ? "&" : "?") + p;
+ if(this.nocache){
+ url += "&_dc=" + (new Date().getTime());
+ }
+ var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
+ var trans = {
+ id : transId,
+ cb : "stcCallback"+transId,
+ scriptId : "stcScript"+transId,
+ params : params,
+ arg : arg,
+ url : url,
+ callback : callback,
+ scope : scope,
+ reader : reader
+ };
+ var conn = this;
+
+ window[trans.cb] = function(o){
+ conn.handleResponse(o, trans);
+ };
+
+ url += String.format("&{0}={1}", this.callbackParam, trans.cb);
+
+ if(this.autoAbort !== false){
+ this.abort();
+ }
+
+ trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
+
+ var script = document.createElement("script");
+ script.setAttribute("src", url);
+ script.setAttribute("type", "text/javascript");
+ script.setAttribute("id", trans.scriptId);
+ this.head.appendChild(script);
+
+ this.trans = trans;
+ }else{
+ callback.call(scope||this, null, arg, false);
+ }
+ },
+
+ // private
+ isLoading : function(){
+ return this.trans ? true : false;
+ },
+
+ /**
+ * Abort the current server request.
+ */
+ abort : function(){
+ if(this.isLoading()){
+ this.destroyTrans(this.trans);
+ }
+ },
+
+ // private
+ destroyTrans : function(trans, isLoaded){
+ this.head.removeChild(document.getElementById(trans.scriptId));
+ clearTimeout(trans.timeoutId);
+ if(isLoaded){
+ window[trans.cb] = undefined;
+ try{
+ delete window[trans.cb];
+ }catch(e){}
+ }else{
+ // if hasn't been loaded, wait for load to remove it to prevent script error
+ window[trans.cb] = function(){
+ window[trans.cb] = undefined;
+ try{
+ delete window[trans.cb];
+ }catch(e){}
+ };
+ }
+ },
+
+ // private
+ handleResponse : function(o, trans){
+ this.trans = false;
+ this.destroyTrans(trans, true);
+ var result;
+ try {
+ result = trans.reader.readRecords(o);
+ }catch(e){
+ this.fireEvent("loadexception", this, o, trans.arg, e);
+ trans.callback.call(trans.scope||window, null, trans.arg, false);
+ return;
+ }
+ this.fireEvent("load", this, o, trans.arg);
+ trans.callback.call(trans.scope||window, result, trans.arg, true);
+ },
+
+ // private
+ handleFailure : function(trans){
+ this.trans = false;
+ this.destroyTrans(trans, false);
+ this.fireEvent("loadexception", this, null, trans.arg);
+ trans.callback.call(trans.scope||window, null, trans.arg, false);
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.JsonReader
+ * @extends Roo.data.DataReader
+ * Data reader class to create an Array of Roo.data.Record objects from a JSON response
+ * based on mappings in a provided Roo.data.Record constructor.
+ *
+ * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
+ * in the reply previously.
+ *
+ * <p>
+ * Example code:
+ * <pre><code>
+var RecordDef = Roo.data.Record.create([
+ {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
+ {name: 'occupation'} // This field will use "occupation" as the mapping.
+]);
+var myReader = new Roo.data.JsonReader({
+ totalProperty: "results", // The property which contains the total dataset size (optional)
+ root: "rows", // The property which contains an Array of row objects
+ id: "id" // The property within each row object that provides an ID for the record (optional)
+}, RecordDef);
+</code></pre>
+ * <p>
+ * This would consume a JSON file like this:
+ * <pre><code>
+{ 'results': 2, 'rows': [
+ { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
+ { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
+}
+</code></pre>
+ * @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.
+ * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
+ * @cfg {String} root name of the property which contains the Array of row objects.
+ * @cfg {String} id Name of the property within a row object that contains a record identifier value.
+ * @cfg {Array} fields Array of field definition objects
+ * @constructor
+ * Create a new JsonReader
+ * @param {Object} meta Metadata configuration options
+ * @param {Object} recordType Either an Array of field definition objects,
+ * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
+ */
+Roo.data.JsonReader = function(meta, recordType){
+
+ meta = meta || {};
+ // set some defaults:
+ Roo.applyIf(meta, {
+ totalProperty: 'total',
+ successProperty : 'success',
+ root : 'data',
+ id : 'id'
+ });
+
+ Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+};
+Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
+
+ readerType : 'Json',
+
+ /**
+ * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
+ * Used by Store query builder to append _requestMeta to params.
+ *
+ */
+ metaFromRemote : false,
+ /**
+ * This method is only used by a DataProxy which has retrieved data from a remote server.
+ * @param {Object} response The XHR object which contains the JSON data in its responseText.
+ * @return {Object} data A data block which is used by an Roo.data.Store object as
+ * a cache of Roo.data.Records.
+ */
+ read : function(response){
+ var json = response.responseText;
+
+ var o = /* eval:var:o */ eval("("+json+")");
+ if(!o) {
+ throw {message: "JsonReader.read: Json object not found"};
+ }
+
+ if(o.metaData){
+
+ delete this.ef;
+ this.metaFromRemote = true;
+ this.meta = o.metaData;
+ this.recordType = Roo.data.Record.create(o.metaData.fields);
+ this.onMetaChange(this.meta, this.recordType, o);
+ }
+ return this.readRecords(o);
+ },
+
+ // private function a store will implement
+ onMetaChange : function(meta, recordType, o){
+
+ },
+
+ /**
+ * @ignore
+ */
+ simpleAccess: function(obj, subsc) {
+ return obj[subsc];
+ },
+
+ /**
+ * @ignore
+ */
+ getJsonAccessor: function(){
+ var re = /[\[\.]/;
+ return function(expr) {
+ try {
+ return(re.test(expr))
+ ? new Function("obj", "return obj." + expr)
+ : function(obj){
+ return obj[expr];
+ };
+ } catch(e){}
+ return Roo.emptyFn;
+ };
+ }(),
+
+ /**
+ * Create a data block containing Roo.data.Records from an XML document.
+ * @param {Object} o An object which contains an Array of row objects in the property specified
+ * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
+ * which contains the total size of the dataset.
+ * @return {Object} data A data block which is used by an Roo.data.Store object as
+ * a cache of Roo.data.Records.
+ */
+ readRecords : function(o){
+ /**
+ * After any data loads, the raw JSON data is available for further custom processing.
+ * @type Object
+ */
+ this.o = o;
+ var s = this.meta, Record = this.recordType,
+ f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
+
+// Generate extraction functions for the totalProperty, the root, the id, and for each field
+ if (!this.ef) {
+ if(s.totalProperty) {
+ this.getTotal = this.getJsonAccessor(s.totalProperty);
+ }
+ if(s.successProperty) {
+ this.getSuccess = this.getJsonAccessor(s.successProperty);
+ }
+ this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
+ if (s.id) {
+ var g = this.getJsonAccessor(s.id);
+ this.getId = function(rec) {
+ var r = g(rec);
+ return (r === undefined || r === "") ? null : r;
+ };
+ } else {
+ this.getId = function(){return null;};
+ }
+ this.ef = [];
+ for(var jj = 0; jj < fl; jj++){
+ f = fi[jj];
+ var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
+ this.ef[jj] = this.getJsonAccessor(map);
+ }
+ }
+
+ var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
+ if(s.totalProperty){
+ var vt = parseInt(this.getTotal(o), 10);
+ if(!isNaN(vt)){
+ totalRecords = vt;
+ }
+ }
+ if(s.successProperty){
+ var vs = this.getSuccess(o);
+ if(vs === false || vs === 'false'){
+ success = false;
+ }
+ }
+ var records = [];
+ for(var i = 0; i < c; i++){
+ var n = root[i];
+ var values = {};
+ var id = this.getId(n);
+ for(var j = 0; j < fl; j++){
+ f = fi[j];
+ var v = this.ef[j](n);
+ if (!f.convert) {
+ Roo.log('missing convert for ' + f.name);
+ Roo.log(f);
+ continue;
+ }
+ values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
+ }
+ if (!Record) {
+ return {
+ raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
+ success : false,
+ records : [],
+ totalRecords : 0
+ };
+ }
+ var record = new Record(values, id);
+ record.json = n;
+ records[i] = record;
+ }
+ return {
+ raw : o,
+ success : success,
+ records : records,
+ totalRecords : totalRecords
+ };
+ },
+ // used when loading children.. @see loadDataFromChildren
+ toLoadData: function(rec)
+ {
+ // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
+ var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
+ return { data : data, total : data.length };
+
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.ArrayReader
+ * @extends Roo.data.DataReader
+ * Data reader class to create an Array of Roo.data.Record 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 <em>mapping</em> property
+ * of the field definition if it exists, or the field's ordinal position in the definition.<br>
+ * <p>
+ * Example code:.
+ * <pre><code>
+var RecordDef = Roo.data.Record.create([
+ {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 Roo.data.ArrayReader({
+ id: 0 // The subscript within row Array that provides an ID for the Record (optional)
+}, RecordDef);
+</code></pre>
+ * <p>
+ * This would consume an Array like this:
+ * <pre><code>
+[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
+ </code></pre>
+
+ * @constructor
+ * Create a new JsonReader
+ * @param {Object} meta Metadata configuration options.
+ * @param {Object|Array} recordType Either an Array of field definition objects
+ *
+ * @cfg {Array} fields Array of field definition objects
+ * @cfg {String} id Name of the property within a row object that contains a record identifier value.
+ * as specified to {@link Roo.data.Record#create},
+ * or an {@link Roo.data.Record} object
+ *
+ *
+ * created using {@link Roo.data.Record#create}.
+ */
+Roo.data.ArrayReader = function(meta, recordType)
+{
+ Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+};
+
+Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
+
+ /**
+ * Create a data block containing Roo.data.Records from an XML document.
+ * @param {Object} o An Array of row objects which represents the dataset.
+ * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
+ * a cache of Roo.data.Records.
+ */
+ readRecords : function(o)
+ {
+ var sid = this.meta ? this.meta.id : null;
+ var recordType = this.recordType, fields = recordType.prototype.fields;
+ var records = [];
+ var root = o;
+ for(var i = 0; i < root.length; i++){
+ var n = root[i];
+ var values = {};
+ var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
+ for(var j = 0, jlen = fields.length; j < jlen; j++){
+ var f = fields.items[j];
+ var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
+ var v = n[k] !== undefined ? n[k] : f.defaultValue;
+ v = f.convert(v);
+ values[f.name] = v;
+ }
+ var record = new recordType(values, id);
+ record.json = n;
+ records[records.length] = record;
+ }
+ return {
+ records : records,
+ totalRecords : records.length
+ };
+ },
+ // used when loading children.. @see loadDataFromChildren
+ toLoadData: function(rec)
+ {
+ // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
+ return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
+
+ }
+
+
+});/*
+ * - LGPL
+ * *
+ */
+
+/**
+ * @class Roo.bootstrap.form.ComboBox
+ * @extends Roo.bootstrap.form.TriggerField
+ * A combobox control with support for autocomplete, remote-loading, paging and many other features.
+ * @cfg {Boolean} append (true|false) default false
+ * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
+ * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
+ * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
+ * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
+ * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
+ * @cfg {Boolean} animate default true
+ * @cfg {Boolean} emptyResultText only for touch device
+ * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
+ * @cfg {String} emptyTitle default ''
+ * @cfg {Number} width fixed with? experimental
+ * @constructor
+ * Create a new ComboBox.
+ * @param {Object} config Configuration options
+ */
+Roo.bootstrap.form.ComboBox = function(config){
+ Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event expand
+ * Fires when the dropdown list is expanded
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'expand' : true,
+ /**
+ * @event collapse
+ * Fires when the dropdown list is collapsed
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'collapse' : true,
+ /**
+ * @event beforeselect
+ * Fires before a list item is selected. Return false to cancel the selection.
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {Roo.data.Record} record The data record returned from the underlying store
+ * @param {Number} index The index of the selected item in the dropdown list
+ */
+ 'beforeselect' : true,
+ /**
+ * @event select
+ * Fires when a list item is selected
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
+ * @param {Number} index The index of the selected item in the dropdown list
+ */
+ 'select' : true,
+ /**
+ * @event beforequery
+ * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
+ * The event object passed has these properties:
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {String} query The query
+ * @param {Boolean} forceAll true to force "all" query
+ * @param {Boolean} cancel true to cancel the query
+ * @param {Object} e The query event object
+ */
+ 'beforequery': true,
+ /**
+ * @event add
+ * Fires when the 'add' icon is pressed (add a listener to enable add button)
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'add' : true,
+ /**
+ * @event edit
+ * Fires when the 'edit' icon is pressed (add a listener to enable add button)
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
+ */
+ 'edit' : true,
+ /**
+ * @event remove
+ * Fires when the remove value from the combobox array
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'remove' : true,
+ /**
+ * @event afterremove
+ * Fires when the remove value from the combobox array
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'afterremove' : true,
+ /**
+ * @event specialfilter
+ * Fires when specialfilter
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'specialfilter' : true,
+ /**
+ * @event tick
+ * Fires when tick the element
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'tick' : true,
+ /**
+ * @event touchviewdisplay
+ * Fires when touch view require special display (default is using displayField)
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {Object} cfg set html .
+ */
+ 'touchviewdisplay' : true
+
+ });
+
+ this.item = [];
+ this.tickItems = [];
+
+ this.selectedIndex = -1;
+ if(this.mode == 'local'){
+ if(config.queryDelay === undefined){
+ this.queryDelay = 10;
+ }
+ if(config.minChars === undefined){
+ this.minChars = 0;
+ }
+ }
+};
+
+Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
+
+ /**
+ * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
+ * rendering into an Roo.Editor, defaults to false)
+ */
+ /**
+ * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
+ * {tag: "input", type: "text", size: "24", autocomplete: "off"})
+ */
+ /**
+ * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
+ */
+ /**
+ * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
+ * the dropdown list (defaults to undefined, with no header element)
+ */
+
+ /**
+ * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
+ */
+
+ /**
+ * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
+ */
+ listWidth: undefined,
+ /**
+ * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
+ * mode = 'remote' or 'text' if mode = 'local')
+ */
+ displayField: undefined,
+
+ /**
+ * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
+ * mode = 'remote' or 'value' if mode = 'local').
+ * Note: use of a valueField requires the user make a selection
+ * in order for a value to be mapped.
+ */
+ valueField: undefined,
+ /**
+ * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
+ */
+ modalTitle : '',
+
+ /**
+ * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
+ * field's data value (defaults to the underlying DOM element's name)
+ */
+ hiddenName: undefined,
+ /**
+ * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
+ */
+ listClass: '',
+ /**
+ * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
+ */
+ selectedClass: 'active',
+
+ /**
+ * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
+ */
+ shadow:'sides',
+ /**
+ * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
+ * anchor positions (defaults to 'tl-bl')
+ */
+ listAlign: 'tl-bl?',
+ /**
+ * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
+ */
+ maxHeight: 300,
+ /**
+ * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
+ * query specified by the allQuery config option (defaults to 'query')
+ */
+ triggerAction: 'query',
+ /**
+ * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
+ * (defaults to 4, does not apply if editable = false)
+ */
+ minChars : 4,
+ /**
+ * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
+ * delay (typeAheadDelay) if it matches a known value (defaults to false)
+ */
+ typeAhead: false,
+ /**
+ * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
+ * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
+ */
+ queryDelay: 500,
+ /**
+ * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
+ * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
+ */
+ pageSize: 0,
+ /**
+ * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
+ * when editable = true (defaults to false)
+ */
+ selectOnFocus:false,
+ /**
+ * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
+ */
+ queryParam: 'query',
+ /**
+ * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
+ * when mode = 'remote' (defaults to 'Loading...')
+ */
+ loadingText: 'Loading...',
+ /**
+ * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
+ */
+ resizable: false,
+ /**
+ * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
+ */
+ handleHeight : 8,
+ /**
+ * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
+ * traditional select (defaults to true)
+ */
+ editable: true,
+ /**
+ * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
+ */
+ allQuery: '',
+ /**
+ * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
+ */
+ mode: 'remote',
+ /**
+ * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
+ * listWidth has a higher value)
+ */
+ minListWidth : 70,
+ /**
+ * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
+ * allow the user to set arbitrary text into the field (defaults to false)
+ */
+ forceSelection:false,
+ /**
+ * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
+ * if typeAhead = true (defaults to 250)
+ */
+ typeAheadDelay : 250,
+ /**
+ * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
+ * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
+ */
+ valueNotFoundText : undefined,
+ /**
+ * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
+ */
+ blockFocus : false,
+
+ /**
+ * @cfg {Boolean} disableClear Disable showing of clear button.
+ */
+ disableClear : false,
+ /**
+ * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
+ */
+ alwaysQuery : false,
+
+ /**
+ * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
+ */
+ multiple : false,
+
+ /**
+ * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
+ */
+ invalidClass : "has-warning",
+
+ /**
+ * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
+ */
+ validClass : "has-success",
+
+ /**
+ * @cfg {Boolean} specialFilter (true|false) special filter default false
+ */
+ specialFilter : false,
+
+ /**
+ * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
+ */
+ mobileTouchView : true,
+
+ /**
+ * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
+ */
+ useNativeIOS : false,
+
+ /**
+ * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
+ */
+ mobile_restrict_height : false,
+
+ ios_options : false,
+
+ //private
+ addicon : false,
+ editicon: false,
+
+ page: 0,
+ hasQuery: false,
+ append: false,
+ loadNext: false,
+ autoFocus : true,
+ tickable : false,
+ btnPosition : 'right',
+ triggerList : true,
+ showToggleBtn : true,
+ animate : true,
+ emptyResultText: 'Empty',
+ triggerText : 'Select',
+ emptyTitle : '',
+ width : false,
+
+ // element that contains real text value.. (when hidden is used..)
+
+ getAutoCreate : function()
+ {
+ var cfg = false;
+ //render
+ /*
+ * Render classic select for iso
+ */
+
+ if(Roo.isIOS && this.useNativeIOS){
+ cfg = this.getAutoCreateNativeIOS();
+ return cfg;
+ }
+
+ /*
+ * Touch Devices
+ */
+
+ if(Roo.isTouch && this.mobileTouchView){
+ cfg = this.getAutoCreateTouchView();
+ return cfg;;
+ }
+
+ /*
+ * Normal ComboBox
+ */
+ if(!this.tickable){
+ cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
+ return cfg;
+ }
+
+ /*
+ * ComboBox with tickable selections
+ */
+
+ var align = this.labelAlign || this.parentLabelAlign();
+
+ cfg = {
+ cls : 'form-group roo-combobox-tickable' //input-group
+ };
+
+ var btn_text_select = '';
+ var btn_text_done = '';
+ var btn_text_cancel = '';
+
+ if (this.btn_text_show) {
+ btn_text_select = 'Select';
+ btn_text_done = 'Done';
+ btn_text_cancel = 'Cancel';
+ }
+
+ var buttons = {
+ tag : 'div',
+ cls : 'tickable-buttons',
+ cn : [
+ {
+ tag : 'button',
+ type : 'button',
+ cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
+ //html : this.triggerText
+ html: btn_text_select
+ },
+ {
+ tag : 'button',
+ type : 'button',
+ name : 'ok',
+ cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
+ //html : 'Done'
+ html: btn_text_done
+ },
+ {
+ tag : 'button',
+ type : 'button',
+ name : 'cancel',
+ cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
+ //html : 'Cancel'
+ html: btn_text_cancel
+ }
+ ]
+ };
+
+ if(this.editable){
+ buttons.cn.unshift({
+ tag: 'input',
+ cls: 'roo-select2-search-field-input'
+ });
+ }
+
+ var _this = this;
+
+ Roo.each(buttons.cn, function(c){
+ if (_this.size) {
+ c.cls += ' btn-' + _this.size;
+ }
+
+ if (_this.disabled) {
+ c.disabled = true;
+ }
+ });
+
+ var box = {
+ tag: 'div',
+ style : 'display: contents',
+ cn: [
+ {
+ tag: 'input',
+ type : 'hidden',
+ cls: 'form-hidden-field'
+ },
+ {
+ tag: 'ul',
+ cls: 'roo-select2-choices',
+ cn:[
+ {
+ tag: 'li',
+ cls: 'roo-select2-search-field',
+ cn: [
+ buttons
+ ]
+ }
+ ]
+ }
+ ]
+ };
+
+ var combobox = {
+ cls: 'roo-select2-container input-group roo-select2-container-multi',
+ cn: [
+
+ box
+// {
+// tag: 'ul',
+// cls: 'typeahead typeahead-long dropdown-menu',
+// style: 'display:none; max-height:' + this.maxHeight + 'px;'
+// }
+ ]
+ };
+
+ if(this.hasFeedback && !this.allowBlank){
+
+ var feedback = {
+ tag: 'span',
+ cls: 'glyphicon form-control-feedback'
+ };
+
+ combobox.cn.push(feedback);
+ }
+
+
+
+ var indicator = {
+ tag : 'i',
+ cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required'
+ };
+ if (Roo.bootstrap.version == 4) {
+ indicator = {
+ tag : 'i',
+ style : 'display:none'
+ };
+ }
+ if (align ==='left' && this.fieldLabel.length) {
+
+ cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
+
+ cfg.cn = [
+ indicator,
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-form-label',
+ html : this.fieldLabel
+
+ },
+ {
+ cls : "",
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+
+ var labelCfg = cfg.cn[1];
+ var contentCfg = cfg.cn[2];
+
+
+ if(this.indicatorpos == 'right'){
+
+ cfg.cn = [
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-form-label',
+ cn : [
+ {
+ tag : 'span',
+ html : this.fieldLabel
+ },
+ indicator
+ ]
+ },
+ {
+ cls : "",
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+
+
+
+ labelCfg = cfg.cn[0];
+ contentCfg = cfg.cn[1];
+
+ }
+
+ if(this.labelWidth > 12){
+ labelCfg.style = "width: " + this.labelWidth + 'px';
+ }
+ if(this.width * 1 > 0){
+ contentCfg.style = "width: " + this.width + 'px';
+ }
+ if(this.labelWidth < 13 && this.labelmd == 0){
+ this.labelmd = this.labelWidth;
+ }
+
+ if(this.labellg > 0){
+ labelCfg.cls += ' col-lg-' + this.labellg;
+ contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+ }
+
+ if(this.labelmd > 0){
+ labelCfg.cls += ' col-md-' + this.labelmd;
+ contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ }
+
+ if(this.labelsm > 0){
+ labelCfg.cls += ' col-sm-' + this.labelsm;
+ contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ }
+
+ if(this.labelxs > 0){
+ labelCfg.cls += ' col-xs-' + this.labelxs;
+ contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ }
+
+
+ } else if ( this.fieldLabel.length) {
+// Roo.log(" label");
+ cfg.cn = [
+ indicator,
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+ },
+ combobox
+ ];
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+ },
+ indicator,
+ combobox
+ ];
+
+ }
+
+ } else {
+
+// Roo.log(" no label && no align");
+ cfg = combobox
+
+
+ }
+
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ return cfg;
+
+ },
+
+ _initEventsCalled : false,
+
+ // private
+ initEvents: function()
+ {
+ if (this._initEventsCalled) { // as we call render... prevent looping...
+ return;
+ }
+ this._initEventsCalled = true;
+
+ if (!this.store) {
+ throw "can not find store for combo";
+ }
+
+ this.indicator = this.indicatorEl();
+
+ this.store = Roo.factory(this.store, Roo.data);
+ this.store.parent = this;
+
+ // if we are building from html. then this element is so complex, that we can not really
+ // use the rendered HTML.
+ // so we have to trash and replace the previous code.
+ if (Roo.XComponent.build_from_html) {
+ // remove this element....
+ var e = this.el.dom, k=0;
+ while (e ) { e = e.previousSibling; ++k;}
+
+ this.el.remove();
+
+ this.el=false;
+ this.rendered = false;
+
+ this.render(this.parent().getChildContainer(true), k);
+ }
+
+ if(Roo.isIOS && this.useNativeIOS){
+ this.initIOSView();
+ return;
+ }
+
+ /*
+ * Touch Devices
+ */
+
+ if(Roo.isTouch && this.mobileTouchView){
+ this.initTouchView();
+ return;
+ }
+
+ if(this.tickable){
+ this.initTickableEvents();
+ return;
+ }
+
+ Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
+
+ if(this.hiddenName){
+
+ this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+
+ this.hiddenField.dom.value =
+ this.hiddenValue !== undefined ? this.hiddenValue :
+ this.value !== undefined ? this.value : '';
+
+ // prevent input submission
+ this.el.dom.removeAttribute('name');
+ this.hiddenField.dom.setAttribute('name', this.hiddenName);
+
+
+ }
+ //if(Roo.isGecko){
+ // this.el.dom.setAttribute('autocomplete', 'off');
+ //}
+
+ var cls = 'x-combo-list';
+
+ //this.list = new Roo.Layer({
+ // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
+ //});
+
+ var _this = this;
+
+ (function(){
+ var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
+ _this.list.setWidth(lw);
+ }).defer(100);
+
+ this.list.on('mouseover', this.onViewOver, this);
+ this.list.on('mousemove', this.onViewMove, this);
+ this.list.on('scroll', this.onViewScroll, this);
+
+ /*
+ this.list.swallowEvent('mousewheel');
+ this.assetHeight = 0;
+
+ if(this.title){
+ this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
+ this.assetHeight += this.header.getHeight();
+ }
+
+ this.innerList = this.list.createChild({cls:cls+'-inner'});
+ this.innerList.on('mouseover', this.onViewOver, this);
+ this.innerList.on('mousemove', this.onViewMove, this);
+ this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+
+ if(this.allowBlank && !this.pageSize && !this.disableClear){
+ this.footer = this.list.createChild({cls:cls+'-ft'});
+ this.pageTb = new Roo.Toolbar(this.footer);
+
+ }
+ if(this.pageSize){
+ this.footer = this.list.createChild({cls:cls+'-ft'});
+ this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
+ {pageSize: this.pageSize});
+
+ }
+
+ if (this.pageTb && this.allowBlank && !this.disableClear) {
+ var _this = this;
+ this.pageTb.add(new Roo.Toolbar.Fill(), {
+ cls: 'x-btn-icon x-btn-clear',
+ text: ' ',
+ handler: function()
+ {
+ _this.collapse();
+ _this.clearValue();
+ _this.onSelect(false, -1);
+ }
+ });
+ }
+ if (this.footer) {
+ this.assetHeight += this.footer.getHeight();
+ }
+ */
+
+ if(!this.tpl){
+ this.tpl = Roo.bootstrap.version == 4 ?
+ '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
+ '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
+ }
+
+ this.view = new Roo.View(this.list, this.tpl, {
+ singleSelect:true, store: this.store, selectedClass: this.selectedClass
+ });
+ //this.view.wrapEl.setDisplayed(false);
+ this.view.on('click', this.onViewClick, this);
+
+
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('loadexception', this.onLoadException, this);
+ /*
+ if(this.resizable){
+ this.resizer = new Roo.Resizable(this.list, {
+ pinned:true, handles:'se'
+ });
+ this.resizer.on('resize', function(r, w, h){
+ this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
+ this.listWidth = w;
+ this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
+ this.restrictHeight();
+ }, this);
+ this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
+ }
+ */
+ if(!this.editable){
+ this.editable = true;
+ this.setEditable(false);
+ }
+
+ /*
+
+ if (typeof(this.events.add.listeners) != 'undefined') {
+
+ this.addicon = this.wrap.createChild(
+ {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
+
+ this.addicon.on('click', function(e) {
+ this.fireEvent('add', this);
+ }, this);
+ }
+ if (typeof(this.events.edit.listeners) != 'undefined') {
+
+ this.editicon = this.wrap.createChild(
+ {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
+ if (this.addicon) {
+ this.editicon.setStyle('margin-left', '40px');
+ }
+ this.editicon.on('click', function(e) {
+
+ // we fire even if inothing is selected..
+ this.fireEvent('edit', this, this.lastData );
+
+ }, this);
+ }
+ */
+
+ this.keyNav = new Roo.KeyNav(this.inputEl(), {
+ "up" : function(e){
+ this.inKeyMode = true;
+ this.selectPrev();
+ },
+
+ "down" : function(e){
+ if(!this.isExpanded()){
+ this.onTriggerClick();
+ }else{
+ this.inKeyMode = true;
+ this.selectNext();
+ }
+ },
+
+ "enter" : function(e){
+// this.onViewClick();
+ //return true;
+ this.collapse();
+
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
+ },
+
+ "esc" : function(e){
+ this.collapse();
+ },
+
+ "tab" : function(e){
+ this.collapse();
+
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
+ },
+
+ scope : this,
+
+ doRelay : function(foo, bar, hname){
+ if(hname == 'down' || this.scope.isExpanded()){
+ return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+ }
+ return true;
+ },
+
+ forceKeyDown: true
+ });
+
+
+ this.queryDelay = Math.max(this.queryDelay || 10,
+ this.mode == 'local' ? 10 : 250);
+
+
+ this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
+
+ if(this.typeAhead){
+ this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
+ }
+ if(this.editable !== false){
+ this.inputEl().on("keyup", this.onKeyUp, this);
+ }
+ if(this.forceSelection){
+ this.inputEl().on('blur', this.doForce, this);
+ }
+
+ if(this.multiple){
+ this.choices = this.el.select('ul.roo-select2-choices', true).first();
+ this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+ }
+ },
+
+ initTickableEvents: function()
+ {
+ this.createList();
+
+ if(this.hiddenName){
+
+ this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+
+ this.hiddenField.dom.value =
+ this.hiddenValue !== undefined ? this.hiddenValue :
+ this.value !== undefined ? this.value : '';
+
+ // prevent input submission
+ this.el.dom.removeAttribute('name');
+ this.hiddenField.dom.setAttribute('name', this.hiddenName);
+
+
+ }
+
+// this.list = this.el.select('ul.dropdown-menu',true).first();
+
+ this.choices = this.el.select('ul.roo-select2-choices', true).first();
+ this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+ if(this.triggerList){
+ this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
+ }
+
+ this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
+ this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
+
+ this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
+ this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
+
+ this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
+ this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
+
+ this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
+ this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
+ this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.okBtn.hide();
+ this.cancelBtn.hide();
+
+ var _this = this;
+
+ (function(){
+ var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
+ _this.list.setWidth(lw);
+ }).defer(100);
+
+ this.list.on('mouseover', this.onViewOver, this);
+ this.list.on('mousemove', this.onViewMove, this);
+
+ this.list.on('scroll', this.onViewScroll, this);
+
+ if(!this.tpl){
+ this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
+ 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
+ }
+
+ this.view = new Roo.View(this.list, this.tpl, {
+ singleSelect:true,
+ tickable:true,
+ parent:this,
+ store: this.store,
+ selectedClass: this.selectedClass
+ });
+
+ //this.view.wrapEl.setDisplayed(false);
+ this.view.on('click', this.onViewClick, this);
+
+
+
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('loadexception', this.onLoadException, this);
+
+ if(this.editable){
+ this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
+ "up" : function(e){
+ this.inKeyMode = true;
+ this.selectPrev();
+ },
+
+ "down" : function(e){
+ this.inKeyMode = true;
+ this.selectNext();
+ },
+
+ "enter" : function(e){
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
+ },
+
+ "esc" : function(e){
+ this.onTickableFooterButtonClick(e, false, false);
+ },
+
+ "tab" : function(e){
+ this.fireEvent("specialkey", this, e);
+
+ this.onTickableFooterButtonClick(e, false, false);
+
+ return true;
+ },
+
+ scope : this,
+
+ doRelay : function(e, fn, key){
+ if(this.scope.isExpanded()){
+ return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+ }
+ return true;
+ },
+
+ forceKeyDown: true
+ });
+ }
+
+ this.queryDelay = Math.max(this.queryDelay || 10,
+ this.mode == 'local' ? 10 : 250);
+
+
+ this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
+
+ if(this.typeAhead){
+ this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
+ }
+
+ if(this.editable !== false){
+ this.tickableInputEl().on("keyup", this.onKeyUp, this);
+ }
+
+ this.indicator = this.indicatorEl();
+
+ if(this.indicator){
+ this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
+ this.indicator.hide();
+ }
+
+ },
+
+ onDestroy : function(){
+ if(this.view){
+ this.view.setStore(null);
+ this.view.el.removeAllListeners();
+ this.view.el.remove();
+ this.view.purgeListeners();
+ }
+ if(this.list){
+ this.list.dom.innerHTML = '';
+ }
+
+ if(this.store){
+ this.store.un('beforeload', this.onBeforeLoad, this);
+ this.store.un('load', this.onLoad, this);
+ this.store.un('loadexception', this.onLoadException, this);
+ }
+ Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
+ },
+
+ // private
+ fireKey : function(e){
+ if(e.isNavKeyPress() && !this.list.isVisible()){
+ this.fireEvent("specialkey", this, e);
+ }
+ },
+
+ // private
+ onResize: function(w, h)
+ {
+
+
+// Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
+//
+// if(typeof w != 'number'){
+// // we do not handle it!?!?
+// return;
+// }
+// var tw = this.trigger.getWidth();
+// // tw += this.addicon ? this.addicon.getWidth() : 0;
+// // tw += this.editicon ? this.editicon.getWidth() : 0;
+// var x = w - tw;
+// this.inputEl().setWidth( this.adjustWidth('input', x));
+//
+// //this.trigger.setStyle('left', x+'px');
+//
+// if(this.list && this.listWidth === undefined){
+// var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
+// this.list.setWidth(lw);
+// this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+// }
+
+
+
+ },
+
+ /**
+ * Allow or prevent the user from directly editing the field text. If false is passed,
+ * the user will only be able to select from the items defined in the dropdown list. This method
+ * is the runtime equivalent of setting the 'editable' config option at config time.
+ * @param {Boolean} value True to allow the user to directly edit the field text
+ */
+ setEditable : function(value){
+ if(value == this.editable){
+ return;
+ }
+ this.editable = value;
+ if(!value){
+ this.inputEl().dom.setAttribute('readOnly', true);
+ this.inputEl().on('mousedown', this.onTriggerClick, this);
+ this.inputEl().addClass('x-combo-noedit');
+ }else{
+ this.inputEl().dom.removeAttribute('readOnly');
+ this.inputEl().un('mousedown', this.onTriggerClick, this);
+ this.inputEl().removeClass('x-combo-noedit');
+ }
+ },
+
+ // private
+
+ onBeforeLoad : function(combo,opts){
+ if(!this.hasFocus){
+ return;
+ }
+ if (!opts.add) {
+ this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
+ }
+ this.restrictHeight();
+ this.selectedIndex = -1;
+ },
+
+ // private
+ onLoad : function(){
+
+ this.hasQuery = false;
+
+ if(!this.hasFocus){
+ return;
+ }
+
+ if(typeof(this.loading) !== 'undefined' && this.loading !== null){
+ this.loading.hide();
+ }
+
+ if(this.store.getCount() > 0){
+
+ this.expand();
+ this.restrictHeight();
+ if(this.lastQuery == this.allQuery){
+ if(this.editable && !this.tickable){
+ this.inputEl().dom.select();
+ }
+
+ if(
+ !this.selectByValue(this.value, true) &&
+ this.autoFocus &&
+ (
+ !this.store.lastOptions ||
+ typeof(this.store.lastOptions.add) == 'undefined' ||
+ this.store.lastOptions.add != true
+ )
+ ){
+ this.select(0, true);
+ }
+ }else{
+ if(this.autoFocus){
+ this.selectNext();
+ }
+ if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
+ this.taTask.delay(this.typeAheadDelay);
+ }
+ }
+ }else{
+ this.onEmptyResults();
+ }
+
+ //this.el.focus();
+ },
+ // private
+ onLoadException : function()
+ {
+ this.hasQuery = false;
+
+ if(typeof(this.loading) !== 'undefined' && this.loading !== null){
+ this.loading.hide();
+ }
+
+ if(this.tickable && this.editable){
+ return;
+ }
+
+ this.collapse();
+ // only causes errors at present
+ //Roo.log(this.store.reader.jsonData);
+ //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+ // fixme
+ //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+ //}
+
+
+ },
+ // private
+ onTypeAhead : function(){
+ if(this.store.getCount() > 0){
+ var r = this.store.getAt(0);
+ var newValue = r.data[this.displayField];
+ var len = newValue.length;
+ var selStart = this.getRawValue().length;
+
+ if(selStart != len){
+ this.setRawValue(newValue);
+ this.selectText(selStart, newValue.length);
+ }
+ }
+ },
+
+ // private
+ onSelect : function(record, index){
+
+ if(this.fireEvent('beforeselect', this, record, index) !== false){
+
+ this.setFromData(index > -1 ? record.data : false);
+
+ this.collapse();
+ this.fireEvent('select', this, record, index);
+ }
+ },
+
+ /**
+ * Returns the currently selected field value or empty string if no value is set.
+ * @return {String} value The selected value
+ */
+ getValue : function()
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
+ }
+
+ if(this.multiple){
+ return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
+ }
+
+ if(this.valueField){
+ return typeof this.value != 'undefined' ? this.value : '';
+ }else{
+ return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
+ }
+ },
+
+ getRawValue : function()
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
+ }
+
+ var v = this.inputEl().getValue();
+
+ return v;
+ },
+
+ /**
+ * Clears any text/value currently set in the field
+ */
+ clearValue : function(){
+
+ if(this.hiddenField){
+ this.hiddenField.dom.value = '';
+ }
+ this.value = '';
+ this.setRawValue('');
+ this.lastSelectionText = '';
+ this.lastData = false;
+
+ var close = this.closeTriggerEl();
+
+ if(close){
+ close.hide();
+ }
+
+ this.validate();
+
+ },
+
+ /**
+ * Sets the specified value into the field. If the value finds a match, the corresponding record text
+ * will be displayed in the field. If the value does not match the data value of an existing item,
+ * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
+ * Otherwise the field will be blank (although the value will still be set).
+ * @param {String} value The value to match
+ */
+ setValue : function(v)
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ this.setIOSValue(v);
+ return;
+ }
+
+ if(this.multiple){
+ this.syncValue();
+ return;
+ }
+
+ var text = v;
+ if(this.valueField){
+ var r = this.findRecord(this.valueField, v);
+ if(r){
+ text = r.data[this.displayField];
+ }else if(this.valueNotFoundText !== undefined){
+ text = this.valueNotFoundText;
+ }
+ }
+ this.lastSelectionText = text;
+ if(this.hiddenField){
+ this.hiddenField.dom.value = v;
+ }
+ Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
+ this.value = v;
+
+ var close = this.closeTriggerEl();
+
+ if(close){
+ (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
+ }
+
+ this.validate();
+ },
+ /**
+ * @property {Object} the last set data for the element
+ */
+
+ lastData : false,
+ /**
+ * Sets the value of the field based on a object which is related to the record format for the store.
+ * @param {Object} value the value to set as. or false on reset?
+ */
+ setFromData : function(o){
+
+ if(this.multiple){
+ this.addItem(o);
+ return;
+ }
+
+ var dv = ''; // display value
+ var vv = ''; // value value..
+ this.lastData = o;
+ if (this.displayField) {
+ dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+ } else {
+ // this is an error condition!!!
+ Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
+ }
+
+ if(this.valueField){
+ vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
+ }
+
+ var close = this.closeTriggerEl();
+
+ if(close){
+ if(dv.length || vv * 1 > 0){
+ close.show() ;
+ this.blockFocus=true;
+ } else {
+ close.hide();
+ }
+ }
+
+ if(this.hiddenField){
+ this.hiddenField.dom.value = vv;
+
+ this.lastSelectionText = dv;
+ Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
+ this.value = vv;
+ return;
+ }
+ // no hidden field.. - we store the value in 'value', but still display
+ // display field!!!!
+ this.lastSelectionText = dv;
+ Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
+ this.value = vv;
+
+
+
+ },
+ // private
+ reset : function(){
+ // overridden so that last data is reset..
+
+ if(this.multiple){
+ this.clearItem();
+ return;
+ }
+
+ this.setValue(this.originalValue);
+ //this.clearInvalid();
+ this.lastData = false;
+ if (this.view) {
+ this.view.clearSelections();
+ }
+
+ this.validate();
+ },
+ // private
+ findRecord : function(prop, value){
+ var record;
+ if(this.store.getCount() > 0){
+ this.store.each(function(r){
+ if(r.data[prop] == value){
+ record = r;
+ return false;
+ }
+ return true;
+ });
+ }
+ return record;
+ },
+
+ getName: function()
+ {
+ // returns hidden if it's set..
+ if (!this.rendered) {return ''};
+ return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
+
+ },
+ // private
+ onViewMove : function(e, t){
+ this.inKeyMode = false;
+ },
+
+ // private
+ onViewOver : function(e, t){
+ if(this.inKeyMode){ // prevent key nav and mouse over conflicts
+ return;
+ }
+ var item = this.view.findItemFromChild(t);
+
+ if(item){
+ var index = this.view.indexOf(item);
+ this.select(index, false);
+ }
+ },
+
+ // private
+ onViewClick : function(view, doFocus, el, e)
+ {
+ var index = this.view.getSelectedIndexes()[0];
+
+ var r = this.store.getAt(index);
+
+ if(this.tickable){
+
+ if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
+ return;
+ }
+
+ var rm = false;
+ var _this = this;
+
+ Roo.each(this.tickItems, function(v,k){
+
+ if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
+ Roo.log(v);
+ _this.tickItems.splice(k, 1);
+
+ if(typeof(e) == 'undefined' && view == false){
+ Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
+ }
+
+ rm = true;
+ return;
+ }
+ });
+
+ if(rm){
+ return;
+ }
+
+ if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
+ this.tickItems.push(r.data);
+ }
+
+ if(typeof(e) == 'undefined' && view == false){
+ Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
+ }
+
+ return;
+ }
+
+ if(r){
+ this.onSelect(r, index);
+ }
+ if(doFocus !== false && !this.blockFocus){
+ this.inputEl().focus();
+ }
+ },
+
+ // private
+ restrictHeight : function(){
+ //this.innerList.dom.style.height = '';
+ //var inner = this.innerList.dom;
+ //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
+ //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
+ //this.list.beginUpdate();
+ //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
+ this.list.alignTo(this.inputEl(), this.listAlign);
+ this.list.alignTo(this.inputEl(), this.listAlign);
+ //this.list.endUpdate();
+ },
+
+ // private
+ onEmptyResults : function(){
+
+ if(this.tickable && this.editable){
+ this.hasFocus = false;
+ this.restrictHeight();
+ return;
+ }
+
+ this.collapse();
+ },
+
+ /**
+ * Returns true if the dropdown list is expanded, else false.
+ */
+ isExpanded : function(){
+ return this.list.isVisible();
+ },
+
+ /**
+ * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
+ * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+ * @param {String} value The data value of the item to select
+ * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+ * selected item if it is not currently in view (defaults to true)
+ * @return {Boolean} True if the value matched an item in the list, else false
+ */
+ selectByValue : function(v, scrollIntoView){
+ if(v !== undefined && v !== null){
+ var r = this.findRecord(this.valueField || this.displayField, v);
+ if(r){
+ this.select(this.store.indexOf(r), scrollIntoView);
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
+ * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+ * @param {Number} index The zero-based index of the list item to select
+ * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+ * selected item if it is not currently in view (defaults to true)
+ */
+ select : function(index, scrollIntoView){
+ this.selectedIndex = index;
+ this.view.select(index);
+ if(scrollIntoView !== false){
+ var el = this.view.getNode(index);
+ /*
+ * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
+ */
+ if(el){
+ this.list.scrollChildIntoView(el, false);
+ }
+ }
+ },
+
+ // private
+ selectNext : function(){
+ var ct = this.store.getCount();
+ if(ct > 0){
+ if(this.selectedIndex == -1){
+ this.select(0);
+ }else if(this.selectedIndex < ct-1){
+ this.select(this.selectedIndex+1);
+ }
+ }
+ },
+
+ // private
+ selectPrev : function(){
+ var ct = this.store.getCount();
+ if(ct > 0){
+ if(this.selectedIndex == -1){
+ this.select(0);
+ }else if(this.selectedIndex != 0){
+ this.select(this.selectedIndex-1);
+ }
+ }
+ },
+
+ // private
+ onKeyUp : function(e){
+ if(this.editable !== false && !e.isSpecialKey()){
+ this.lastKey = e.getKey();
+ this.dqTask.delay(this.queryDelay);
+ }
+ },
+
+ // private
+ validateBlur : function(){
+ return !this.list || !this.list.isVisible();
+ },
+
+ // private
+ initQuery : function(){
+
+ var v = this.getRawValue();
+
+ if(this.tickable && this.editable){
+ v = this.tickableInputEl().getValue();
+ }
+
+ this.doQuery(v);
+ },
+
+ // private
+ doForce : function(){
+ if(this.inputEl().dom.value.length > 0){
+ this.inputEl().dom.value =
+ this.lastSelectionText === undefined ? '' : this.lastSelectionText;
+
+ }
+ },
+
+ /**
+ * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
+ * query allowing the query action to be canceled if needed.
+ * @param {String} query The SQL query to execute
+ * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
+ * in the field than the minimum specified by the minChars config option. It also clears any filter previously
+ * saved in the current store (defaults to false)
+ */
+ doQuery : function(q, forceAll){
+
+ if(q === undefined || q === null){
+ q = '';
+ }
+ var qe = {
+ query: q,
+ forceAll: forceAll,
+ combo: this,
+ cancel:false
+ };
+ if(this.fireEvent('beforequery', qe)===false || qe.cancel){
+ return false;
+ }
+ q = qe.query;
+
+ forceAll = qe.forceAll;
+ if(forceAll === true || (q.length >= this.minChars)){
+
+ this.hasQuery = true;
+
+ if(this.lastQuery != q || this.alwaysQuery){
+ this.lastQuery = q;
+ if(this.mode == 'local'){
+ this.selectedIndex = -1;
+ if(forceAll){
+ this.store.clearFilter();
+ }else{
+
+ if(this.specialFilter){
+ this.fireEvent('specialfilter', this);
+ this.onLoad();
+ return;
+ }
+
+ this.store.filter(this.displayField, q);
+ }
+
+ this.store.fireEvent("datachanged", this.store);
+
+ this.onLoad();
+
+
+ }else{
+
+ this.store.baseParams[this.queryParam] = q;
+
+ var options = {params : this.getParams(q)};
+
+ if(this.loadNext){
+ options.add = true;
+ options.params.start = this.page * this.pageSize;
+ }
+
+ this.store.load(options);
+
+ /*
+ * this code will make the page width larger, at the beginning, the list not align correctly,
+ * we should expand the list on onLoad
+ * so command out it
+ */
+// this.expand();
+ }
+ }else{
+ this.selectedIndex = -1;
+ this.onLoad();
+ }
+ }
+
+ this.loadNext = false;
+ },
+
+ // private
+ getParams : function(q){
+ var p = {};
+ //p[this.queryParam] = q;
+
+ if(this.pageSize){
+ p.start = 0;
+ p.limit = this.pageSize;
+ }
+ return p;
+ },
+
+ /**
+ * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
+ */
+ collapse : function(){
+ if(!this.isExpanded()){
+ return;
+ }
+
+ this.list.hide();
+
+ this.hasFocus = false;
+
+ if(this.tickable){
+ this.okBtn.hide();
+ this.cancelBtn.hide();
+ this.trigger.show();
+
+ if(this.editable){
+ this.tickableInputEl().dom.value = '';
+ this.tickableInputEl().blur();
+ }
+
+ }
+
+ Roo.get(document).un('mousedown', this.collapseIf, this);
+ Roo.get(document).un('mousewheel', this.collapseIf, this);
+ if (!this.editable) {
+ Roo.get(document).un('keydown', this.listKeyPress, this);
+ }
+ this.fireEvent('collapse', this);
+
+ this.validate();
+ },
+
+ // private
+ collapseIf : function(e){
+ var in_combo = e.within(this.el);
+ var in_list = e.within(this.list);
+ var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
+
+ if (in_combo || in_list || is_list) {
+ //e.stopPropagation();
+ return;
+ }
+
+ if(this.tickable){
+ this.onTickableFooterButtonClick(e, false, false);
+ }
+
+ this.collapse();
+
+ },
+
+ /**
+ * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
+ */
+ expand : function(){
+
+ if(this.isExpanded() || !this.hasFocus){
+ return;
+ }
+
+ var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
+ this.list.setWidth(lw);
+
+ Roo.log('expand');
+
+ this.list.show();
+
+ this.restrictHeight();
+
+ if(this.tickable){
+
+ this.tickItems = Roo.apply([], this.item);
+
+ this.okBtn.show();
+ this.cancelBtn.show();
+ this.trigger.hide();
+
+ if(this.editable){
+ this.tickableInputEl().focus();
+ }
+
+ }
+
+ Roo.get(document).on('mousedown', this.collapseIf, this);
+ Roo.get(document).on('mousewheel', this.collapseIf, this);
+ if (!this.editable) {
+ Roo.get(document).on('keydown', this.listKeyPress, this);
+ }
+
+ this.fireEvent('expand', this);
+ },
+
+ // private
+ // Implements the default empty TriggerField.onTriggerClick function
+ onTriggerClick : function(e)
+ {
+ Roo.log('trigger click');
+
+ if(this.disabled || !this.triggerList){
+ return;
+ }
+
+ this.page = 0;
+ this.loadNext = false;
+
+ if(this.isExpanded()){
+ this.collapse();
+ if (!this.blockFocus) {
+ this.inputEl().focus();
+ }
+
+ }else {
+ this.hasFocus = true;
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
+ }
+ if (!this.blockFocus) {
+ this.inputEl().focus();
+ }
+ }
+ },
+
+ onTickableTriggerClick : function(e)
+ {
+ if(this.disabled){
+ return;
+ }
+
+ this.page = 0;
+ this.loadNext = false;
+ this.hasFocus = true;
+
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
+ }
+ },
+
+ onSearchFieldClick : function(e)
+ {
+ if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
+ this.onTickableFooterButtonClick(e, false, false);
+ return;
+ }
+
+ if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
+ return;
+ }
+
+ this.page = 0;
+ this.loadNext = false;
+ this.hasFocus = true;
+
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
+ }
+ },
+
+ listKeyPress : function(e)
+ {
+ //Roo.log('listkeypress');
+ // scroll to first matching element based on key pres..
+ if (e.isSpecialKey()) {
+ return false;
+ }
+ var k = String.fromCharCode(e.getKey()).toUpperCase();
+ //Roo.log(k);
+ var match = false;
+ var csel = this.view.getSelectedNodes();
+ var cselitem = false;
+ if (csel.length) {
+ var ix = this.view.indexOf(csel[0]);
+ cselitem = this.store.getAt(ix);
+ if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
+ cselitem = false;
+ }
+
+ }
+
+ this.store.each(function(v) {
+ if (cselitem) {
+ // start at existing selection.
+ if (cselitem.id == v.id) {
+ cselitem = false;
+ }
+ return true;
+ }
+
+ if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
+ match = this.store.indexOf(v);
+ return false;
+ }
+ return true;
+ }, this);
+
+ if (match === false) {
+ return true; // no more action?
+ }
+ // scroll to?
+ this.view.select(match);
+ var sn = Roo.get(this.view.getSelectedNodes()[0]);
+ sn.scrollIntoView(sn.dom.parentNode, false);
+ },
+
+ onViewScroll : function(e, t){
+
+ if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
+ return;
+ }
+
+ this.hasQuery = true;
+
+ this.loading = this.list.select('.loading', true).first();
+
+ if(this.loading === null){
+ this.list.createChild({
+ tag: 'div',
+ cls: 'loading roo-select2-more-results roo-select2-active',
+ html: 'Loading more results...'
+ });
+
+ this.loading = this.list.select('.loading', true).first();
+
+ this.loading.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.loading.hide();
+ }
+
+ this.loading.show();
+
+ var _combo = this;
+
+ this.page++;
+ this.loadNext = true;
+
+ (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
+
+ return;
+ },
+
+ addItem : function(o)
+ {
+ var dv = ''; // display value
+
+ if (this.displayField) {
+ dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+ } else {
+ // this is an error condition!!!
+ Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
+ }
+
+ if(!dv.length){
+ return;
+ }
+
+ var choice = this.choices.createChild({
+ tag: 'li',
+ cls: 'roo-select2-search-choice',
+ cn: [
+ {
+ tag: 'div',
+ html: dv
+ },
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'roo-select2-search-choice-close fa fa-times',
+ tabindex: '-1'
+ }
+ ]
+
+ }, this.searchField);
+
+ var close = choice.select('a.roo-select2-search-choice-close', true).first();
+
+ close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
+
+ this.item.push(o);
+
+ this.lastData = o;
+
+ this.syncValue();
+
+ this.inputEl().dom.value = '';
+
+ this.validate();
+ },
+
+ onRemoveItem : function(e, _self, o)
+ {
+ e.preventDefault();
+
+ this.lastItem = Roo.apply([], this.item);
+
+ var index = this.item.indexOf(o.data) * 1;
+
+ if( index < 0){
+ Roo.log('not this item?!');
+ return;
+ }
+
+ this.item.splice(index, 1);
+ o.item.remove();
+
+ this.syncValue();
+
+ this.fireEvent('remove', this, e);
+
+ this.validate();
+
+ },
+
+ syncValue : function()
+ {
+ if(!this.item.length){
+ this.clearValue();
+ return;
+ }
+
+ var value = [];
+ var _this = this;
+ Roo.each(this.item, function(i){
+ if(_this.valueField){
+ value.push(i[_this.valueField]);
+ return;
+ }
+
+ value.push(i);
+ });
+
+ this.value = value.join(',');
+
+ if(this.hiddenField){
+ this.hiddenField.dom.value = this.value;
+ }
+
+ this.store.fireEvent("datachanged", this.store);
+
+ this.validate();
+ },
+
+ clearItem : function()
+ {
+ if(!this.multiple){
+ return;
+ }
+
+ this.item = [];
+
+ Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
+ c.remove();
+ });
+
+ this.syncValue();
+
+ this.validate();
+
+ if(this.tickable && !Roo.isTouch){
+ this.view.refresh();
+ }
+ },
+
+ inputEl: function ()
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ return this.el.select('select.roo-ios-select', true).first();
+ }
+
+ if(Roo.isTouch && this.mobileTouchView){
+ return this.el.select('input.form-control',true).first();
+ }
+
+ if(this.tickable){
+ return this.searchField;
+ }
+
+ return this.el.select('input.form-control',true).first();
+ },
+
+ onTickableFooterButtonClick : function(e, btn, el)
+ {
+ e.preventDefault();
+
+ this.lastItem = Roo.apply([], this.item);
+
+ if(btn && btn.name == 'cancel'){
+ this.tickItems = Roo.apply([], this.item);
+ this.collapse();
+ return;
+ }
+
+ this.clearItem();
+
+ var _this = this;
+
+ Roo.each(this.tickItems, function(o){
+ _this.addItem(o);
+ });
+
+ this.collapse();
+
+ },
+
+ validate : function()
+ {
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
+ }
+
+ var v = this.getRawValue();
+
+ if(this.multiple){
+ v = this.getValue();
+ }
+
+ if(this.disabled || this.allowBlank || v.length){
+ this.markValid();
+ return true;
+ }
+
+ this.markInvalid();
+ return false;
+ },
+
+ tickableInputEl : function()
+ {
+ if(!this.tickable || !this.editable){
+ return this.inputEl();
+ }
+
+ return this.inputEl().select('.roo-select2-search-field-input', true).first();
+ },
+
+
+ getAutoCreateTouchView : function()
+ {
+ var id = Roo.id();
+
+ var cfg = {
+ cls: 'form-group' //input-group
+ };
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ cls : 'form-control x-combo-noedit',
+ autocomplete: 'new-password',
+ placeholder : this.placeholder || '',
+ readonly : true
+ };
+
+ if (this.name) {
+ input.name = this.name;
+ }
+
+ if (this.size) {
+ input.cls += ' input-' + this.size;
+ }
+
+ if (this.disabled) {
+ input.disabled = true;
+ }
+
+ var inputblock = {
+ cls : 'roo-combobox-wrap',
+ cn : [
+ input
+ ]
+ };
+
+ if(this.before){
+ inputblock.cls += ' input-group';
+
+ inputblock.cn.unshift({
+ tag :'span',
+ cls : 'input-group-addon input-group-prepend input-group-text',
+ html : this.before
+ });
+ }
+
+ if(this.removable && !this.multiple){
+ inputblock.cls += ' roo-removable';
+
+ inputblock.cn.push({
+ tag: 'button',
+ html : 'x',
+ cls : 'roo-combo-removable-btn close'
+ });
+ }
+
+ if(this.hasFeedback && !this.allowBlank){
+
+ inputblock.cls += ' has-feedback';
+
+ inputblock.cn.push({
+ tag: 'span',
+ cls: 'glyphicon form-control-feedback'
+ });
+
+ }
+
+ if (this.after) {
+
+ inputblock.cls += (this.before) ? '' : ' input-group';
+
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon input-group-append input-group-text',
+ html : this.after
+ });
+ }
+
+
+ var ibwrap = inputblock;
+
+ if(this.multiple){
+ ibwrap = {
+ tag: 'ul',
+ cls: 'roo-select2-choices',
+ cn:[
+ {
+ tag: 'li',
+ cls: 'roo-select2-search-field',
+ cn: [
+
+ inputblock
+ ]
+ }
+ ]
+ };
+
+
+ }
+
+ var combobox = {
+ cls: 'roo-select2-container input-group roo-touchview-combobox ',
+ cn: [
+ {
+ tag: 'input',
+ type : 'hidden',
+ cls: 'form-hidden-field'
+ },
+ ibwrap
+ ]
+ };
+
+ if(!this.multiple && this.showToggleBtn){
+
+ var caret = {
+ cls: 'caret'
+ };
+
+ if (this.caret != false) {
+ caret = {
+ tag: 'i',
+ cls: 'fa fa-' + this.caret
+ };
+
+ }
+
+ combobox.cn.push({
+ tag :'span',
+ cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
+ cn : [
+ Roo.bootstrap.version == 3 ? caret : '',
+ {
+ tag: 'span',
+ cls: 'combobox-clear',
+ cn : [
+ {
+ tag : 'i',
+ cls: 'icon-remove'
+ }
+ ]
+ }
+ ]
+
+ })
+ }
+
+ if(this.multiple){
+ combobox.cls += ' roo-select2-container-multi';
+ }
+
+ var required = this.allowBlank ? {
+ tag : 'i',
+ style: 'display: none'
+ } : {
+ tag : 'i',
+ cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required'
+ };
+
+ var align = this.labelAlign || this.parentLabelAlign();
+
+ if (align ==='left' && this.fieldLabel.length) {
+
+ cfg.cn = [
+ required,
+ {
+ tag: 'label',
+ cls : 'control-label col-form-label',
+ html : this.fieldLabel
+
+ },
+ {
+ cls : 'roo-combobox-wrap ',
+ cn: [
+ combobox
+ ]
+ }
+ ];
+
+ var labelCfg = cfg.cn[1];
+ var contentCfg = cfg.cn[2];
+
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-form-label',
+ cn : [
+ {
+ tag : 'span',
+ html : this.fieldLabel
+ },
+ required
+ ]
+ },
+ {
+ cls : "roo-combobox-wrap ",
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+
+ labelCfg = cfg.cn[0];
+ contentCfg = cfg.cn[1];
+ }
+
+
+
+ if(this.labelWidth > 12){
+ labelCfg.style = "width: " + this.labelWidth + 'px';
+ }
+
+ if(this.labelWidth < 13 && this.labelmd == 0){
+ this.labelmd = this.labelWidth;
+ }
+
+ if(this.labellg > 0){
+ labelCfg.cls += ' col-lg-' + this.labellg;
+ contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+ }
+
+ if(this.labelmd > 0){
+ labelCfg.cls += ' col-md-' + this.labelmd;
+ contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ }
+
+ if(this.labelsm > 0){
+ labelCfg.cls += ' col-sm-' + this.labelsm;
+ contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ }
+
+ if(this.labelxs > 0){
+ labelCfg.cls += ' col-xs-' + this.labelxs;
+ contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ }
+
+
+ } else if ( this.fieldLabel.length) {
+ cfg.cn = [
+ required,
+ {
+ tag: 'label',
+ cls : 'control-label',
+ html : this.fieldLabel
+
+ },
+ {
+ cls : '',
+ cn: [
+ combobox
+ ]
+ }
+ ];
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ cls : 'control-label',
+ html : this.fieldLabel,
+ cn : [
+ required
+ ]
+ },
+ {
+ cls : '',
+ cn: [
+ combobox
+ ]
+ }
+ ];
+ }
+ } else {
+ cfg.cn = combobox;
+ }
+
+
+ var settings = this;
+
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ return cfg;
+ },
+
+ initTouchView : function()
+ {
+ this.renderTouchView();
+
+ this.touchViewEl.on('scroll', function(){
+ this.el.dom.scrollTop = 0;
+ }, this);
+
+ this.originalValue = this.getValue();
+
+ this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
+
+ this.inputEl().on("click", this.showTouchView, this);
+ if (this.triggerEl) {
+ this.triggerEl.on("click", this.showTouchView, this);
+ }
+
+
+ this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
+ this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
+
+ this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
+
+ this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
+ this.store.on('load', this.onTouchViewLoad, this);
+ this.store.on('loadexception', this.onTouchViewLoadException, this);
+
+ if(this.hiddenName){
+
+ this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+
+ this.hiddenField.dom.value =
+ this.hiddenValue !== undefined ? this.hiddenValue :
+ this.value !== undefined ? this.value : '';
+
+ this.el.dom.removeAttribute('name');
+ this.hiddenField.dom.setAttribute('name', this.hiddenName);
+ }
+
+ if(this.multiple){
+ this.choices = this.el.select('ul.roo-select2-choices', true).first();
+ this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+ }
+
+ if(this.removable && !this.multiple){
+ var close = this.closeTriggerEl();
+ if(close){
+ close.setVisibilityMode(Roo.Element.DISPLAY).hide();
+ close.on('click', this.removeBtnClick, this, close);
+ }
+ }
+ /*
+ * fix the bug in Safari iOS8
+ */
+ this.inputEl().on("focus", function(e){
+ document.activeElement.blur();
+ }, this);
+
+ this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
+
+ return;
+
+
+ },
+
+ renderTouchView : function()
+ {
+ this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
+ this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
+ this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
+ this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+ this.touchViewBodyEl.setStyle('overflow', 'auto');
+
+ this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
+ this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
+ this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ },
+
+ showTouchView : function()
+ {
+ if(this.disabled){
+ return;
+ }
+
+ this.touchViewHeaderEl.hide();
+
+ if(this.modalTitle.length){
+ this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
+ this.touchViewHeaderEl.show();
+ }
+
+ this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+ this.touchViewEl.show();
+
+ this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
+
+ //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
+ // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
+
+ var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
+
+ if(this.modalTitle.length){
+ bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+ }
+
+ this.touchViewBodyEl.setHeight(bodyHeight);
+
+ if(this.animate){
+ var _this = this;
+ (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
+ }else{
+ this.touchViewEl.addClass(['in','show']);
+ }
+
+ if(this._touchViewMask){
+ Roo.get(document.body).addClass("x-body-masked");
+ this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
+ this._touchViewMask.setStyle('z-index', 10000);
+ this._touchViewMask.addClass('show');
+ }
+
+ this.doTouchViewQuery();
+
+ },
+
+ hideTouchView : function()
+ {
+ this.touchViewEl.removeClass(['in','show']);
+
+ if(this.animate){
+ var _this = this;
+ (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
+ }else{
+ this.touchViewEl.setStyle('display', 'none');
+ }
+
+ if(this._touchViewMask){
+ this._touchViewMask.removeClass('show');
+ Roo.get(document.body).removeClass("x-body-masked");
+ }
+ },
+
+ setTouchViewValue : function()
+ {
+ if(this.multiple){
+ this.clearItem();
+
+ var _this = this;
+
+ Roo.each(this.tickItems, function(o){
+ this.addItem(o);
+ }, this);
+ }
+
+ this.hideTouchView();
+ },
+
+ doTouchViewQuery : function()
+ {
+ var qe = {
+ query: '',
+ forceAll: true,
+ combo: this,
+ cancel:false
+ };
+
+ if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
+ return false;
+ }
+
+ if(!this.alwaysQuery || this.mode == 'local'){
+ this.onTouchViewLoad();
+ return;
+ }
+
+ this.store.load();
+ },
+
+ onTouchViewBeforeLoad : function(combo,opts)
+ {
+ return;
+ },
+
+ // private
+ onTouchViewLoad : function()
+ {
+ if(this.store.getCount() < 1){
+ this.onTouchViewEmptyResults();
+ return;
+ }
+
+ this.clearTouchView();
+
+ var rawValue = this.getRawValue();
+
+ var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
+
+ this.tickItems = [];
+
+ this.store.data.each(function(d, rowIndex){
+ var row = this.touchViewListGroup.createChild(template);
+
+ if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
+ row.addClass(d.data.cls);
+ }
+
+ if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
+ var cfg = {
+ data : d.data,
+ html : d.data[this.displayField]
+ };
+
+ if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
+ row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
+ }
+ }
+ row.removeClass('selected');
+ if(!this.multiple && this.valueField &&
+ typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
+ {
+ // radio buttons..
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+ row.addClass('selected');
+ }
+
+ if(this.multiple && this.valueField &&
+ typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
+ {
+
+ // checkboxes...
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+ this.tickItems.push(d.data);
+ }
+
+ row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
+
+ }, this);
+
+ var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
+
+ var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
+
+ if(this.modalTitle.length){
+ bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+ }
+
+ var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
+
+ if(this.mobile_restrict_height && listHeight < bodyHeight){
+ this.touchViewBodyEl.setHeight(listHeight);
+ }
+
+ var _this = this;
+
+ if(firstChecked && listHeight > bodyHeight){
+ (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
+ }
+
+ },
+
+ onTouchViewLoadException : function()
+ {
+ this.hideTouchView();
+ },
+
+ onTouchViewEmptyResults : function()
+ {
+ this.clearTouchView();
+
+ this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
+
+ this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
+
+ },
+
+ clearTouchView : function()
+ {
+ this.touchViewListGroup.dom.innerHTML = '';
+ },
+
+ onTouchViewClick : function(e, el, o)
+ {
+ e.preventDefault();
+
+ var row = o.row;
+ var rowIndex = o.rowIndex;
+
+ var r = this.store.getAt(rowIndex);
+
+ if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
+
+ if(!this.multiple){
+ Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
+ c.dom.removeAttribute('checked');
+ }, this);
+
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+
+ this.setFromData(r.data);
+
+ var close = this.closeTriggerEl();
+
+ if(close){
+ close.show();
+ }
+
+ this.hideTouchView();
+
+ this.fireEvent('select', this, r, rowIndex);
+
+ return;
+ }
+
+ if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
+ row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
+ this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
+ return;
+ }
+
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+ this.addItem(r.data);
+ this.tickItems.push(r.data);
+ }
+ },
+
+ getAutoCreateNativeIOS : function()
+ {
+ var cfg = {
+ cls: 'form-group' //input-group,
+ };
+
+ var combobox = {
+ tag: 'select',
+ cls : 'roo-ios-select'
+ };
+
+ if (this.name) {
+ combobox.name = this.name;
+ }
+
+ if (this.disabled) {
+ combobox.disabled = true;
+ }
+
+ var settings = this;
+
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ cfg.cn = combobox;
+
+ return cfg;
+
+ },
+
+ initIOSView : function()
+ {
+ this.store.on('load', this.onIOSViewLoad, this);
+
+ return;
+ },
+
+ onIOSViewLoad : function()
+ {
+ if(this.store.getCount() < 1){
+ return;
+ }
+
+ this.clearIOSView();
+
+ if(this.allowBlank) {
+
+ var default_text = '-- SELECT --';
+
+ if(this.placeholder.length){
+ default_text = this.placeholder;
+ }
+
+ if(this.emptyTitle.length){
+ default_text += ' - ' + this.emptyTitle + ' -';
+ }
+
+ var opt = this.inputEl().createChild({
+ tag: 'option',
+ value : 0,
+ html : default_text
+ });
+
+ var o = {};
+ o[this.valueField] = 0;
+ o[this.displayField] = default_text;
+
+ this.ios_options.push({
+ data : o,
+ el : opt
+ });
+
+ }
+
+ this.store.data.each(function(d, rowIndex){
+
+ var html = '';
+
+ if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
+ html = d.data[this.displayField];
+ }
+
+ var value = '';
+
+ if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
+ value = d.data[this.valueField];
+ }
+
+ var option = {
+ tag: 'option',
+ value : value,
+ html : html
+ };
+
+ if(this.value == d.data[this.valueField]){
+ option['selected'] = true;
+ }
+
+ var opt = this.inputEl().createChild(option);
+
+ this.ios_options.push({
+ data : d.data,
+ el : opt
+ });
+
+ }, this);
+
+ this.inputEl().on('change', function(){
+ this.fireEvent('select', this);
+ }, this);
+
+ },
+
+ clearIOSView: function()
+ {
+ this.inputEl().dom.innerHTML = '';
+
+ this.ios_options = [];
+ },
+
+ setIOSValue: function(v)
+ {
+ this.value = v;
+
+ if(!this.ios_options){
+ return;
+ }
+
+ Roo.each(this.ios_options, function(opts){
+
+ opts.el.dom.removeAttribute('selected');
+
+ if(opts.data[this.valueField] != v){
+ return;
+ }
+
+ opts.el.dom.setAttribute('selected', true);
+
+ }, this);
+ }
+
+ /**
+ * @cfg {Boolean} grow
+ * @hide
+ */
+ /**
+ * @cfg {Number} growMin
+ * @hide
+ */
+ /**
+ * @cfg {Number} growMax
+ * @hide
+ */
+ /**
+ * @hide
+ * @method autoSize
+ */
+});
+
+Roo.apply(Roo.bootstrap.form.ComboBox, {
+
+ header : {
+ tag: 'div',
+ cls: 'modal-header',
+ cn: [
+ {
+ tag: 'h4',
+ cls: 'modal-title'
+ }
+ ]
+ },
+
+ body : {
+ tag: 'div',
+ cls: 'modal-body',
+ cn: [
+ {
+ tag: 'ul',
+ cls: 'list-group'
+ }
+ ]
+ },
+
+ listItemRadio : {
+ tag: 'li',
+ cls: 'list-group-item',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'roo-combobox-list-group-item-value'
+ },
+ {
+ tag: 'div',
+ cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
+ cn: [
+ {
+ tag: 'input',
+ type: 'radio'
+ },
+ {
+ tag: 'label'
+ }
+ ]
+ }
+ ]
+ },
+
+ listItemCheckbox : {
+ tag: 'li',
+ cls: 'list-group-item',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'roo-combobox-list-group-item-value'
+ },
+ {
+ tag: 'div',
+ cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
+ cn: [
+ {
+ tag: 'input',
+ type: 'checkbox'
+ },
+ {
+ tag: 'label'
+ }
+ ]
+ }
+ ]
+ },
+
+ emptyResult : {
+ tag: 'div',
+ cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
+ },
+
+ footer : {
+ tag: 'div',
+ cls: 'modal-footer',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'row',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'col-xs-6 text-left',
+ cn: {
+ tag: 'button',
+ cls: 'btn btn-danger roo-touch-view-cancel',
+ html: 'Cancel'
+ }
+ },
+ {
+ tag: 'div',
+ cls: 'col-xs-6 text-right',
+ cn: {
+ tag: 'button',
+ cls: 'btn btn-success roo-touch-view-ok',
+ html: 'OK'
+ }
+ }
+ ]
+ }
+ ]
+
+ }
+});
+
+Roo.apply(Roo.bootstrap.form.ComboBox, {
+
+ touchViewTemplate : {
+ tag: 'div',
+ cls: 'modal fade roo-combobox-touch-view',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'modal-dialog',
+ style : 'position:fixed', // we have to fix position....
+ cn: [
+ {
+ tag: 'div',
+ cls: 'modal-content',
+ cn: [
+ Roo.bootstrap.form.ComboBox.header,
+ Roo.bootstrap.form.ComboBox.body,
+ Roo.bootstrap.form.ComboBox.footer
+ ]
+ }
+ ]
+ }
+ ]
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.View
+ * @extends Roo.util.Observable
+ * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
+ * This class also supports single and multi selection modes. <br>
+ * Create a data model bound view:
+ <pre><code>
+ var store = new Roo.data.Store(...);
+
+ var view = new Roo.View({
+ el : "my-element",
+ tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
+
+ singleSelect: true,
+ selectedClass: "ydataview-selected",
+ store: store
+ });
+
+ // listen for node click?
+ view.on("click", function(vw, index, node, e){
+ alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
+ });
+
+ // load XML data
+ dataModel.load("foobar.xml");
+ </code></pre>
+ For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
+ * <br><br>
+ * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
+ * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
+ *
+ * Note: old style constructor is still suported (container, template, config)
+ *
+ * @constructor
+ * Create a new View
+ * @param {Object} config The config object
+ *
+ */
+Roo.View = function(config, depreciated_tpl, depreciated_config){
+
+ this.parent = false;
+
+ if (typeof(depreciated_tpl) == 'undefined') {
+ // new way.. - universal constructor.
+ Roo.apply(this, config);
+ this.el = Roo.get(this.el);
+ } else {
+ // old format..
+ this.el = Roo.get(config);
+ this.tpl = depreciated_tpl;
+ Roo.apply(this, depreciated_config);
+ }
+ this.wrapEl = this.el.wrap().wrap();
+ ///this.el = this.wrapEla.appendChild(document.createElement("div"));
+
+
+ if(typeof(this.tpl) == "string"){
+ this.tpl = new Roo.Template(this.tpl);
+ } else {
+ // support xtype ctors..
+ this.tpl = new Roo.factory(this.tpl, Roo);
+ }
+
+
+ this.tpl.compile();
+
+ /** @private */
+ this.addEvents({
+ /**
+ * @event beforeclick
+ * Fires before a click is processed. Returns false to cancel the default action.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "beforeclick" : true,
+ /**
+ * @event click
+ * Fires when a template node is clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "click" : true,
+ /**
+ * @event dblclick
+ * Fires when a template node is double clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "dblclick" : true,
+ /**
+ * @event contextmenu
+ * Fires when a template node is right clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "contextmenu" : true,
+ /**
+ * @event selectionchange
+ * Fires when the selected nodes change.
+ * @param {Roo.View} this
+ * @param {Array} selections Array of the selected nodes
+ */
+ "selectionchange" : true,
+
+ /**
+ * @event beforeselect
+ * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+ * @param {Roo.View} this
+ * @param {HTMLElement} node The node to be selected
+ * @param {Array} selections Array of currently selected nodes
+ */
+ "beforeselect" : true,
+ /**
+ * @event preparedata
+ * Fires on every row to render, to allow you to change the data.
+ * @param {Roo.View} this
+ * @param {Object} data to be rendered (change this)
+ */
+ "preparedata" : true
+
+
+ });
+
+
+
+ this.el.on({
+ "click": this.onClick,
+ "dblclick": this.onDblClick,
+ "contextmenu": this.onContextMenu,
+ scope:this
+ });
+
+ this.selections = [];
+ this.nodes = [];
+ this.cmp = new Roo.CompositeElementLite([]);
+ if(this.store){
+ this.store = Roo.factory(this.store, Roo.data);
+ this.setStore(this.store, true);
+ }
+
+ if ( this.footer && this.footer.xtype) {
+
+ var fctr = this.wrapEl.appendChild(document.createElement("div"));
+
+ this.footer.dataSource = this.store;
+ this.footer.container = fctr;
+ this.footer = Roo.factory(this.footer, Roo);
+ fctr.insertFirst(this.el);
+
+ // this is a bit insane - as the paging toolbar seems to detach the el..
+// dom.parentNode.parentNode.parentNode
+ // they get detached?
+ }
+
+
+ Roo.View.superclass.constructor.call(this);
+
+
+};
+
+Roo.extend(Roo.View, Roo.util.Observable, {
+
+ /**
+ * @cfg {Roo.data.Store} store Data store to load data from.
+ */
+ store : false,
+
+ /**
+ * @cfg {String|Roo.Element} el The container element.
+ */
+ el : '',
+
+ /**
+ * @cfg {String|Roo.Template} tpl The template used by this View
+ */
+ tpl : false,
+ /**
+ * @cfg {String} dataName the named area of the template to use as the data area
+ * Works with domtemplates roo-name="name"
+ */
+ dataName: false,
+ /**
+ * @cfg {String} selectedClass The css class to add to selected nodes
+ */
+ selectedClass : "x-view-selected",
+ /**
+ * @cfg {String} emptyText The empty text to show when nothing is loaded.
+ */
+ emptyText : "",
+
+ /**
+ * @cfg {String} text to display on mask (default Loading)
+ */
+ mask : false,
+ /**
+ * @cfg {Boolean} multiSelect Allow multiple selection
+ */
+ multiSelect : false,
+ /**
+ * @cfg {Boolean} singleSelect Allow single selection
+ */
+ singleSelect: false,
+
+ /**
+ * @cfg {Boolean} toggleSelect - selecting
+ */
+ toggleSelect : false,
+
+ /**
+ * @cfg {Boolean} tickable - selecting
+ */
+ tickable : false,
+
+ /**
+ * Returns the element this view is bound to.
+ * @return {Roo.Element}
+ */
+ getEl : function(){
+ return this.wrapEl;