4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
14 * @class Roo.data.SortTypes
16 * Defines the default sorting (casting?) comparison functions used when sorting data.
18 Roo.data.SortTypes = {
20 * Default sort that does nothing
21 * @param {Mixed} s The value being converted
22 * @return {Mixed} The comparison value
29 * The regular expression used to strip tags
33 stripTagsRE : /<\/?[^>]+>/gi,
36 * Strips all HTML tags to sort on text only
37 * @param {Mixed} s The value being converted
38 * @return {String} The comparison value
41 return String(s).replace(this.stripTagsRE, "");
45 * Strips all HTML tags to sort on text only - Case insensitive
46 * @param {Mixed} s The value being converted
47 * @return {String} The comparison value
49 asUCText : function(s){
50 return String(s).toUpperCase().replace(this.stripTagsRE, "");
54 * Case insensitive string
55 * @param {Mixed} s The value being converted
56 * @return {String} The comparison value
58 asUCString : function(s) {
59 return String(s).toUpperCase();
64 * @param {Mixed} s The value being converted
65 * @return {Number} The comparison value
67 asDate : function(s) {
71 if(s instanceof Date){
74 return Date.parse(String(s));
79 * @param {Mixed} s The value being converted
80 * @return {Float} The comparison value
82 asFloat : function(s) {
83 var val = parseFloat(String(s).replace(/,/g, ""));
92 * @param {Mixed} s The value being converted
93 * @return {Number} The comparison value
96 var val = parseInt(String(s).replace(/,/g, ""));
104 * Ext JS Library 1.1.1
105 * Copyright(c) 2006-2007, Ext JS, LLC.
107 * Originally Released Under LGPL - original licence link has changed is not relivant.
110 * <script type="text/javascript">
114 * @class Roo.data.Record
115 * Instances of this class encapsulate both record <em>definition</em> information, and record
116 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117 * to access Records cached in an {@link Roo.data.Store} object.<br>
119 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
123 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
125 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126 * {@link #create}. The parameters are the same.
127 * @param {Array} data An associative Array of data values keyed by the field name.
128 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130 * not specified an integer id is generated.
132 Roo.data.Record = function(data, id){
133 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
138 * Generate a constructor for a specific record layout.
139 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141 * Each field definition object may contain the following properties: <ul>
142 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146 * is being used, then this is a string containing the javascript expression to reference the data relative to
147 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148 * to the data item relative to the record element. If the mapping expression is the same as the field name,
149 * this may be omitted.</p></li>
150 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151 * <ul><li>auto (Default, implies no conversion)</li>
156 * <li>date</li></ul></p></li>
157 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160 * by the Reader into an object that will be stored in the Record. It is passed the
161 * following parameters:<ul>
162 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
164 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
166 * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168 {name: 'title', mapping: 'topic_title'},
169 {name: 'author', mapping: 'username'},
170 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171 {name: 'lastPost', mapping: 'post_time', type: 'date'},
172 {name: 'lastPoster', mapping: 'user2'},
173 {name: 'excerpt', mapping: 'post_text'}
176 var myNewRecord = new TopicRecord({
177 title: 'Do my job please',
180 lastPost: new Date(),
181 lastPoster: 'Animal',
182 excerpt: 'No way dude!'
184 myStore.add(myNewRecord);
189 Roo.data.Record.create = function(o){
191 f.superclass.constructor.apply(this, arguments);
193 Roo.extend(f, Roo.data.Record);
195 p.fields = new Roo.util.MixedCollection(false, function(field){
198 for(var i = 0, len = o.length; i < len; i++){
199 p.fields.add(new Roo.data.Field(o[i]));
201 f.getField = function(name){
202 return p.fields.get(name);
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
212 Roo.data.Record.prototype = {
214 * Readonly flag - true if this record has been modified.
223 join : function(store){
228 * Set the named field to the specified value.
229 * @param {String} name The name of the field to set.
230 * @param {Object} value The value to set the field to.
232 set : function(name, value){
233 if(this.data[name] == value){
240 if(typeof this.modified[name] == 'undefined'){
241 this.modified[name] = this.data[name];
243 this.data[name] = value;
244 if(!this.editing && this.store){
245 this.store.afterEdit(this);
250 * Get the value of the named field.
251 * @param {String} name The name of the field to get the value of.
252 * @return {Object} The value of the field.
254 get : function(name){
255 return this.data[name];
259 beginEdit : function(){
265 cancelEdit : function(){
266 this.editing = false;
267 delete this.modified;
271 endEdit : function(){
272 this.editing = false;
273 if(this.dirty && this.store){
274 this.store.afterEdit(this);
279 * Usually called by the {@link Roo.data.Store} which owns the Record.
280 * Rejects all changes made to the Record since either creation, or the last commit operation.
281 * Modified fields are reverted to their original values.
283 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284 * of reject operations.
287 var m = this.modified;
289 if(typeof m[n] != "function"){
294 delete this.modified;
295 this.editing = false;
297 this.store.afterReject(this);
302 * Usually called by the {@link Roo.data.Store} which owns the Record.
303 * Commits all changes made to the Record since either creation, or the last commit operation.
305 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306 * of commit operations.
310 delete this.modified;
311 this.editing = false;
313 this.store.afterCommit(this);
318 hasError : function(){
319 return this.error != null;
323 clearError : function(){
328 * Creates a copy of this record.
329 * @param {String} id (optional) A new record id if you don't want to use this record's id
332 copy : function(newId) {
333 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
337 * Ext JS Library 1.1.1
338 * Copyright(c) 2006-2007, Ext JS, LLC.
340 * Originally Released Under LGPL - original licence link has changed is not relivant.
343 * <script type="text/javascript">
349 * @class Roo.data.Store
350 * @extends Roo.util.Observable
351 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
354 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355 * has no knowledge of the format of the data returned by the Proxy.<br>
357 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358 * instances from the data object. These records are cached and made available through accessor functions.
360 * Creates a new Store.
361 * @param {Object} config A config object containing the objects needed for the Store to access data,
362 * and read the data into Records.
364 Roo.data.Store = function(config){
365 this.data = new Roo.util.MixedCollection(false);
366 this.data.getKey = function(o){
369 this.baseParams = {};
376 "multisort" : "_multisort"
379 if(config && config.data){
380 this.inlineData = config.data;
384 Roo.apply(this, config);
386 if(this.reader){ // reader passed
387 this.reader = Roo.factory(this.reader, Roo.data);
388 this.reader.xmodule = this.xmodule || false;
389 if(!this.recordType){
390 this.recordType = this.reader.recordType;
392 if(this.reader.onMetaChange){
393 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
398 this.fields = this.recordType.prototype.fields;
405 * Fires when the data cache has changed, and a widget which is using this Store
406 * as a Record cache should refresh its view.
407 * @param {Store} this
412 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413 * @param {Store} this
414 * @param {Object} meta The JSON metadata
419 * Fires when Records have been added to the Store
420 * @param {Store} this
421 * @param {Roo.data.Record[]} records The array of Records added
422 * @param {Number} index The index at which the record(s) were added
427 * Fires when a Record has been removed from the Store
428 * @param {Store} this
429 * @param {Roo.data.Record} record The Record that was removed
430 * @param {Number} index The index at which the record was removed
435 * Fires when a Record has been updated
436 * @param {Store} this
437 * @param {Roo.data.Record} record The Record that was updated
438 * @param {String} operation The update operation being performed. Value may be one of:
441 Roo.data.Record.REJECT
442 Roo.data.Record.COMMIT
448 * Fires when the data cache has been cleared.
449 * @param {Store} this
454 * Fires before a request is made for a new data object. If the beforeload handler returns false
455 * the load action will be canceled.
456 * @param {Store} this
457 * @param {Object} options The loading options that were specified (see {@link #load} for details)
461 * @event beforeloadadd
462 * Fires after a new set of Records has been loaded.
463 * @param {Store} this
464 * @param {Roo.data.Record[]} records The Records that were loaded
465 * @param {Object} options The loading options that were specified (see {@link #load} for details)
467 beforeloadadd : true,
470 * Fires after a new set of Records has been loaded, before they are added to the store.
471 * @param {Store} this
472 * @param {Roo.data.Record[]} records The Records that were loaded
473 * @param {Object} options The loading options that were specified (see {@link #load} for details)
474 * @params {Object} return from reader
478 * @event loadexception
479 * Fires if an exception occurs in the Proxy during loading.
480 * Called with the signature of the Proxy's "loadexception" event.
481 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
484 * @param {Object} return from JsonData.reader() - success, totalRecords, records
485 * @param {Object} load options
486 * @param {Object} jsonData from your request (normally this contains the Exception)
492 this.proxy = Roo.factory(this.proxy, Roo.data);
493 this.proxy.xmodule = this.xmodule || false;
494 this.relayEvents(this.proxy, ["loadexception"]);
496 this.sortToggle = {};
497 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
499 Roo.data.Store.superclass.constructor.call(this);
502 this.loadData(this.inlineData);
503 delete this.inlineData;
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
509 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
510 * without a remote query - used by combo/forms at present.
514 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
517 * @cfg {Array} data Inline data to be loaded when the store is initialized.
520 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
521 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
524 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525 * on any HTTP request
528 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
531 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
535 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
541 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542 * loaded or when a record is removed. (defaults to false).
544 pruneModifiedRecords : false,
550 * Add Records to the Store and fires the add event.
551 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
553 add : function(records){
554 records = [].concat(records);
555 for(var i = 0, len = records.length; i < len; i++){
556 records[i].join(this);
558 var index = this.data.length;
559 this.data.addAll(records);
560 this.fireEvent("add", this, records, index);
564 * Remove a Record from the Store and fires the remove event.
565 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
567 remove : function(record){
568 var index = this.data.indexOf(record);
569 this.data.removeAt(index);
571 if(this.pruneModifiedRecords){
572 this.modified.remove(record);
574 this.fireEvent("remove", this, record, index);
578 * Remove all Records from the Store and fires the clear event.
580 removeAll : function(){
582 if(this.pruneModifiedRecords){
585 this.fireEvent("clear", this);
589 * Inserts Records to the Store at the given index and fires the add event.
590 * @param {Number} index The start index at which to insert the passed Records.
591 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
593 insert : function(index, records){
594 records = [].concat(records);
595 for(var i = 0, len = records.length; i < len; i++){
596 this.data.insert(index, records[i]);
597 records[i].join(this);
599 this.fireEvent("add", this, records, index);
603 * Get the index within the cache of the passed Record.
604 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605 * @return {Number} The index of the passed Record. Returns -1 if not found.
607 indexOf : function(record){
608 return this.data.indexOf(record);
612 * Get the index within the cache of the Record with the passed id.
613 * @param {String} id The id of the Record to find.
614 * @return {Number} The index of the Record. Returns -1 if not found.
616 indexOfId : function(id){
617 return this.data.indexOfKey(id);
621 * Get the Record with the specified id.
622 * @param {String} id The id of the Record to find.
623 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
625 getById : function(id){
626 return this.data.key(id);
630 * Get the Record at the specified index.
631 * @param {Number} index The index of the Record to find.
632 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
634 getAt : function(index){
635 return this.data.itemAt(index);
639 * Returns a range of Records between specified indices.
640 * @param {Number} startIndex (optional) The starting index (defaults to 0)
641 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642 * @return {Roo.data.Record[]} An array of Records
644 getRange : function(start, end){
645 return this.data.getRange(start, end);
649 storeOptions : function(o){
650 o = Roo.apply({}, o);
653 this.lastOptions = o;
657 * Loads the Record cache from the configured Proxy using the configured Reader.
659 * If using remote paging, then the first load call must specify the <em>start</em>
660 * and <em>limit</em> properties in the options.params property to establish the initial
661 * position within the dataset, and the number of Records to cache on each read from the Proxy.
663 * <strong>It is important to note that for remote data sources, loading is asynchronous,
664 * and this call will return before the new data has been loaded. Perform any post-processing
665 * in a callback function, or in a "load" event handler.</strong>
667 * @param {Object} options An object containing properties which control loading options:<ul>
668 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670 * passed the following arguments:<ul>
671 * <li>r : Roo.data.Record[]</li>
672 * <li>options: Options object from the load call</li>
673 * <li>success: Boolean success indicator</li></ul></li>
674 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
678 load : function(options){
679 options = options || {};
680 if(this.fireEvent("beforeload", this, options) !== false){
681 this.storeOptions(options);
682 var p = Roo.apply(options.params || {}, this.baseParams);
683 // if meta was not loaded from remote source.. try requesting it.
684 if (!this.reader.metaFromRemote) {
687 if(this.sortInfo && this.remoteSort){
688 var pn = this.paramNames;
689 p[pn["sort"]] = this.sortInfo.field;
690 p[pn["dir"]] = this.sortInfo.direction;
692 if (this.multiSort) {
693 var pn = this.paramNames;
694 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
697 this.proxy.load(p, this.reader, this.loadRecords, this, options);
702 * Reloads the Record cache from the configured Proxy using the configured Reader and
703 * the options from the last load operation performed.
704 * @param {Object} options (optional) An object containing properties which may override the options
705 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706 * the most recently used options are reused).
708 reload : function(options){
709 this.load(Roo.applyIf(options||{}, this.lastOptions));
713 // Called as a callback by the Reader during a load operation.
714 loadRecords : function(o, options, success){
715 if(!o || success === false){
716 if(success !== false){
717 this.fireEvent("load", this, [], options, o);
719 if(options.callback){
720 options.callback.call(options.scope || this, [], options, false);
724 // if data returned failure - throw an exception.
725 if (o.success === false) {
726 // show a message if no listener is registered.
727 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
730 // loadmask wil be hooked into this..
731 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
734 var r = o.records, t = o.totalRecords || r.length;
736 this.fireEvent("beforeloadadd", this, r, options, o);
738 if(!options || options.add !== true){
739 if(this.pruneModifiedRecords){
742 for(var i = 0, len = r.length; i < len; i++){
746 this.data = this.snapshot;
747 delete this.snapshot;
751 this.totalLength = t;
753 this.fireEvent("datachanged", this);
755 this.totalLength = Math.max(t, this.data.length+r.length);
759 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
761 var e = new Roo.data.Record({});
763 e.set(this.parent.displayField, this.parent.emptyTitle);
764 e.set(this.parent.valueField, '');
769 this.fireEvent("load", this, r, options, o);
770 if(options.callback){
771 options.callback.call(options.scope || this, r, options, true);
777 * Loads data from a passed data block. A Reader which understands the format of the data
778 * must have been configured in the constructor.
779 * @param {Object} data The data block from which to read the Records. The format of the data expected
780 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
783 loadData : function(o, append){
784 var r = this.reader.readRecords(o);
785 this.loadRecords(r, {add: append}, true);
789 * using 'cn' the nested child reader read the child array into it's child stores.
790 * @param {Object} rec The record with a 'children array
792 loadDataFromChildren : function(rec)
794 this.loadData(this.reader.toLoadData(rec));
799 * Gets the number of cached records.
801 * <em>If using paging, this may not be the total size of the dataset. If the data object
802 * used by the Reader contains the dataset size, then the getTotalCount() function returns
803 * the data set size</em>
805 getCount : function(){
806 return this.data.length || 0;
810 * Gets the total number of records in the dataset as returned by the server.
812 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813 * the dataset size</em>
815 getTotalCount : function(){
816 return this.totalLength || 0;
820 * Returns the sort state of the Store as an object with two properties:
822 field {String} The name of the field by which the Records are sorted
823 direction {String} The sort order, "ASC" or "DESC"
826 getSortState : function(){
827 return this.sortInfo;
831 applySort : function(){
832 if(this.sortInfo && !this.remoteSort){
833 var s = this.sortInfo, f = s.field;
834 var st = this.fields.get(f).sortType;
835 var fn = function(r1, r2){
836 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
839 this.data.sort(s.direction, fn);
840 if(this.snapshot && this.snapshot != this.data){
841 this.snapshot.sort(s.direction, fn);
847 * Sets the default sort column and order to be used by the next load operation.
848 * @param {String} fieldName The name of the field to sort by.
849 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851 setDefaultSort : function(field, dir){
852 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
857 * If remote sorting is used, the sort is performed on the server, and the cache is
858 * reloaded. If local sorting is used, the cache is sorted internally.
859 * @param {String} fieldName The name of the field to sort by.
860 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
862 sort : function(fieldName, dir){
863 var f = this.fields.get(fieldName);
865 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
867 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
873 this.sortToggle[f.name] = dir;
874 this.sortInfo = {field: f.name, direction: dir};
875 if(!this.remoteSort){
877 this.fireEvent("datachanged", this);
879 this.load(this.lastOptions);
884 * Calls the specified function for each of the Records in the cache.
885 * @param {Function} fn The function to call. The Record is passed as the first parameter.
886 * Returning <em>false</em> aborts and exits the iteration.
887 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
889 each : function(fn, scope){
890 this.data.each(fn, scope);
894 * Gets all records modified since the last commit. Modified records are persisted across load operations
895 * (e.g., during paging).
896 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
898 getModifiedRecords : function(){
899 return this.modified;
903 createFilterFn : function(property, value, anyMatch){
904 if(!value.exec){ // not a regex
905 value = String(value);
906 if(value.length == 0){
909 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
912 return value.test(r.data[property]);
917 * Sums the value of <i>property</i> for each record between start and end and returns the result.
918 * @param {String} property A field on your records
919 * @param {Number} start The record index to start at (defaults to 0)
920 * @param {Number} end The last record index to include (defaults to length - 1)
921 * @return {Number} The sum
923 sum : function(property, start, end){
924 var rs = this.data.items, v = 0;
926 end = (end || end === 0) ? end : rs.length-1;
928 for(var i = start; i <= end; i++){
929 v += (rs[i].data[property] || 0);
935 * Filter the records by a specified property.
936 * @param {String} field A field on your records
937 * @param {String/RegExp} value Either a string that the field
938 * should start with or a RegExp to test against the field
939 * @param {Boolean} anyMatch True to match any part not just the beginning
941 filter : function(property, value, anyMatch){
942 var fn = this.createFilterFn(property, value, anyMatch);
943 return fn ? this.filterBy(fn) : this.clearFilter();
947 * Filter by a function. The specified function will be called with each
948 * record in this data source. If the function returns true the record is included,
949 * otherwise it is filtered.
950 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951 * @param {Object} scope (optional) The scope of the function (defaults to this)
953 filterBy : function(fn, scope){
954 this.snapshot = this.snapshot || this.data;
955 this.data = this.queryBy(fn, scope||this);
956 this.fireEvent("datachanged", this);
960 * Query the records by a specified property.
961 * @param {String} field A field on your records
962 * @param {String/RegExp} value Either a string that the field
963 * should start with or a RegExp to test against the field
964 * @param {Boolean} anyMatch True to match any part not just the beginning
965 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
967 query : function(property, value, anyMatch){
968 var fn = this.createFilterFn(property, value, anyMatch);
969 return fn ? this.queryBy(fn) : this.data.clone();
973 * Query by a function. The specified function will be called with each
974 * record in this data source. If the function returns true the record is included
976 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977 * @param {Object} scope (optional) The scope of the function (defaults to this)
978 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
980 queryBy : function(fn, scope){
981 var data = this.snapshot || this.data;
982 return data.filterBy(fn, scope||this);
986 * Collects unique values for a particular dataIndex from this store.
987 * @param {String} dataIndex The property to collect
988 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990 * @return {Array} An array of the unique values
992 collect : function(dataIndex, allowNull, bypassFilter){
993 var d = (bypassFilter === true && this.snapshot) ?
994 this.snapshot.items : this.data.items;
995 var v, sv, r = [], l = {};
996 for(var i = 0, len = d.length; i < len; i++){
997 v = d[i].data[dataIndex];
999 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1008 * Revert to a view of the Record cache with no filtering applied.
1009 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1011 clearFilter : function(suppressEvent){
1012 if(this.snapshot && this.snapshot != this.data){
1013 this.data = this.snapshot;
1014 delete this.snapshot;
1015 if(suppressEvent !== true){
1016 this.fireEvent("datachanged", this);
1022 afterEdit : function(record){
1023 if(this.modified.indexOf(record) == -1){
1024 this.modified.push(record);
1026 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1030 afterReject : function(record){
1031 this.modified.remove(record);
1032 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1036 afterCommit : function(record){
1037 this.modified.remove(record);
1038 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1042 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1045 commitChanges : function(){
1046 var m = this.modified.slice(0);
1048 for(var i = 0, len = m.length; i < len; i++){
1054 * Cancel outstanding changes on all changed records.
1056 rejectChanges : function(){
1057 var m = this.modified.slice(0);
1059 for(var i = 0, len = m.length; i < len; i++){
1064 onMetaChange : function(meta, rtype, o){
1065 this.recordType = rtype;
1066 this.fields = rtype.prototype.fields;
1067 delete this.snapshot;
1068 this.sortInfo = meta.sortInfo || this.sortInfo;
1070 this.fireEvent('metachange', this, this.reader.meta);
1073 moveIndex : function(data, type)
1075 var index = this.indexOf(data);
1077 var newIndex = index + type;
1081 this.insert(newIndex, data);
1086 * Ext JS Library 1.1.1
1087 * Copyright(c) 2006-2007, Ext JS, LLC.
1089 * Originally Released Under LGPL - original licence link has changed is not relivant.
1092 * <script type="text/javascript">
1096 * @class Roo.data.SimpleStore
1097 * @extends Roo.data.Store
1098 * Small helper class to make creating Stores from Array data easier.
1099 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100 * @cfg {Array} fields An array of field definition objects, or field name strings.
1101 * @cfg {Object} an existing reader (eg. copied from another store)
1102 * @cfg {Array} data The multi-dimensional array of data
1103 * @cfg {Roo.data.DataProxy} proxy [not-required]
1104 * @cfg {Roo.data.Reader} reader [not-required]
1106 * @param {Object} config
1108 Roo.data.SimpleStore = function(config)
1110 Roo.data.SimpleStore.superclass.constructor.call(this, {
1112 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1115 Roo.data.Record.create(config.fields)
1117 proxy : new Roo.data.MemoryProxy(config.data)
1121 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1123 * Ext JS Library 1.1.1
1124 * Copyright(c) 2006-2007, Ext JS, LLC.
1126 * Originally Released Under LGPL - original licence link has changed is not relivant.
1129 * <script type="text/javascript">
1134 * @extends Roo.data.Store
1135 * @class Roo.data.JsonStore
1136 * Small helper class to make creating Stores for JSON data easier. <br/>
1138 var store = new Roo.data.JsonStore({
1139 url: 'get-images.php',
1141 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1144 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1145 * JsonReader and HttpProxy (unless inline data is provided).</b>
1146 * @cfg {Array} fields An array of field definition objects, or field name strings.
1148 * @param {Object} config
1150 Roo.data.JsonStore = function(c){
1151 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1152 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1153 reader: new Roo.data.JsonReader(c, c.fields)
1156 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1158 * Ext JS Library 1.1.1
1159 * Copyright(c) 2006-2007, Ext JS, LLC.
1161 * Originally Released Under LGPL - original licence link has changed is not relivant.
1164 * <script type="text/javascript">
1168 Roo.data.Field = function(config){
1169 if(typeof config == "string"){
1170 config = {name: config};
1172 Roo.apply(this, config);
1178 var st = Roo.data.SortTypes;
1179 // named sortTypes are supported, here we look them up
1180 if(typeof this.sortType == "string"){
1181 this.sortType = st[this.sortType];
1184 // set default sortType for strings and dates
1188 this.sortType = st.asUCString;
1191 this.sortType = st.asDate;
1194 this.sortType = st.none;
1199 var stripRe = /[\$,%]/g;
1201 // prebuilt conversion function for this field, instead of
1202 // switching every time we're reading a value
1204 var cv, dateFormat = this.dateFormat;
1209 cv = function(v){ return v; };
1212 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1216 return v !== undefined && v !== null && v !== '' ?
1217 parseInt(String(v).replace(stripRe, ""), 10) : '';
1222 return v !== undefined && v !== null && v !== '' ?
1223 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1228 cv = function(v){ return v === true || v === "true" || v == 1; };
1235 if(v instanceof Date){
1239 if(dateFormat == "timestamp"){
1240 return new Date(v*1000);
1242 return Date.parseDate(v, dateFormat);
1244 var parsed = Date.parse(v);
1245 return parsed ? new Date(parsed) : null;
1254 Roo.data.Field.prototype = {
1262 * Ext JS Library 1.1.1
1263 * Copyright(c) 2006-2007, Ext JS, LLC.
1265 * Originally Released Under LGPL - original licence link has changed is not relivant.
1268 * <script type="text/javascript">
1271 // Base class for reading structured data from a data source. This class is intended to be
1272 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1275 * @class Roo.data.DataReader
1277 * Base class for reading structured data from a data source. This class is intended to be
1278 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1281 Roo.data.DataReader = function(meta, recordType){
1285 this.recordType = recordType instanceof Array ?
1286 Roo.data.Record.create(recordType) : recordType;
1289 Roo.data.DataReader.prototype = {
1292 readerType : 'Data',
1294 * Create an empty record
1295 * @param {Object} data (optional) - overlay some values
1296 * @return {Roo.data.Record} record created.
1298 newRow : function(d) {
1300 this.recordType.prototype.fields.each(function(c) {
1302 case 'int' : da[c.name] = 0; break;
1303 case 'date' : da[c.name] = new Date(); break;
1304 case 'float' : da[c.name] = 0.0; break;
1305 case 'boolean' : da[c.name] = false; break;
1306 default : da[c.name] = ""; break;
1310 return new this.recordType(Roo.apply(da, d));
1316 * Ext JS Library 1.1.1
1317 * Copyright(c) 2006-2007, Ext JS, LLC.
1319 * Originally Released Under LGPL - original licence link has changed is not relivant.
1322 * <script type="text/javascript">
1326 * @class Roo.data.DataProxy
1327 * @extends Roo.data.Observable
1329 * This class is an abstract base class for implementations which provide retrieval of
1330 * unformatted data objects.<br>
1332 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1333 * (of the appropriate type which knows how to parse the data object) to provide a block of
1334 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1336 * Custom implementations must implement the load method as described in
1337 * {@link Roo.data.HttpProxy#load}.
1339 Roo.data.DataProxy = function(){
1343 * Fires before a network request is made to retrieve a data object.
1344 * @param {Object} This DataProxy object.
1345 * @param {Object} params The params parameter to the load function.
1350 * Fires before the load method's callback is called.
1351 * @param {Object} This DataProxy object.
1352 * @param {Object} o The data object.
1353 * @param {Object} arg The callback argument object passed to the load function.
1357 * @event loadexception
1358 * Fires if an Exception occurs during data retrieval.
1359 * @param {Object} This DataProxy object.
1360 * @param {Object} o The data object.
1361 * @param {Object} arg The callback argument object passed to the load function.
1362 * @param {Object} e The Exception.
1364 loadexception : true
1366 Roo.data.DataProxy.superclass.constructor.call(this);
1369 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1372 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1376 * Ext JS Library 1.1.1
1377 * Copyright(c) 2006-2007, Ext JS, LLC.
1379 * Originally Released Under LGPL - original licence link has changed is not relivant.
1382 * <script type="text/javascript">
1385 * @class Roo.data.MemoryProxy
1386 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1387 * to the Reader when its load method is called.
1389 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1391 Roo.data.MemoryProxy = function(data){
1395 Roo.data.MemoryProxy.superclass.constructor.call(this);
1399 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1402 * Load data from the requested source (in this case an in-memory
1403 * data object passed to the constructor), read the data object into
1404 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1405 * process that block using the passed callback.
1406 * @param {Object} params This parameter is not used by the MemoryProxy class.
1407 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1408 * object into a block of Roo.data.Records.
1409 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1410 * The function must be passed <ul>
1411 * <li>The Record block object</li>
1412 * <li>The "arg" argument from the load function</li>
1413 * <li>A boolean success indicator</li>
1415 * @param {Object} scope The scope in which to call the callback
1416 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1418 load : function(params, reader, callback, scope, arg){
1419 params = params || {};
1422 result = reader.readRecords(params.data ? params.data :this.data);
1424 this.fireEvent("loadexception", this, arg, null, e);
1425 callback.call(scope, null, arg, false);
1428 callback.call(scope, result, arg, true);
1432 update : function(params, records){
1437 * Ext JS Library 1.1.1
1438 * Copyright(c) 2006-2007, Ext JS, LLC.
1440 * Originally Released Under LGPL - original licence link has changed is not relivant.
1443 * <script type="text/javascript">
1446 * @class Roo.data.HttpProxy
1447 * @extends Roo.data.DataProxy
1448 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1449 * configured to reference a certain URL.<br><br>
1451 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1452 * from which the running page was served.<br><br>
1454 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1456 * Be aware that to enable the browser to parse an XML document, the server must set
1457 * the Content-Type header in the HTTP response to "text/xml".
1459 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1460 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1461 * will be used to make the request.
1463 Roo.data.HttpProxy = function(conn){
1464 Roo.data.HttpProxy.superclass.constructor.call(this);
1465 // is conn a conn config or a real conn?
1467 this.useAjax = !conn || !conn.events;
1471 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1472 // thse are take from connection...
1475 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1478 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1479 * extra parameters to each request made by this object. (defaults to undefined)
1482 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1483 * to each request made by this object. (defaults to undefined)
1486 * @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)
1489 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1492 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1498 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1502 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1503 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1504 * a finer-grained basis than the DataProxy events.
1506 getConnection : function(){
1507 return this.useAjax ? Roo.Ajax : this.conn;
1511 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1512 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1513 * process that block using the passed callback.
1514 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1515 * for the request to the remote server.
1516 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1517 * object into a block of Roo.data.Records.
1518 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1519 * The function must be passed <ul>
1520 * <li>The Record block object</li>
1521 * <li>The "arg" argument from the load function</li>
1522 * <li>A boolean success indicator</li>
1524 * @param {Object} scope The scope in which to call the callback
1525 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1527 load : function(params, reader, callback, scope, arg){
1528 if(this.fireEvent("beforeload", this, params) !== false){
1530 params : params || {},
1532 callback : callback,
1537 callback : this.loadResponse,
1541 Roo.applyIf(o, this.conn);
1542 if(this.activeRequest){
1543 Roo.Ajax.abort(this.activeRequest);
1545 this.activeRequest = Roo.Ajax.request(o);
1547 this.conn.request(o);
1550 callback.call(scope||this, null, arg, false);
1555 loadResponse : function(o, success, response){
1556 delete this.activeRequest;
1558 this.fireEvent("loadexception", this, o, response);
1559 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564 result = o.reader.read(response);
1566 this.fireEvent("loadexception", this, o, response, e);
1567 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1571 this.fireEvent("load", this, o, o.request.arg);
1572 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1576 update : function(dataSet){
1581 updateResponse : function(dataSet){
1586 * Ext JS Library 1.1.1
1587 * Copyright(c) 2006-2007, Ext JS, LLC.
1589 * Originally Released Under LGPL - original licence link has changed is not relivant.
1592 * <script type="text/javascript">
1596 * @class Roo.data.ScriptTagProxy
1597 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1598 * other than the originating domain of the running page.<br><br>
1600 * <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
1601 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1603 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1604 * source code that is used as the source inside a <script> tag.<br><br>
1606 * In order for the browser to process the returned data, the server must wrap the data object
1607 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1608 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1609 * depending on whether the callback name was passed:
1612 boolean scriptTag = false;
1613 String cb = request.getParameter("callback");
1616 response.setContentType("text/javascript");
1618 response.setContentType("application/x-json");
1620 Writer out = response.getWriter();
1622 out.write(cb + "(");
1624 out.print(dataBlock.toJsonString());
1631 * @param {Object} config A configuration object.
1633 Roo.data.ScriptTagProxy = function(config){
1634 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1635 Roo.apply(this, config);
1636 this.head = document.getElementsByTagName("head")[0];
1639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1643 * @cfg {String} url The URL from which to request the data object.
1646 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1650 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1651 * the server the name of the callback function set up by the load call to process the returned data object.
1652 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1653 * javascript output which calls this named function passing the data object as its only parameter.
1655 callbackParam : "callback",
1657 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1658 * name to the request.
1663 * Load data from the configured URL, read the data object into
1664 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1665 * process that block using the passed callback.
1666 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1667 * for the request to the remote server.
1668 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1669 * object into a block of Roo.data.Records.
1670 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1671 * The function must be passed <ul>
1672 * <li>The Record block object</li>
1673 * <li>The "arg" argument from the load function</li>
1674 * <li>A boolean success indicator</li>
1676 * @param {Object} scope The scope in which to call the callback
1677 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1679 load : function(params, reader, callback, scope, arg){
1680 if(this.fireEvent("beforeload", this, params) !== false){
1682 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1685 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1687 url += "&_dc=" + (new Date().getTime());
1689 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1692 cb : "stcCallback"+transId,
1693 scriptId : "stcScript"+transId,
1697 callback : callback,
1703 window[trans.cb] = function(o){
1704 conn.handleResponse(o, trans);
1707 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1709 if(this.autoAbort !== false){
1713 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1715 var script = document.createElement("script");
1716 script.setAttribute("src", url);
1717 script.setAttribute("type", "text/javascript");
1718 script.setAttribute("id", trans.scriptId);
1719 this.head.appendChild(script);
1723 callback.call(scope||this, null, arg, false);
1728 isLoading : function(){
1729 return this.trans ? true : false;
1733 * Abort the current server request.
1736 if(this.isLoading()){
1737 this.destroyTrans(this.trans);
1742 destroyTrans : function(trans, isLoaded){
1743 this.head.removeChild(document.getElementById(trans.scriptId));
1744 clearTimeout(trans.timeoutId);
1746 window[trans.cb] = undefined;
1748 delete window[trans.cb];
1751 // if hasn't been loaded, wait for load to remove it to prevent script error
1752 window[trans.cb] = function(){
1753 window[trans.cb] = undefined;
1755 delete window[trans.cb];
1762 handleResponse : function(o, trans){
1764 this.destroyTrans(trans, true);
1767 result = trans.reader.readRecords(o);
1769 this.fireEvent("loadexception", this, o, trans.arg, e);
1770 trans.callback.call(trans.scope||window, null, trans.arg, false);
1773 this.fireEvent("load", this, o, trans.arg);
1774 trans.callback.call(trans.scope||window, result, trans.arg, true);
1778 handleFailure : function(trans){
1780 this.destroyTrans(trans, false);
1781 this.fireEvent("loadexception", this, null, trans.arg);
1782 trans.callback.call(trans.scope||window, null, trans.arg, false);
1786 * Ext JS Library 1.1.1
1787 * Copyright(c) 2006-2007, Ext JS, LLC.
1789 * Originally Released Under LGPL - original licence link has changed is not relivant.
1792 * <script type="text/javascript">
1796 * @class Roo.data.JsonReader
1797 * @extends Roo.data.DataReader
1798 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1799 * based on mappings in a provided Roo.data.Record constructor.
1801 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1802 * in the reply previously.
1807 var RecordDef = Roo.data.Record.create([
1808 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1809 {name: 'occupation'} // This field will use "occupation" as the mapping.
1811 var myReader = new Roo.data.JsonReader({
1812 totalProperty: "results", // The property which contains the total dataset size (optional)
1813 root: "rows", // The property which contains an Array of row objects
1814 id: "id" // The property within each row object that provides an ID for the record (optional)
1818 * This would consume a JSON file like this:
1820 { 'results': 2, 'rows': [
1821 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1822 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1825 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1826 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1827 * paged from the remote server.
1828 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1829 * @cfg {String} root name of the property which contains the Array of row objects.
1830 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1831 * @cfg {Array} fields Array of field definition objects
1833 * Create a new JsonReader
1834 * @param {Object} meta Metadata configuration options
1835 * @param {Object} recordType Either an Array of field definition objects,
1836 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1838 Roo.data.JsonReader = function(meta, recordType){
1841 // set some defaults:
1843 totalProperty: 'total',
1844 successProperty : 'success',
1849 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1851 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1853 readerType : 'Json',
1856 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1857 * Used by Store query builder to append _requestMeta to params.
1860 metaFromRemote : false,
1862 * This method is only used by a DataProxy which has retrieved data from a remote server.
1863 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1864 * @return {Object} data A data block which is used by an Roo.data.Store object as
1865 * a cache of Roo.data.Records.
1867 read : function(response){
1868 var json = response.responseText;
1870 var o = /* eval:var:o */ eval("("+json+")");
1872 throw {message: "JsonReader.read: Json object not found"};
1878 this.metaFromRemote = true;
1879 this.meta = o.metaData;
1880 this.recordType = Roo.data.Record.create(o.metaData.fields);
1881 this.onMetaChange(this.meta, this.recordType, o);
1883 return this.readRecords(o);
1886 // private function a store will implement
1887 onMetaChange : function(meta, recordType, o){
1894 simpleAccess: function(obj, subsc) {
1901 getJsonAccessor: function(){
1903 return function(expr) {
1905 return(re.test(expr))
1906 ? new Function("obj", "return obj." + expr)
1916 * Create a data block containing Roo.data.Records from an XML document.
1917 * @param {Object} o An object which contains an Array of row objects in the property specified
1918 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1919 * which contains the total size of the dataset.
1920 * @return {Object} data A data block which is used by an Roo.data.Store object as
1921 * a cache of Roo.data.Records.
1923 readRecords : function(o){
1925 * After any data loads, the raw JSON data is available for further custom processing.
1929 var s = this.meta, Record = this.recordType,
1930 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1932 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1934 if(s.totalProperty) {
1935 this.getTotal = this.getJsonAccessor(s.totalProperty);
1937 if(s.successProperty) {
1938 this.getSuccess = this.getJsonAccessor(s.successProperty);
1940 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1942 var g = this.getJsonAccessor(s.id);
1943 this.getId = function(rec) {
1945 return (r === undefined || r === "") ? null : r;
1948 this.getId = function(){return null;};
1951 for(var jj = 0; jj < fl; jj++){
1953 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1954 this.ef[jj] = this.getJsonAccessor(map);
1958 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1959 if(s.totalProperty){
1960 var vt = parseInt(this.getTotal(o), 10);
1965 if(s.successProperty){
1966 var vs = this.getSuccess(o);
1967 if(vs === false || vs === 'false'){
1972 for(var i = 0; i < c; i++){
1975 var id = this.getId(n);
1976 for(var j = 0; j < fl; j++){
1978 var v = this.ef[j](n);
1980 Roo.log('missing convert for ' + f.name);
1984 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1986 var record = new Record(values, id);
1988 records[i] = record;
1994 totalRecords : totalRecords
1997 // used when loading children.. @see loadDataFromChildren
1998 toLoadData: function(rec)
2000 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2001 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2002 return { data : data, total : data.length };
2007 * Ext JS Library 1.1.1
2008 * Copyright(c) 2006-2007, Ext JS, LLC.
2010 * Originally Released Under LGPL - original licence link has changed is not relivant.
2013 * <script type="text/javascript">
2017 * @class Roo.data.XmlReader
2018 * @extends Roo.data.DataReader
2019 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2020 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2022 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2023 * header in the HTTP response must be set to "text/xml".</em>
2027 var RecordDef = Roo.data.Record.create([
2028 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2029 {name: 'occupation'} // This field will use "occupation" as the mapping.
2031 var myReader = new Roo.data.XmlReader({
2032 totalRecords: "results", // The element which contains the total dataset size (optional)
2033 record: "row", // The repeated element which contains row information
2034 id: "id" // The element within the row that provides an ID for the record (optional)
2038 * This would consume an XML file like this:
2042 <results>2</results>
2045 <name>Bill</name>
2046 <occupation>Gardener</occupation>
2050 <name>Ben</name>
2051 <occupation>Horticulturalist</occupation>
2055 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2056 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2057 * paged from the remote server.
2058 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2059 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2060 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2061 * a record identifier value.
2063 * Create a new XmlReader
2064 * @param {Object} meta Metadata configuration options
2065 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2066 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2067 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2069 Roo.data.XmlReader = function(meta, recordType){
2071 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2073 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2078 * This method is only used by a DataProxy which has retrieved data from a remote server.
2079 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2080 * to contain a method called 'responseXML' that returns an XML document object.
2081 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2082 * a cache of Roo.data.Records.
2084 read : function(response){
2085 var doc = response.responseXML;
2087 throw {message: "XmlReader.read: XML Document not available"};
2089 return this.readRecords(doc);
2093 * Create a data block containing Roo.data.Records from an XML document.
2094 * @param {Object} doc A parsed XML document.
2095 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2096 * a cache of Roo.data.Records.
2098 readRecords : function(doc){
2100 * After any data loads/reads, the raw XML Document is available for further custom processing.
2104 var root = doc.documentElement || doc;
2105 var q = Roo.DomQuery;
2106 var recordType = this.recordType, fields = recordType.prototype.fields;
2107 var sid = this.meta.id;
2108 var totalRecords = 0, success = true;
2109 if(this.meta.totalRecords){
2110 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2113 if(this.meta.success){
2114 var sv = q.selectValue(this.meta.success, root, true);
2115 success = sv !== false && sv !== 'false';
2118 var ns = q.select(this.meta.record, root);
2119 for(var i = 0, len = ns.length; i < len; i++) {
2122 var id = sid ? q.selectValue(sid, n) : undefined;
2123 for(var j = 0, jlen = fields.length; j < jlen; j++){
2124 var f = fields.items[j];
2125 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2129 var record = new recordType(values, id);
2131 records[records.length] = record;
2137 totalRecords : totalRecords || records.length
2142 * Ext JS Library 1.1.1
2143 * Copyright(c) 2006-2007, Ext JS, LLC.
2145 * Originally Released Under LGPL - original licence link has changed is not relivant.
2148 * <script type="text/javascript">
2152 * @class Roo.data.ArrayReader
2153 * @extends Roo.data.DataReader
2154 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2155 * Each element of that Array represents a row of data fields. The
2156 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2157 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2161 var RecordDef = Roo.data.Record.create([
2162 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2163 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2165 var myReader = new Roo.data.ArrayReader({
2166 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2170 * This would consume an Array like this:
2172 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2176 * Create a new JsonReader
2177 * @param {Object} meta Metadata configuration options.
2178 * @param {Object|Array} recordType Either an Array of field definition objects
2180 * @cfg {Array} fields Array of field definition objects
2181 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2182 * as specified to {@link Roo.data.Record#create},
2183 * or an {@link Roo.data.Record} object
2186 * created using {@link Roo.data.Record#create}.
2188 Roo.data.ArrayReader = function(meta, recordType)
2190 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2193 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2196 * Create a data block containing Roo.data.Records from an XML document.
2197 * @param {Object} o An Array of row objects which represents the dataset.
2198 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2199 * a cache of Roo.data.Records.
2201 readRecords : function(o)
2203 var sid = this.meta ? this.meta.id : null;
2204 var recordType = this.recordType, fields = recordType.prototype.fields;
2207 for(var i = 0; i < root.length; i++){
2210 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2211 for(var j = 0, jlen = fields.length; j < jlen; j++){
2212 var f = fields.items[j];
2213 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2214 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2218 var record = new recordType(values, id);
2220 records[records.length] = record;
2224 totalRecords : records.length
2227 // used when loading children.. @see loadDataFromChildren
2228 toLoadData: function(rec)
2230 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2231 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2238 * Ext JS Library 1.1.1
2239 * Copyright(c) 2006-2007, Ext JS, LLC.
2241 * Originally Released Under LGPL - original licence link has changed is not relivant.
2244 * <script type="text/javascript">
2249 * @class Roo.data.Tree
2250 * @extends Roo.util.Observable
2251 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2252 * in the tree have most standard DOM functionality.
2254 * @param {Node} root (optional) The root node
2256 Roo.data.Tree = function(root){
2259 * The root node for this tree
2264 this.setRootNode(root);
2269 * Fires when a new child node is appended to a node in this tree.
2270 * @param {Tree} tree The owner tree
2271 * @param {Node} parent The parent node
2272 * @param {Node} node The newly appended node
2273 * @param {Number} index The index of the newly appended node
2278 * Fires when a child node is removed from a node in this tree.
2279 * @param {Tree} tree The owner tree
2280 * @param {Node} parent The parent node
2281 * @param {Node} node The child node removed
2286 * Fires when a node is moved to a new location in the tree
2287 * @param {Tree} tree The owner tree
2288 * @param {Node} node The node moved
2289 * @param {Node} oldParent The old parent of this node
2290 * @param {Node} newParent The new parent of this node
2291 * @param {Number} index The index it was moved to
2296 * Fires when a new child node is inserted in a node in this tree.
2297 * @param {Tree} tree The owner tree
2298 * @param {Node} parent The parent node
2299 * @param {Node} node The child node inserted
2300 * @param {Node} refNode The child node the node was inserted before
2304 * @event beforeappend
2305 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2306 * @param {Tree} tree The owner tree
2307 * @param {Node} parent The parent node
2308 * @param {Node} node The child node to be appended
2310 "beforeappend" : true,
2312 * @event beforeremove
2313 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2314 * @param {Tree} tree The owner tree
2315 * @param {Node} parent The parent node
2316 * @param {Node} node The child node to be removed
2318 "beforeremove" : true,
2321 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2322 * @param {Tree} tree The owner tree
2323 * @param {Node} node The node being moved
2324 * @param {Node} oldParent The parent of the node
2325 * @param {Node} newParent The new parent the node is moving to
2326 * @param {Number} index The index it is being moved to
2328 "beforemove" : true,
2330 * @event beforeinsert
2331 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2332 * @param {Tree} tree The owner tree
2333 * @param {Node} parent The parent node
2334 * @param {Node} node The child node to be inserted
2335 * @param {Node} refNode The child node the node is being inserted before
2337 "beforeinsert" : true
2340 Roo.data.Tree.superclass.constructor.call(this);
2343 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2346 proxyNodeEvent : function(){
2347 return this.fireEvent.apply(this, arguments);
2351 * Returns the root node for this tree.
2354 getRootNode : function(){
2359 * Sets the root node for this tree.
2360 * @param {Node} node
2363 setRootNode : function(node){
2365 node.ownerTree = this;
2367 this.registerNode(node);
2372 * Gets a node in this tree by its id.
2373 * @param {String} id
2376 getNodeById : function(id){
2377 return this.nodeHash[id];
2380 registerNode : function(node){
2381 this.nodeHash[node.id] = node;
2384 unregisterNode : function(node){
2385 delete this.nodeHash[node.id];
2388 toString : function(){
2389 return "[Tree"+(this.id?" "+this.id:"")+"]";
2394 * @class Roo.data.Node
2395 * @extends Roo.util.Observable
2396 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2397 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2399 * @param {Object} attributes The attributes/config for the node
2401 Roo.data.Node = function(attributes){
2403 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2406 this.attributes = attributes || {};
2407 this.leaf = this.attributes.leaf;
2409 * The node id. @type String
2411 this.id = this.attributes.id;
2413 this.id = Roo.id(null, "ynode-");
2414 this.attributes.id = this.id;
2419 * All child nodes of this node. @type Array
2421 this.childNodes = [];
2422 if(!this.childNodes.indexOf){ // indexOf is a must
2423 this.childNodes.indexOf = function(o){
2424 for(var i = 0, len = this.length; i < len; i++){
2433 * The parent node for this node. @type Node
2435 this.parentNode = null;
2437 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2439 this.firstChild = null;
2441 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2443 this.lastChild = null;
2445 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2447 this.previousSibling = null;
2449 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2451 this.nextSibling = null;
2456 * Fires when a new child node is appended
2457 * @param {Tree} tree The owner tree
2458 * @param {Node} this This node
2459 * @param {Node} node The newly appended node
2460 * @param {Number} index The index of the newly appended node
2465 * Fires when a child node is removed
2466 * @param {Tree} tree The owner tree
2467 * @param {Node} this This node
2468 * @param {Node} node The removed node
2473 * Fires when this node is moved to a new location in the tree
2474 * @param {Tree} tree The owner tree
2475 * @param {Node} this This node
2476 * @param {Node} oldParent The old parent of this node
2477 * @param {Node} newParent The new parent of this node
2478 * @param {Number} index The index it was moved to
2483 * Fires when a new child node is inserted.
2484 * @param {Tree} tree The owner tree
2485 * @param {Node} this This node
2486 * @param {Node} node The child node inserted
2487 * @param {Node} refNode The child node the node was inserted before
2491 * @event beforeappend
2492 * Fires before a new child is appended, return false to cancel the append.
2493 * @param {Tree} tree The owner tree
2494 * @param {Node} this This node
2495 * @param {Node} node The child node to be appended
2497 "beforeappend" : true,
2499 * @event beforeremove
2500 * Fires before a child is removed, return false to cancel the remove.
2501 * @param {Tree} tree The owner tree
2502 * @param {Node} this This node
2503 * @param {Node} node The child node to be removed
2505 "beforeremove" : true,
2508 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2509 * @param {Tree} tree The owner tree
2510 * @param {Node} this This node
2511 * @param {Node} oldParent The parent of this node
2512 * @param {Node} newParent The new parent this node is moving to
2513 * @param {Number} index The index it is being moved to
2515 "beforemove" : true,
2517 * @event beforeinsert
2518 * Fires before a new child is inserted, return false to cancel the insert.
2519 * @param {Tree} tree The owner tree
2520 * @param {Node} this This node
2521 * @param {Node} node The child node to be inserted
2522 * @param {Node} refNode The child node the node is being inserted before
2524 "beforeinsert" : true
2526 this.listeners = this.attributes.listeners;
2527 Roo.data.Node.superclass.constructor.call(this);
2530 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2531 fireEvent : function(evtName){
2532 // first do standard event for this node
2533 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2536 // then bubble it up to the tree if the event wasn't cancelled
2537 var ot = this.getOwnerTree();
2539 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2547 * Returns true if this node is a leaf
2550 isLeaf : function(){
2551 return this.leaf === true;
2555 setFirstChild : function(node){
2556 this.firstChild = node;
2560 setLastChild : function(node){
2561 this.lastChild = node;
2566 * Returns true if this node is the last child of its parent
2569 isLast : function(){
2570 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2574 * Returns true if this node is the first child of its parent
2577 isFirst : function(){
2578 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2581 hasChildNodes : function(){
2582 return !this.isLeaf() && this.childNodes.length > 0;
2586 * Insert node(s) as the last child node of this node.
2587 * @param {Node/Array} node The node or Array of nodes to append
2588 * @return {Node} The appended node if single append, or null if an array was passed
2590 appendChild : function(node){
2592 if(node instanceof Array){
2594 }else if(arguments.length > 1){
2598 // if passed an array or multiple args do them one by one
2600 for(var i = 0, len = multi.length; i < len; i++) {
2601 this.appendChild(multi[i]);
2604 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2607 var index = this.childNodes.length;
2608 var oldParent = node.parentNode;
2609 // it's a move, make sure we move it cleanly
2611 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2614 oldParent.removeChild(node);
2617 index = this.childNodes.length;
2619 this.setFirstChild(node);
2621 this.childNodes.push(node);
2622 node.parentNode = this;
2623 var ps = this.childNodes[index-1];
2625 node.previousSibling = ps;
2626 ps.nextSibling = node;
2628 node.previousSibling = null;
2630 node.nextSibling = null;
2631 this.setLastChild(node);
2632 node.setOwnerTree(this.getOwnerTree());
2633 this.fireEvent("append", this.ownerTree, this, node, index);
2634 if(this.ownerTree) {
2635 this.ownerTree.fireEvent("appendnode", this, node, index);
2638 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2645 * Removes a child node from this node.
2646 * @param {Node} node The node to remove
2647 * @return {Node} The removed node
2649 removeChild : function(node){
2650 var index = this.childNodes.indexOf(node);
2654 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2658 // remove it from childNodes collection
2659 this.childNodes.splice(index, 1);
2662 if(node.previousSibling){
2663 node.previousSibling.nextSibling = node.nextSibling;
2665 if(node.nextSibling){
2666 node.nextSibling.previousSibling = node.previousSibling;
2669 // update child refs
2670 if(this.firstChild == node){
2671 this.setFirstChild(node.nextSibling);
2673 if(this.lastChild == node){
2674 this.setLastChild(node.previousSibling);
2677 node.setOwnerTree(null);
2678 // clear any references from the node
2679 node.parentNode = null;
2680 node.previousSibling = null;
2681 node.nextSibling = null;
2682 this.fireEvent("remove", this.ownerTree, this, node);
2687 * Inserts the first node before the second node in this nodes childNodes collection.
2688 * @param {Node} node The node to insert
2689 * @param {Node} refNode The node to insert before (if null the node is appended)
2690 * @return {Node} The inserted node
2692 insertBefore : function(node, refNode){
2693 if(!refNode){ // like standard Dom, refNode can be null for append
2694 return this.appendChild(node);
2697 if(node == refNode){
2701 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2704 var index = this.childNodes.indexOf(refNode);
2705 var oldParent = node.parentNode;
2706 var refIndex = index;
2708 // when moving internally, indexes will change after remove
2709 if(oldParent == this && this.childNodes.indexOf(node) < index){
2713 // it's a move, make sure we move it cleanly
2715 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2718 oldParent.removeChild(node);
2721 this.setFirstChild(node);
2723 this.childNodes.splice(refIndex, 0, node);
2724 node.parentNode = this;
2725 var ps = this.childNodes[refIndex-1];
2727 node.previousSibling = ps;
2728 ps.nextSibling = node;
2730 node.previousSibling = null;
2732 node.nextSibling = refNode;
2733 refNode.previousSibling = node;
2734 node.setOwnerTree(this.getOwnerTree());
2735 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2737 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2743 * Returns the child node at the specified index.
2744 * @param {Number} index
2747 item : function(index){
2748 return this.childNodes[index];
2752 * Replaces one child node in this node with another.
2753 * @param {Node} newChild The replacement node
2754 * @param {Node} oldChild The node to replace
2755 * @return {Node} The replaced node
2757 replaceChild : function(newChild, oldChild){
2758 this.insertBefore(newChild, oldChild);
2759 this.removeChild(oldChild);
2764 * Returns the index of a child node
2765 * @param {Node} node
2766 * @return {Number} The index of the node or -1 if it was not found
2768 indexOf : function(child){
2769 return this.childNodes.indexOf(child);
2773 * Returns the tree this node is in.
2776 getOwnerTree : function(){
2777 // if it doesn't have one, look for one
2778 if(!this.ownerTree){
2782 this.ownerTree = p.ownerTree;
2788 return this.ownerTree;
2792 * Returns depth of this node (the root node has a depth of 0)
2795 getDepth : function(){
2798 while(p.parentNode){
2806 setOwnerTree : function(tree){
2807 // if it's move, we need to update everyone
2808 if(tree != this.ownerTree){
2810 this.ownerTree.unregisterNode(this);
2812 this.ownerTree = tree;
2813 var cs = this.childNodes;
2814 for(var i = 0, len = cs.length; i < len; i++) {
2815 cs[i].setOwnerTree(tree);
2818 tree.registerNode(this);
2824 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2825 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2826 * @return {String} The path
2828 getPath : function(attr){
2829 attr = attr || "id";
2830 var p = this.parentNode;
2831 var b = [this.attributes[attr]];
2833 b.unshift(p.attributes[attr]);
2836 var sep = this.getOwnerTree().pathSeparator;
2837 return sep + b.join(sep);
2841 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2842 * function call will be the scope provided or the current node. The arguments to the function
2843 * will be the args provided or the current node. If the function returns false at any point,
2844 * the bubble is stopped.
2845 * @param {Function} fn The function to call
2846 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2847 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2849 bubble : function(fn, scope, args){
2852 if(fn.call(scope || p, args || p) === false){
2860 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2861 * function call will be the scope provided or the current node. The arguments to the function
2862 * will be the args provided or the current node. If the function returns false at any point,
2863 * the cascade is stopped on that branch.
2864 * @param {Function} fn The function to call
2865 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2866 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2868 cascade : function(fn, scope, args){
2869 if(fn.call(scope || this, args || this) !== false){
2870 var cs = this.childNodes;
2871 for(var i = 0, len = cs.length; i < len; i++) {
2872 cs[i].cascade(fn, scope, args);
2878 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2879 * function call will be the scope provided or the current node. The arguments to the function
2880 * will be the args provided or the current node. If the function returns false at any point,
2881 * the iteration stops.
2882 * @param {Function} fn The function to call
2883 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2884 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2886 eachChild : function(fn, scope, args){
2887 var cs = this.childNodes;
2888 for(var i = 0, len = cs.length; i < len; i++) {
2889 if(fn.call(scope || this, args || cs[i]) === false){
2896 * Finds the first child that has the attribute with the specified value.
2897 * @param {String} attribute The attribute name
2898 * @param {Mixed} value The value to search for
2899 * @return {Node} The found child or null if none was found
2901 findChild : function(attribute, value){
2902 var cs = this.childNodes;
2903 for(var i = 0, len = cs.length; i < len; i++) {
2904 if(cs[i].attributes[attribute] == value){
2912 * Finds the first child by a custom function. The child matches if the function passed
2914 * @param {Function} fn
2915 * @param {Object} scope (optional)
2916 * @return {Node} The found child or null if none was found
2918 findChildBy : function(fn, scope){
2919 var cs = this.childNodes;
2920 for(var i = 0, len = cs.length; i < len; i++) {
2921 if(fn.call(scope||cs[i], cs[i]) === true){
2929 * Sorts this nodes children using the supplied sort function
2930 * @param {Function} fn
2931 * @param {Object} scope (optional)
2933 sort : function(fn, scope){
2934 var cs = this.childNodes;
2935 var len = cs.length;
2937 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2939 for(var i = 0; i < len; i++){
2941 n.previousSibling = cs[i-1];
2942 n.nextSibling = cs[i+1];
2944 this.setFirstChild(n);
2947 this.setLastChild(n);
2954 * Returns true if this node is an ancestor (at any point) of the passed node.
2955 * @param {Node} node
2958 contains : function(node){
2959 return node.isAncestor(this);
2963 * Returns true if the passed node is an ancestor (at any point) of this node.
2964 * @param {Node} node
2967 isAncestor : function(node){
2968 var p = this.parentNode;
2978 toString : function(){
2979 return "[Node"+(this.id?" "+this.id:"")+"]";
2983 * Ext JS Library 1.1.1
2984 * Copyright(c) 2006-2007, Ext JS, LLC.
2986 * Originally Released Under LGPL - original licence link has changed is not relivant.
2989 * <script type="text/javascript">
2995 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
2996 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
2997 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2999 * Create a new Shadow
3000 * @param {Object} config The config object
3002 Roo.Shadow = function(config){
3003 Roo.apply(this, config);
3004 if(typeof this.mode != "string"){
3005 this.mode = this.defaultMode;
3007 var o = this.offset, a = {h: 0};
3008 var rad = Math.floor(this.offset/2);
3009 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3015 a.l -= this.offset + rad;
3016 a.t -= this.offset + rad;
3027 a.l -= (this.offset - rad);
3028 a.t -= this.offset + rad;
3030 a.w -= (this.offset - rad)*2;
3041 a.l -= (this.offset - rad);
3042 a.t -= (this.offset - rad);
3044 a.w -= (this.offset + rad + 1);
3045 a.h -= (this.offset + rad);
3054 Roo.Shadow.prototype = {
3056 * @cfg {String} mode
3057 * The shadow display mode. Supports the following options:<br />
3058 * sides: Shadow displays on both sides and bottom only<br />
3059 * frame: Shadow displays equally on all four sides<br />
3060 * drop: Traditional bottom-right drop shadow (default)
3064 * @cfg {String} offset
3065 * The number of pixels to offset the shadow from the element (defaults to 4)
3070 defaultMode: "drop",
3073 * Displays the shadow under the target element
3074 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3076 show : function(target){
3077 target = Roo.get(target);
3079 this.el = Roo.Shadow.Pool.pull();
3080 if(this.el.dom.nextSibling != target.dom){
3081 this.el.insertBefore(target);
3084 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3086 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3089 target.getLeft(true),
3090 target.getTop(true),
3094 this.el.dom.style.display = "block";
3098 * Returns true if the shadow is visible, else false
3100 isVisible : function(){
3101 return this.el ? true : false;
3105 * Direct alignment when values are already available. Show must be called at least once before
3106 * calling this method to ensure it is initialized.
3107 * @param {Number} left The target element left position
3108 * @param {Number} top The target element top position
3109 * @param {Number} width The target element width
3110 * @param {Number} height The target element height
3112 realign : function(l, t, w, h){
3116 var a = this.adjusts, d = this.el.dom, s = d.style;
3118 s.left = (l+a.l)+"px";
3119 s.top = (t+a.t)+"px";
3120 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3122 if(s.width != sws || s.height != shs){
3126 var cn = d.childNodes;
3127 var sww = Math.max(0, (sw-12))+"px";
3128 cn[0].childNodes[1].style.width = sww;
3129 cn[1].childNodes[1].style.width = sww;
3130 cn[2].childNodes[1].style.width = sww;
3131 cn[1].style.height = Math.max(0, (sh-12))+"px";
3141 this.el.dom.style.display = "none";
3142 Roo.Shadow.Pool.push(this.el);
3148 * Adjust the z-index of this shadow
3149 * @param {Number} zindex The new z-index
3151 setZIndex : function(z){
3154 this.el.setStyle("z-index", z);
3159 // Private utility class that manages the internal Shadow cache
3160 Roo.Shadow.Pool = function(){
3162 var markup = Roo.isIE ?
3163 '<div class="x-ie-shadow"></div>' :
3164 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3169 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3170 sh.autoBoxAdjust = false;
3175 push : function(sh){
3181 * Ext JS Library 1.1.1
3182 * Copyright(c) 2006-2007, Ext JS, LLC.
3184 * Originally Released Under LGPL - original licence link has changed is not relivant.
3187 * <script type="text/javascript">
3192 * @class Roo.SplitBar
3193 * @extends Roo.util.Observable
3194 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3198 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3199 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3200 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3201 split.minSize = 100;
3202 split.maxSize = 600;
3203 split.animate = true;
3204 split.on('moved', splitterMoved);
3207 * Create a new SplitBar
3208 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3209 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3210 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3211 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3212 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3213 position of the SplitBar).
3215 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3218 this.el = Roo.get(dragElement, true);
3219 this.el.dom.unselectable = "on";
3221 this.resizingEl = Roo.get(resizingElement, true);
3225 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3226 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3229 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3232 * The minimum size of the resizing element. (Defaults to 0)
3238 * The maximum size of the resizing element. (Defaults to 2000)
3241 this.maxSize = 2000;
3244 * Whether to animate the transition to the new size
3247 this.animate = false;
3250 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3253 this.useShim = false;
3260 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3262 this.proxy = Roo.get(existingProxy).dom;
3265 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3268 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3271 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3274 this.dragSpecs = {};
3277 * @private The adapter to use to positon and resize elements
3279 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3280 this.adapter.init(this);
3282 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3284 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3285 this.el.addClass("x-splitbar-h");
3288 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3289 this.el.addClass("x-splitbar-v");
3295 * Fires when the splitter is moved (alias for {@link #event-moved})
3296 * @param {Roo.SplitBar} this
3297 * @param {Number} newSize the new width or height
3302 * Fires when the splitter is moved
3303 * @param {Roo.SplitBar} this
3304 * @param {Number} newSize the new width or height
3308 * @event beforeresize
3309 * Fires before the splitter is dragged
3310 * @param {Roo.SplitBar} this
3312 "beforeresize" : true,
3314 "beforeapply" : true
3317 Roo.util.Observable.call(this);
3320 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3321 onStartProxyDrag : function(x, y){
3322 this.fireEvent("beforeresize", this);
3324 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3326 o.enableDisplayMode("block");
3327 // all splitbars share the same overlay
3328 Roo.SplitBar.prototype.overlay = o;
3330 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3331 this.overlay.show();
3332 Roo.get(this.proxy).setDisplayed("block");
3333 var size = this.adapter.getElementSize(this);
3334 this.activeMinSize = this.getMinimumSize();;
3335 this.activeMaxSize = this.getMaximumSize();;
3336 var c1 = size - this.activeMinSize;
3337 var c2 = Math.max(this.activeMaxSize - size, 0);
3338 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3339 this.dd.resetConstraints();
3340 this.dd.setXConstraint(
3341 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3342 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3344 this.dd.setYConstraint(0, 0);
3346 this.dd.resetConstraints();
3347 this.dd.setXConstraint(0, 0);
3348 this.dd.setYConstraint(
3349 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3350 this.placement == Roo.SplitBar.TOP ? c2 : c1
3353 this.dragSpecs.startSize = size;
3354 this.dragSpecs.startPoint = [x, y];
3355 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3359 * @private Called after the drag operation by the DDProxy
3361 onEndProxyDrag : function(e){
3362 Roo.get(this.proxy).setDisplayed(false);
3363 var endPoint = Roo.lib.Event.getXY(e);
3365 this.overlay.hide();
3368 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3369 newSize = this.dragSpecs.startSize +
3370 (this.placement == Roo.SplitBar.LEFT ?
3371 endPoint[0] - this.dragSpecs.startPoint[0] :
3372 this.dragSpecs.startPoint[0] - endPoint[0]
3375 newSize = this.dragSpecs.startSize +
3376 (this.placement == Roo.SplitBar.TOP ?
3377 endPoint[1] - this.dragSpecs.startPoint[1] :
3378 this.dragSpecs.startPoint[1] - endPoint[1]
3381 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3382 if(newSize != this.dragSpecs.startSize){
3383 if(this.fireEvent('beforeapply', this, newSize) !== false){
3384 this.adapter.setElementSize(this, newSize);
3385 this.fireEvent("moved", this, newSize);
3386 this.fireEvent("resize", this, newSize);
3392 * Get the adapter this SplitBar uses
3393 * @return The adapter object
3395 getAdapter : function(){
3396 return this.adapter;
3400 * Set the adapter this SplitBar uses
3401 * @param {Object} adapter A SplitBar adapter object
3403 setAdapter : function(adapter){
3404 this.adapter = adapter;
3405 this.adapter.init(this);
3409 * Gets the minimum size for the resizing element
3410 * @return {Number} The minimum size
3412 getMinimumSize : function(){
3413 return this.minSize;
3417 * Sets the minimum size for the resizing element
3418 * @param {Number} minSize The minimum size
3420 setMinimumSize : function(minSize){
3421 this.minSize = minSize;
3425 * Gets the maximum size for the resizing element
3426 * @return {Number} The maximum size
3428 getMaximumSize : function(){
3429 return this.maxSize;
3433 * Sets the maximum size for the resizing element
3434 * @param {Number} maxSize The maximum size
3436 setMaximumSize : function(maxSize){
3437 this.maxSize = maxSize;
3441 * Sets the initialize size for the resizing element
3442 * @param {Number} size The initial size
3444 setCurrentSize : function(size){
3445 var oldAnimate = this.animate;
3446 this.animate = false;
3447 this.adapter.setElementSize(this, size);
3448 this.animate = oldAnimate;
3452 * Destroy this splitbar.
3453 * @param {Boolean} removeEl True to remove the element
3455 destroy : function(removeEl){
3460 this.proxy.parentNode.removeChild(this.proxy);
3468 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3470 Roo.SplitBar.createProxy = function(dir){
3471 var proxy = new Roo.Element(document.createElement("div"));
3472 proxy.unselectable();
3473 var cls = 'x-splitbar-proxy';
3474 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3475 document.body.appendChild(proxy.dom);
3480 * @class Roo.SplitBar.BasicLayoutAdapter
3481 * Default Adapter. It assumes the splitter and resizing element are not positioned
3482 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3484 Roo.SplitBar.BasicLayoutAdapter = function(){
3487 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3488 // do nothing for now
3493 * Called before drag operations to get the current size of the resizing element.
3494 * @param {Roo.SplitBar} s The SplitBar using this adapter
3496 getElementSize : function(s){
3497 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3498 return s.resizingEl.getWidth();
3500 return s.resizingEl.getHeight();
3505 * Called after drag operations to set the size of the resizing element.
3506 * @param {Roo.SplitBar} s The SplitBar using this adapter
3507 * @param {Number} newSize The new size to set
3508 * @param {Function} onComplete A function to be invoked when resizing is complete
3510 setElementSize : function(s, newSize, onComplete){
3511 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3513 s.resizingEl.setWidth(newSize);
3515 onComplete(s, newSize);
3518 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3523 s.resizingEl.setHeight(newSize);
3525 onComplete(s, newSize);
3528 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3535 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3536 * @extends Roo.SplitBar.BasicLayoutAdapter
3537 * Adapter that moves the splitter element to align with the resized sizing element.
3538 * Used with an absolute positioned SplitBar.
3539 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3540 * document.body, make sure you assign an id to the body element.
3542 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3543 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3544 this.container = Roo.get(container);
3547 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3552 getElementSize : function(s){
3553 return this.basic.getElementSize(s);
3556 setElementSize : function(s, newSize, onComplete){
3557 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3560 moveSplitter : function(s){
3561 var yes = Roo.SplitBar;
3562 switch(s.placement){
3564 s.el.setX(s.resizingEl.getRight());
3567 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3570 s.el.setY(s.resizingEl.getBottom());
3573 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3580 * Orientation constant - Create a vertical SplitBar
3584 Roo.SplitBar.VERTICAL = 1;
3587 * Orientation constant - Create a horizontal SplitBar
3591 Roo.SplitBar.HORIZONTAL = 2;
3594 * Placement constant - The resizing element is to the left of the splitter element
3598 Roo.SplitBar.LEFT = 1;
3601 * Placement constant - The resizing element is to the right of the splitter element
3605 Roo.SplitBar.RIGHT = 2;
3608 * Placement constant - The resizing element is positioned above the splitter element
3612 Roo.SplitBar.TOP = 3;
3615 * Placement constant - The resizing element is positioned under splitter element
3619 Roo.SplitBar.BOTTOM = 4;
3622 * Ext JS Library 1.1.1
3623 * Copyright(c) 2006-2007, Ext JS, LLC.
3625 * Originally Released Under LGPL - original licence link has changed is not relivant.
3628 * <script type="text/javascript">
3633 * @extends Roo.util.Observable
3634 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
3635 * This class also supports single and multi selection modes. <br>
3636 * Create a data model bound view:
3638 var store = new Roo.data.Store(...);
3640 var view = new Roo.View({
3642 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
3645 selectedClass: "ydataview-selected",
3649 // listen for node click?
3650 view.on("click", function(vw, index, node, e){
3651 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3655 dataModel.load("foobar.xml");
3657 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3659 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3660 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3662 * Note: old style constructor is still suported (container, template, config)
3666 * @param {Object} config The config object
3669 Roo.View = function(config, depreciated_tpl, depreciated_config){
3671 this.parent = false;
3673 if (typeof(depreciated_tpl) == 'undefined') {
3674 // new way.. - universal constructor.
3675 Roo.apply(this, config);
3676 this.el = Roo.get(this.el);
3679 this.el = Roo.get(config);
3680 this.tpl = depreciated_tpl;
3681 Roo.apply(this, depreciated_config);
3683 this.wrapEl = this.el.wrap().wrap();
3684 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3687 if(typeof(this.tpl) == "string"){
3688 this.tpl = new Roo.Template(this.tpl);
3690 // support xtype ctors..
3691 this.tpl = new Roo.factory(this.tpl, Roo);
3700 * @event beforeclick
3701 * Fires before a click is processed. Returns false to cancel the default action.
3702 * @param {Roo.View} this
3703 * @param {Number} index The index of the target node
3704 * @param {HTMLElement} node The target node
3705 * @param {Roo.EventObject} e The raw event object
3707 "beforeclick" : true,
3710 * Fires when a template node is clicked.
3711 * @param {Roo.View} this
3712 * @param {Number} index The index of the target node
3713 * @param {HTMLElement} node The target node
3714 * @param {Roo.EventObject} e The raw event object
3719 * Fires when a template node is double clicked.
3720 * @param {Roo.View} this
3721 * @param {Number} index The index of the target node
3722 * @param {HTMLElement} node The target node
3723 * @param {Roo.EventObject} e The raw event object
3727 * @event contextmenu
3728 * Fires when a template node is right clicked.
3729 * @param {Roo.View} this
3730 * @param {Number} index The index of the target node
3731 * @param {HTMLElement} node The target node
3732 * @param {Roo.EventObject} e The raw event object
3734 "contextmenu" : true,
3736 * @event selectionchange
3737 * Fires when the selected nodes change.
3738 * @param {Roo.View} this
3739 * @param {Array} selections Array of the selected nodes
3741 "selectionchange" : true,
3744 * @event beforeselect
3745 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3746 * @param {Roo.View} this
3747 * @param {HTMLElement} node The node to be selected
3748 * @param {Array} selections Array of currently selected nodes
3750 "beforeselect" : true,
3752 * @event preparedata
3753 * Fires on every row to render, to allow you to change the data.
3754 * @param {Roo.View} this
3755 * @param {Object} data to be rendered (change this)
3757 "preparedata" : true
3765 "click": this.onClick,
3766 "dblclick": this.onDblClick,
3767 "contextmenu": this.onContextMenu,
3771 this.selections = [];
3773 this.cmp = new Roo.CompositeElementLite([]);
3775 this.store = Roo.factory(this.store, Roo.data);
3776 this.setStore(this.store, true);
3779 if ( this.footer && this.footer.xtype) {
3781 var fctr = this.wrapEl.appendChild(document.createElement("div"));
3783 this.footer.dataSource = this.store;
3784 this.footer.container = fctr;
3785 this.footer = Roo.factory(this.footer, Roo);
3786 fctr.insertFirst(this.el);
3788 // this is a bit insane - as the paging toolbar seems to detach the el..
3789 // dom.parentNode.parentNode.parentNode
3790 // they get detached?
3794 Roo.View.superclass.constructor.call(this);
3799 Roo.extend(Roo.View, Roo.util.Observable, {
3802 * @cfg {Roo.data.Store} store Data store to load data from.
3807 * @cfg {String|Roo.Element} el The container element.
3812 * @cfg {String|Roo.Template} tpl The template used by this View
3816 * @cfg {String} dataName the named area of the template to use as the data area
3817 * Works with domtemplates roo-name="name"
3821 * @cfg {String} selectedClass The css class to add to selected nodes
3823 selectedClass : "x-view-selected",
3825 * @cfg {String} emptyText The empty text to show when nothing is loaded.
3830 * @cfg {String} text to display on mask (default Loading)
3834 * @cfg {Boolean} multiSelect Allow multiple selection
3836 multiSelect : false,
3838 * @cfg {Boolean} singleSelect Allow single selection
3840 singleSelect: false,
3843 * @cfg {Boolean} toggleSelect - selecting
3845 toggleSelect : false,
3848 * @cfg {Boolean} tickable - selecting
3853 * Returns the element this view is bound to.
3854 * @return {Roo.Element}
3863 * Refreshes the view. - called by datachanged on the store. - do not call directly.
3865 refresh : function(){
3866 //Roo.log('refresh');
3869 // if we are using something like 'domtemplate', then
3870 // the what gets used is:
3871 // t.applySubtemplate(NAME, data, wrapping data..)
3872 // the outer template then get' applied with
3873 // the store 'extra data'
3874 // and the body get's added to the
3875 // roo-name="data" node?
3876 // <span class='roo-tpl-{name}'></span> ?????
3880 this.clearSelections();
3883 var records = this.store.getRange();
3884 if(records.length < 1) {
3886 // is this valid?? = should it render a template??
3888 this.el.update(this.emptyText);
3892 if (this.dataName) {
3893 this.el.update(t.apply(this.store.meta)); //????
3894 el = this.el.child('.roo-tpl-' + this.dataName);
3897 for(var i = 0, len = records.length; i < len; i++){
3898 var data = this.prepareData(records[i].data, i, records[i]);
3899 this.fireEvent("preparedata", this, data, i, records[i]);
3901 var d = Roo.apply({}, data);
3904 Roo.apply(d, {'roo-id' : Roo.id()});
3908 Roo.each(this.parent.item, function(item){
3909 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3912 Roo.apply(d, {'roo-data-checked' : 'checked'});
3916 html[html.length] = Roo.util.Format.trim(
3918 t.applySubtemplate(this.dataName, d, this.store.meta) :
3925 el.update(html.join(""));
3926 this.nodes = el.dom.childNodes;
3927 this.updateIndexes(0);
3932 * Function to override to reformat the data that is sent to
3933 * the template for each node.
3934 * DEPRICATED - use the preparedata event handler.
3935 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3936 * a JSON object for an UpdateManager bound view).
3938 prepareData : function(data, index, record)
3940 this.fireEvent("preparedata", this, data, index, record);
3944 onUpdate : function(ds, record){
3945 // Roo.log('on update');
3946 this.clearSelections();
3947 var index = this.store.indexOf(record);
3948 var n = this.nodes[index];
3949 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3950 n.parentNode.removeChild(n);
3951 this.updateIndexes(index, index);
3957 onAdd : function(ds, records, index)
3959 //Roo.log(['on Add', ds, records, index] );
3960 this.clearSelections();
3961 if(this.nodes.length == 0){
3965 var n = this.nodes[index];
3966 for(var i = 0, len = records.length; i < len; i++){
3967 var d = this.prepareData(records[i].data, i, records[i]);
3969 this.tpl.insertBefore(n, d);
3972 this.tpl.append(this.el, d);
3975 this.updateIndexes(index);
3978 onRemove : function(ds, record, index){
3979 // Roo.log('onRemove');
3980 this.clearSelections();
3981 var el = this.dataName ?
3982 this.el.child('.roo-tpl-' + this.dataName) :
3985 el.dom.removeChild(this.nodes[index]);
3986 this.updateIndexes(index);
3990 * Refresh an individual node.
3991 * @param {Number} index
3993 refreshNode : function(index){
3994 this.onUpdate(this.store, this.store.getAt(index));
3997 updateIndexes : function(startIndex, endIndex){
3998 var ns = this.nodes;
3999 startIndex = startIndex || 0;
4000 endIndex = endIndex || ns.length - 1;
4001 for(var i = startIndex; i <= endIndex; i++){
4002 ns[i].nodeIndex = i;
4007 * Changes the data store this view uses and refresh the view.
4008 * @param {Store} store
4010 setStore : function(store, initial){
4011 if(!initial && this.store){
4012 this.store.un("datachanged", this.refresh);
4013 this.store.un("add", this.onAdd);
4014 this.store.un("remove", this.onRemove);
4015 this.store.un("update", this.onUpdate);
4016 this.store.un("clear", this.refresh);
4017 this.store.un("beforeload", this.onBeforeLoad);
4018 this.store.un("load", this.onLoad);
4019 this.store.un("loadexception", this.onLoad);
4023 store.on("datachanged", this.refresh, this);
4024 store.on("add", this.onAdd, this);
4025 store.on("remove", this.onRemove, this);
4026 store.on("update", this.onUpdate, this);
4027 store.on("clear", this.refresh, this);
4028 store.on("beforeload", this.onBeforeLoad, this);
4029 store.on("load", this.onLoad, this);
4030 store.on("loadexception", this.onLoad, this);
4038 * onbeforeLoad - masks the loading area.
4041 onBeforeLoad : function(store,opts)
4043 //Roo.log('onBeforeLoad');
4047 this.el.mask(this.mask ? this.mask : "Loading" );
4049 onLoad : function ()
4056 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4057 * @param {HTMLElement} node
4058 * @return {HTMLElement} The template node
4060 findItemFromChild : function(node){
4061 var el = this.dataName ?
4062 this.el.child('.roo-tpl-' + this.dataName,true) :
4065 if(!node || node.parentNode == el){
4068 var p = node.parentNode;
4069 while(p && p != el){
4070 if(p.parentNode == el){
4079 onClick : function(e){
4080 var item = this.findItemFromChild(e.getTarget());
4082 var index = this.indexOf(item);
4083 if(this.onItemClick(item, index, e) !== false){
4084 this.fireEvent("click", this, index, item, e);
4087 this.clearSelections();
4092 onContextMenu : function(e){
4093 var item = this.findItemFromChild(e.getTarget());
4095 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4100 onDblClick : function(e){
4101 var item = this.findItemFromChild(e.getTarget());
4103 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4107 onItemClick : function(item, index, e)
4109 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4112 if (this.toggleSelect) {
4113 var m = this.isSelected(item) ? 'unselect' : 'select';
4116 _t[m](item, true, false);
4119 if(this.multiSelect || this.singleSelect){
4120 if(this.multiSelect && e.shiftKey && this.lastSelection){
4121 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4123 this.select(item, this.multiSelect && e.ctrlKey);
4124 this.lastSelection = item;
4136 * Get the number of selected nodes.
4139 getSelectionCount : function(){
4140 return this.selections.length;
4144 * Get the currently selected nodes.
4145 * @return {Array} An array of HTMLElements
4147 getSelectedNodes : function(){
4148 return this.selections;
4152 * Get the indexes of the selected nodes.
4155 getSelectedIndexes : function(){
4156 var indexes = [], s = this.selections;
4157 for(var i = 0, len = s.length; i < len; i++){
4158 indexes.push(s[i].nodeIndex);
4164 * Clear all selections
4165 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4167 clearSelections : function(suppressEvent){
4168 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4169 this.cmp.elements = this.selections;
4170 this.cmp.removeClass(this.selectedClass);
4171 this.selections = [];
4173 this.fireEvent("selectionchange", this, this.selections);
4179 * Returns true if the passed node is selected
4180 * @param {HTMLElement/Number} node The node or node index
4183 isSelected : function(node){
4184 var s = this.selections;
4188 node = this.getNode(node);
4189 return s.indexOf(node) !== -1;
4194 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4195 * @param {Boolean} keepExisting (optional) true to keep existing selections
4196 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4198 select : function(nodeInfo, keepExisting, suppressEvent){
4199 if(nodeInfo instanceof Array){
4201 this.clearSelections(true);
4203 for(var i = 0, len = nodeInfo.length; i < len; i++){
4204 this.select(nodeInfo[i], true, true);
4208 var node = this.getNode(nodeInfo);
4209 if(!node || this.isSelected(node)){
4210 return; // already selected.
4213 this.clearSelections(true);
4216 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4217 Roo.fly(node).addClass(this.selectedClass);
4218 this.selections.push(node);
4220 this.fireEvent("selectionchange", this, this.selections);
4228 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4229 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4230 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4232 unselect : function(nodeInfo, keepExisting, suppressEvent)
4234 if(nodeInfo instanceof Array){
4235 Roo.each(this.selections, function(s) {
4236 this.unselect(s, nodeInfo);
4240 var node = this.getNode(nodeInfo);
4241 if(!node || !this.isSelected(node)){
4242 //Roo.log("not selected");
4243 return; // not selected.
4247 Roo.each(this.selections, function(s) {
4249 Roo.fly(node).removeClass(this.selectedClass);
4256 this.selections= ns;
4257 this.fireEvent("selectionchange", this, this.selections);
4261 * Gets a template node.
4262 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4263 * @return {HTMLElement} The node or null if it wasn't found
4265 getNode : function(nodeInfo){
4266 if(typeof nodeInfo == "string"){
4267 return document.getElementById(nodeInfo);
4268 }else if(typeof nodeInfo == "number"){
4269 return this.nodes[nodeInfo];
4275 * Gets a range template nodes.
4276 * @param {Number} startIndex
4277 * @param {Number} endIndex
4278 * @return {Array} An array of nodes
4280 getNodes : function(start, end){
4281 var ns = this.nodes;
4283 end = typeof end == "undefined" ? ns.length - 1 : end;
4286 for(var i = start; i <= end; i++){
4290 for(var i = start; i >= end; i--){
4298 * Finds the index of the passed node
4299 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4300 * @return {Number} The index of the node or -1
4302 indexOf : function(node){
4303 node = this.getNode(node);
4304 if(typeof node.nodeIndex == "number"){
4305 return node.nodeIndex;
4307 var ns = this.nodes;
4308 for(var i = 0, len = ns.length; i < len; i++){
4318 * Ext JS Library 1.1.1
4319 * Copyright(c) 2006-2007, Ext JS, LLC.
4321 * Originally Released Under LGPL - original licence link has changed is not relivant.
4324 * <script type="text/javascript">
4328 * @class Roo.JsonView
4330 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4332 var view = new Roo.JsonView({
4333 container: "my-element",
4334 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4339 // listen for node click?
4340 view.on("click", function(vw, index, node, e){
4341 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4344 // direct load of JSON data
4345 view.load("foobar.php");
4347 // Example from my blog list
4348 var tpl = new Roo.Template(
4349 '<div class="entry">' +
4350 '<a class="entry-title" href="{link}">{title}</a>' +
4351 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4352 "</div><hr />"
4355 var moreView = new Roo.JsonView({
4356 container : "entry-list",
4360 moreView.on("beforerender", this.sortEntries, this);
4362 url: "/blog/get-posts.php",
4363 params: "allposts=true",
4364 text: "Loading Blog Entries..."
4368 * Note: old code is supported with arguments : (container, template, config)
4372 * Create a new JsonView
4374 * @param {Object} config The config object
4377 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4380 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4382 var um = this.el.getUpdateManager();
4383 um.setRenderer(this);
4384 um.on("update", this.onLoad, this);
4385 um.on("failure", this.onLoadException, this);
4388 * @event beforerender
4389 * Fires before rendering of the downloaded JSON data.
4390 * @param {Roo.JsonView} this
4391 * @param {Object} data The JSON data loaded
4395 * Fires when data is loaded.
4396 * @param {Roo.JsonView} this
4397 * @param {Object} data The JSON data loaded
4398 * @param {Object} response The raw Connect response object
4401 * @event loadexception
4402 * Fires when loading fails.
4403 * @param {Roo.JsonView} this
4404 * @param {Object} response The raw Connect response object
4407 'beforerender' : true,
4409 'loadexception' : true
4412 Roo.extend(Roo.JsonView, Roo.View, {
4414 * @type {String} The root property in the loaded JSON object that contains the data
4419 * Refreshes the view.
4421 refresh : function(){
4422 this.clearSelections();
4425 var o = this.jsonData;
4426 if(o && o.length > 0){
4427 for(var i = 0, len = o.length; i < len; i++){
4428 var data = this.prepareData(o[i], i, o);
4429 html[html.length] = this.tpl.apply(data);
4432 html.push(this.emptyText);
4434 this.el.update(html.join(""));
4435 this.nodes = this.el.dom.childNodes;
4436 this.updateIndexes(0);
4440 * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4441 * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4444 url: "your-url.php",
4445 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4446 callback: yourFunction,
4447 scope: yourObject, //(optional scope)
4455 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4456 * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4457 * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
4458 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4459 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4462 var um = this.el.getUpdateManager();
4463 um.update.apply(um, arguments);
4466 // note - render is a standard framework call...
4467 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4468 render : function(el, response){
4470 this.clearSelections();
4474 if (response != '') {
4475 o = Roo.util.JSON.decode(response.responseText);
4478 o = o[this.jsonRoot];
4484 * The current JSON data or null
4487 this.beforeRender();
4492 * Get the number of records in the current JSON dataset
4495 getCount : function(){
4496 return this.jsonData ? this.jsonData.length : 0;
4500 * Returns the JSON object for the specified node(s)
4501 * @param {HTMLElement/Array} node The node or an array of nodes
4502 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4503 * you get the JSON object for the node
4505 getNodeData : function(node){
4506 if(node instanceof Array){
4508 for(var i = 0, len = node.length; i < len; i++){
4509 data.push(this.getNodeData(node[i]));
4513 return this.jsonData[this.indexOf(node)] || null;
4516 beforeRender : function(){
4517 this.snapshot = this.jsonData;
4519 this.sort.apply(this, this.sortInfo);
4521 this.fireEvent("beforerender", this, this.jsonData);
4524 onLoad : function(el, o){
4525 this.fireEvent("load", this, this.jsonData, o);
4528 onLoadException : function(el, o){
4529 this.fireEvent("loadexception", this, o);
4533 * Filter the data by a specific property.
4534 * @param {String} property A property on your JSON objects
4535 * @param {String/RegExp} value Either string that the property values
4536 * should start with, or a RegExp to test against the property
4538 filter : function(property, value){
4541 var ss = this.snapshot;
4542 if(typeof value == "string"){
4543 var vlen = value.length;
4548 value = value.toLowerCase();
4549 for(var i = 0, len = ss.length; i < len; i++){
4551 if(o[property].substr(0, vlen).toLowerCase() == value){
4555 } else if(value.exec){ // regex?
4556 for(var i = 0, len = ss.length; i < len; i++){
4558 if(value.test(o[property])){
4565 this.jsonData = data;
4571 * Filter by a function. The passed function will be called with each
4572 * object in the current dataset. If the function returns true the value is kept,
4573 * otherwise it is filtered.
4574 * @param {Function} fn
4575 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4577 filterBy : function(fn, scope){
4580 var ss = this.snapshot;
4581 for(var i = 0, len = ss.length; i < len; i++){
4583 if(fn.call(scope || this, o)){
4587 this.jsonData = data;
4593 * Clears the current filter.
4595 clearFilter : function(){
4596 if(this.snapshot && this.jsonData != this.snapshot){
4597 this.jsonData = this.snapshot;
4604 * Sorts the data for this view and refreshes it.
4605 * @param {String} property A property on your JSON objects to sort on
4606 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4607 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4609 sort : function(property, dir, sortType){
4610 this.sortInfo = Array.prototype.slice.call(arguments, 0);
4613 var dsc = dir && dir.toLowerCase() == "desc";
4614 var f = function(o1, o2){
4615 var v1 = sortType ? sortType(o1[p]) : o1[p];
4616 var v2 = sortType ? sortType(o2[p]) : o2[p];
4619 return dsc ? +1 : -1;
4621 return dsc ? -1 : +1;
4626 this.jsonData.sort(f);
4628 if(this.jsonData != this.snapshot){
4629 this.snapshot.sort(f);
4635 * Ext JS Library 1.1.1
4636 * Copyright(c) 2006-2007, Ext JS, LLC.
4638 * Originally Released Under LGPL - original licence link has changed is not relivant.
4641 * <script type="text/javascript">
4646 * @class Roo.ColorPalette
4647 * @extends Roo.Component
4648 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
4649 * Here's an example of typical usage:
4651 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
4652 cp.render('my-div');
4654 cp.on('select', function(palette, selColor){
4655 // do something with selColor
4659 * Create a new ColorPalette
4660 * @param {Object} config The config object
4662 Roo.ColorPalette = function(config){
4663 Roo.ColorPalette.superclass.constructor.call(this, config);
4667 * Fires when a color is selected
4668 * @param {ColorPalette} this
4669 * @param {String} color The 6-digit color hex code (without the # symbol)
4675 this.on("select", this.handler, this.scope, true);
4678 Roo.extend(Roo.ColorPalette, Roo.Component, {
4680 * @cfg {String} itemCls
4681 * The CSS class to apply to the containing element (defaults to "x-color-palette")
4683 itemCls : "x-color-palette",
4685 * @cfg {String} value
4686 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
4687 * the hex codes are case-sensitive.
4692 ctype: "Roo.ColorPalette",
4695 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4697 allowReselect : false,
4700 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
4701 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
4702 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4703 * of colors with the width setting until the box is symmetrical.</p>
4704 * <p>You can override individual colors if needed:</p>
4706 var cp = new Roo.ColorPalette();
4707 cp.colors[0] = "FF0000"; // change the first box to red
4710 Or you can provide a custom array of your own for complete control:
4712 var cp = new Roo.ColorPalette();
4713 cp.colors = ["000000", "993300", "333300"];
4718 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4719 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4720 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4721 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4722 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4726 onRender : function(container, position){
4727 var t = new Roo.MasterTemplate(
4728 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
4730 var c = this.colors;
4731 for(var i = 0, len = c.length; i < len; i++){
4734 var el = document.createElement("div");
4735 el.className = this.itemCls;
4737 container.dom.insertBefore(el, position);
4738 this.el = Roo.get(el);
4739 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
4740 if(this.clickEvent != 'click'){
4741 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
4746 afterRender : function(){
4747 Roo.ColorPalette.superclass.afterRender.call(this);
4756 handleClick : function(e, t){
4759 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4760 this.select(c.toUpperCase());
4765 * Selects the specified color in the palette (fires the select event)
4766 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4768 select : function(color){
4769 color = color.replace("#", "");
4770 if(color != this.value || this.allowReselect){
4773 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4775 el.child("a.color-"+color).addClass("x-color-palette-sel");
4777 this.fireEvent("select", this, color);
4782 * Ext JS Library 1.1.1
4783 * Copyright(c) 2006-2007, Ext JS, LLC.
4785 * Originally Released Under LGPL - original licence link has changed is not relivant.
4788 * <script type="text/javascript">
4792 * @class Roo.DatePicker
4793 * @extends Roo.Component
4794 * Simple date picker class.
4796 * Create a new DatePicker
4797 * @param {Object} config The config object
4799 Roo.DatePicker = function(config){
4800 Roo.DatePicker.superclass.constructor.call(this, config);
4802 this.value = config && config.value ?
4803 config.value.clearTime() : new Date().clearTime();
4808 * Fires when a date is selected
4809 * @param {DatePicker} this
4810 * @param {Date} date The selected date
4814 * @event monthchange
4815 * Fires when the displayed month changes
4816 * @param {DatePicker} this
4817 * @param {Date} date The selected month
4823 this.on("select", this.handler, this.scope || this);
4825 // build the disabledDatesRE
4826 if(!this.disabledDatesRE && this.disabledDates){
4827 var dd = this.disabledDates;
4829 for(var i = 0; i < dd.length; i++){
4831 if(i != dd.length-1) {
4835 this.disabledDatesRE = new RegExp(re + ")");
4839 Roo.extend(Roo.DatePicker, Roo.Component, {
4841 * @cfg {String} todayText
4842 * The text to display on the button that selects the current date (defaults to "Today")
4844 todayText : "Today",
4846 * @cfg {String} okText
4847 * The text to display on the ok button
4849 okText : " OK ", //   to give the user extra clicking room
4851 * @cfg {String} cancelText
4852 * The text to display on the cancel button
4854 cancelText : "Cancel",
4856 * @cfg {String} todayTip
4857 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4859 todayTip : "{0} (Spacebar)",
4861 * @cfg {Date} minDate
4862 * Minimum allowable date (JavaScript date object, defaults to null)
4866 * @cfg {Date} maxDate
4867 * Maximum allowable date (JavaScript date object, defaults to null)
4871 * @cfg {String} minText
4872 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4874 minText : "This date is before the minimum date",
4876 * @cfg {String} maxText
4877 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4879 maxText : "This date is after the maximum date",
4881 * @cfg {String} format
4882 * The default date format string which can be overriden for localization support. The format must be
4883 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4887 * @cfg {Array} disabledDays
4888 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4890 disabledDays : null,
4892 * @cfg {String} disabledDaysText
4893 * The tooltip to display when the date falls on a disabled day (defaults to "")
4895 disabledDaysText : "",
4897 * @cfg {RegExp} disabledDatesRE
4898 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4900 disabledDatesRE : null,
4902 * @cfg {String} disabledDatesText
4903 * The tooltip text to display when the date falls on a disabled date (defaults to "")
4905 disabledDatesText : "",
4907 * @cfg {Boolean} constrainToViewport
4908 * True to constrain the date picker to the viewport (defaults to true)
4910 constrainToViewport : true,
4912 * @cfg {Array} monthNames
4913 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4915 monthNames : Date.monthNames,
4917 * @cfg {Array} dayNames
4918 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4920 dayNames : Date.dayNames,
4922 * @cfg {String} nextText
4923 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4925 nextText: 'Next Month (Control+Right)',
4927 * @cfg {String} prevText
4928 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4930 prevText: 'Previous Month (Control+Left)',
4932 * @cfg {String} monthYearText
4933 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4935 monthYearText: 'Choose a month (Control+Up/Down to move years)',
4937 * @cfg {Number} startDay
4938 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4942 * @cfg {Bool} showClear
4943 * Show a clear button (usefull for date form elements that can be blank.)
4949 * Sets the value of the date field
4950 * @param {Date} value The date to set
4952 setValue : function(value){
4953 var old = this.value;
4955 if (typeof(value) == 'string') {
4957 value = Date.parseDate(value, this.format);
4963 this.value = value.clearTime(true);
4965 this.update(this.value);
4970 * Gets the current selected value of the date field
4971 * @return {Date} The selected date
4973 getValue : function(){
4980 this.update(this.activeDate);
4985 onRender : function(container, position){
4988 '<table cellspacing="0">',
4989 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'"> </a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'"> </a></td></tr>',
4990 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4991 var dn = this.dayNames;
4992 for(var i = 0; i < 7; i++){
4993 var d = this.startDay+i;
4997 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4999 m[m.length] = "</tr></thead><tbody><tr>";
5000 for(var i = 0; i < 42; i++) {
5001 if(i % 7 == 0 && i != 0){
5002 m[m.length] = "</tr><tr>";
5004 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5006 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5007 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5009 var el = document.createElement("div");
5010 el.className = "x-date-picker";
5011 el.innerHTML = m.join("");
5013 container.dom.insertBefore(el, position);
5015 this.el = Roo.get(el);
5016 this.eventEl = Roo.get(el.firstChild);
5018 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5019 handler: this.showPrevMonth,
5021 preventDefault:true,
5025 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5026 handler: this.showNextMonth,
5028 preventDefault:true,
5032 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5034 this.monthPicker = this.el.down('div.x-date-mp');
5035 this.monthPicker.enableDisplayMode('block');
5037 var kn = new Roo.KeyNav(this.eventEl, {
5038 "left" : function(e){
5040 this.showPrevMonth() :
5041 this.update(this.activeDate.add("d", -1));
5044 "right" : function(e){
5046 this.showNextMonth() :
5047 this.update(this.activeDate.add("d", 1));
5052 this.showNextYear() :
5053 this.update(this.activeDate.add("d", -7));
5056 "down" : function(e){
5058 this.showPrevYear() :
5059 this.update(this.activeDate.add("d", 7));
5062 "pageUp" : function(e){
5063 this.showNextMonth();
5066 "pageDown" : function(e){
5067 this.showPrevMonth();
5070 "enter" : function(e){
5071 e.stopPropagation();
5078 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5080 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5082 this.el.unselectable();
5084 this.cells = this.el.select("table.x-date-inner tbody td");
5085 this.textNodes = this.el.query("table.x-date-inner tbody span");
5087 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5089 tooltip: this.monthYearText
5092 this.mbtn.on('click', this.showMonthPicker, this);
5093 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5096 var today = (new Date()).dateFormat(this.format);
5098 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5099 if (this.showClear) {
5100 baseTb.add( new Roo.Toolbar.Fill());
5103 text: String.format(this.todayText, today),
5104 tooltip: String.format(this.todayTip, today),
5105 handler: this.selectToday,
5109 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5112 if (this.showClear) {
5114 baseTb.add( new Roo.Toolbar.Fill());
5117 cls: 'x-btn-icon x-btn-clear',
5118 handler: function() {
5120 this.fireEvent("select", this, '');
5130 this.update(this.value);
5133 createMonthPicker : function(){
5134 if(!this.monthPicker.dom.firstChild){
5135 var buf = ['<table border="0" cellspacing="0">'];
5136 for(var i = 0; i < 6; i++){
5138 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5139 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5141 '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5142 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5146 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5148 '</button><button type="button" class="x-date-mp-cancel">',
5150 '</button></td></tr>',
5153 this.monthPicker.update(buf.join(''));
5154 this.monthPicker.on('click', this.onMonthClick, this);
5155 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5157 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5158 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5160 this.mpMonths.each(function(m, a, i){
5163 m.dom.xmonth = 5 + Math.round(i * .5);
5165 m.dom.xmonth = Math.round((i-1) * .5);
5171 showMonthPicker : function(){
5172 this.createMonthPicker();
5173 var size = this.el.getSize();
5174 this.monthPicker.setSize(size);
5175 this.monthPicker.child('table').setSize(size);
5177 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5178 this.updateMPMonth(this.mpSelMonth);
5179 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5180 this.updateMPYear(this.mpSelYear);
5182 this.monthPicker.slideIn('t', {duration:.2});
5185 updateMPYear : function(y){
5187 var ys = this.mpYears.elements;
5188 for(var i = 1; i <= 10; i++){
5189 var td = ys[i-1], y2;
5191 y2 = y + Math.round(i * .5);
5192 td.firstChild.innerHTML = y2;
5195 y2 = y - (5-Math.round(i * .5));
5196 td.firstChild.innerHTML = y2;
5199 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5203 updateMPMonth : function(sm){
5204 this.mpMonths.each(function(m, a, i){
5205 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5209 selectMPMonth: function(m){
5213 onMonthClick : function(e, t){
5215 var el = new Roo.Element(t), pn;
5216 if(el.is('button.x-date-mp-cancel')){
5217 this.hideMonthPicker();
5219 else if(el.is('button.x-date-mp-ok')){
5220 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5221 this.hideMonthPicker();
5223 else if(pn = el.up('td.x-date-mp-month', 2)){
5224 this.mpMonths.removeClass('x-date-mp-sel');
5225 pn.addClass('x-date-mp-sel');
5226 this.mpSelMonth = pn.dom.xmonth;
5228 else if(pn = el.up('td.x-date-mp-year', 2)){
5229 this.mpYears.removeClass('x-date-mp-sel');
5230 pn.addClass('x-date-mp-sel');
5231 this.mpSelYear = pn.dom.xyear;
5233 else if(el.is('a.x-date-mp-prev')){
5234 this.updateMPYear(this.mpyear-10);
5236 else if(el.is('a.x-date-mp-next')){
5237 this.updateMPYear(this.mpyear+10);
5241 onMonthDblClick : function(e, t){
5243 var el = new Roo.Element(t), pn;
5244 if(pn = el.up('td.x-date-mp-month', 2)){
5245 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5246 this.hideMonthPicker();
5248 else if(pn = el.up('td.x-date-mp-year', 2)){
5249 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5250 this.hideMonthPicker();
5254 hideMonthPicker : function(disableAnim){
5255 if(this.monthPicker){
5256 if(disableAnim === true){
5257 this.monthPicker.hide();
5259 this.monthPicker.slideOut('t', {duration:.2});
5265 showPrevMonth : function(e){
5266 this.update(this.activeDate.add("mo", -1));
5270 showNextMonth : function(e){
5271 this.update(this.activeDate.add("mo", 1));
5275 showPrevYear : function(){
5276 this.update(this.activeDate.add("y", -1));
5280 showNextYear : function(){
5281 this.update(this.activeDate.add("y", 1));
5285 handleMouseWheel : function(e){
5286 var delta = e.getWheelDelta();
5288 this.showPrevMonth();
5290 } else if(delta < 0){
5291 this.showNextMonth();
5297 handleDateClick : function(e, t){
5299 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5300 this.setValue(new Date(t.dateValue));
5301 this.fireEvent("select", this, this.value);
5306 selectToday : function(){
5307 this.setValue(new Date().clearTime());
5308 this.fireEvent("select", this, this.value);
5312 update : function(date)
5314 var vd = this.activeDate;
5315 this.activeDate = date;
5317 var t = date.getTime();
5318 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5319 this.cells.removeClass("x-date-selected");
5320 this.cells.each(function(c){
5321 if(c.dom.firstChild.dateValue == t){
5322 c.addClass("x-date-selected");
5323 setTimeout(function(){
5324 try{c.dom.firstChild.focus();}catch(e){}
5333 var days = date.getDaysInMonth();
5334 var firstOfMonth = date.getFirstDateOfMonth();
5335 var startingPos = firstOfMonth.getDay()-this.startDay;
5337 if(startingPos <= this.startDay){
5341 var pm = date.add("mo", -1);
5342 var prevStart = pm.getDaysInMonth()-startingPos;
5344 var cells = this.cells.elements;
5345 var textEls = this.textNodes;
5346 days += startingPos;
5348 // convert everything to numbers so it's fast
5350 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5351 var today = new Date().clearTime().getTime();
5352 var sel = date.clearTime().getTime();
5353 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5354 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5355 var ddMatch = this.disabledDatesRE;
5356 var ddText = this.disabledDatesText;
5357 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5358 var ddaysText = this.disabledDaysText;
5359 var format = this.format;
5361 var setCellClass = function(cal, cell){
5363 var t = d.getTime();
5364 cell.firstChild.dateValue = t;
5366 cell.className += " x-date-today";
5367 cell.title = cal.todayText;
5370 cell.className += " x-date-selected";
5371 setTimeout(function(){
5372 try{cell.firstChild.focus();}catch(e){}
5377 cell.className = " x-date-disabled";
5378 cell.title = cal.minText;
5382 cell.className = " x-date-disabled";
5383 cell.title = cal.maxText;
5387 if(ddays.indexOf(d.getDay()) != -1){
5388 cell.title = ddaysText;
5389 cell.className = " x-date-disabled";
5392 if(ddMatch && format){
5393 var fvalue = d.dateFormat(format);
5394 if(ddMatch.test(fvalue)){
5395 cell.title = ddText.replace("%0", fvalue);
5396 cell.className = " x-date-disabled";
5402 for(; i < startingPos; i++) {
5403 textEls[i].innerHTML = (++prevStart);
5404 d.setDate(d.getDate()+1);
5405 cells[i].className = "x-date-prevday";
5406 setCellClass(this, cells[i]);
5408 for(; i < days; i++){
5409 intDay = i - startingPos + 1;
5410 textEls[i].innerHTML = (intDay);
5411 d.setDate(d.getDate()+1);
5412 cells[i].className = "x-date-active";
5413 setCellClass(this, cells[i]);
5416 for(; i < 42; i++) {
5417 textEls[i].innerHTML = (++extraDays);
5418 d.setDate(d.getDate()+1);
5419 cells[i].className = "x-date-nextday";
5420 setCellClass(this, cells[i]);
5423 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5424 this.fireEvent('monthchange', this, date);
5426 if(!this.internalRender){
5427 var main = this.el.dom.firstChild;
5428 var w = main.offsetWidth;
5429 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5430 Roo.fly(main).setWidth(w);
5431 this.internalRender = true;
5432 // opera does not respect the auto grow header center column
5433 // then, after it gets a width opera refuses to recalculate
5434 // without a second pass
5435 if(Roo.isOpera && !this.secondPass){
5436 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5437 this.secondPass = true;
5438 this.update.defer(10, this, [date]);
5446 * Ext JS Library 1.1.1
5447 * Copyright(c) 2006-2007, Ext JS, LLC.
5449 * Originally Released Under LGPL - original licence link has changed is not relivant.
5452 * <script type="text/javascript">
5455 * @class Roo.TabPanel
5456 * @extends Roo.util.Observable
5457 * A lightweight tab container.
5461 // basic tabs 1, built from existing content
5462 var tabs = new Roo.TabPanel("tabs1");
5463 tabs.addTab("script", "View Script");
5464 tabs.addTab("markup", "View Markup");
5465 tabs.activate("script");
5467 // more advanced tabs, built from javascript
5468 var jtabs = new Roo.TabPanel("jtabs");
5469 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5471 // set up the UpdateManager
5472 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5473 var updater = tab2.getUpdateManager();
5474 updater.setDefaultUrl("ajax1.htm");
5475 tab2.on('activate', updater.refresh, updater, true);
5477 // Use setUrl for Ajax loading
5478 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5479 tab3.setUrl("ajax2.htm", null, true);
5482 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5485 jtabs.activate("jtabs-1");
5488 * Create a new TabPanel.
5489 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5490 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5492 Roo.TabPanel = function(container, config){
5494 * The container element for this TabPanel.
5497 this.el = Roo.get(container, true);
5499 if(typeof config == "boolean"){
5500 this.tabPosition = config ? "bottom" : "top";
5502 Roo.apply(this, config);
5505 if(this.tabPosition == "bottom"){
5506 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5507 this.el.addClass("x-tabs-bottom");
5509 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5510 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5511 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5513 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5515 if(this.tabPosition != "bottom"){
5516 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5519 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5520 this.el.addClass("x-tabs-top");
5524 this.bodyEl.setStyle("position", "relative");
5527 this.activateDelegate = this.activate.createDelegate(this);
5532 * Fires when the active tab changes
5533 * @param {Roo.TabPanel} this
5534 * @param {Roo.TabPanelItem} activePanel The new active tab
5538 * @event beforetabchange
5539 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5540 * @param {Roo.TabPanel} this
5541 * @param {Object} e Set cancel to true on this object to cancel the tab change
5542 * @param {Roo.TabPanelItem} tab The tab being changed to
5544 "beforetabchange" : true
5547 Roo.EventManager.onWindowResize(this.onResize, this);
5548 this.cpad = this.el.getPadding("lr");
5549 this.hiddenCount = 0;
5552 // toolbar on the tabbar support...
5554 var tcfg = this.toolbar;
5555 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5556 this.toolbar = new Roo.Toolbar(tcfg);
5558 var tbl = tcfg.container.child('table', true);
5559 tbl.setAttribute('width', '100%');
5566 Roo.TabPanel.superclass.constructor.call(this);
5569 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5571 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5573 tabPosition : "top",
5575 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5577 currentTabWidth : 0,
5579 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5583 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5587 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5589 preferredTabWidth : 175,
5591 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5595 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5597 monitorResize : true,
5599 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
5604 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5605 * @param {String} id The id of the div to use <b>or create</b>
5606 * @param {String} text The text for the tab
5607 * @param {String} content (optional) Content to put in the TabPanelItem body
5608 * @param {Boolean} closable (optional) True to create a close icon on the tab
5609 * @return {Roo.TabPanelItem} The created TabPanelItem
5611 addTab : function(id, text, content, closable){
5612 var item = new Roo.TabPanelItem(this, id, text, closable);
5613 this.addTabItem(item);
5615 item.setContent(content);
5621 * Returns the {@link Roo.TabPanelItem} with the specified id/index
5622 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5623 * @return {Roo.TabPanelItem}
5625 getTab : function(id){
5626 return this.items[id];
5630 * Hides the {@link Roo.TabPanelItem} with the specified id/index
5631 * @param {String/Number} id The id or index of the TabPanelItem to hide.
5633 hideTab : function(id){
5634 var t = this.items[id];
5638 this.autoSizeTabs();
5643 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5644 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5646 unhideTab : function(id){
5647 var t = this.items[id];
5651 this.autoSizeTabs();
5656 * Adds an existing {@link Roo.TabPanelItem}.
5657 * @param {Roo.TabPanelItem} item The TabPanelItem to add
5659 addTabItem : function(item){
5660 this.items[item.id] = item;
5661 this.items.push(item);
5662 if(this.resizeTabs){
5663 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5664 this.autoSizeTabs();
5671 * Removes a {@link Roo.TabPanelItem}.
5672 * @param {String/Number} id The id or index of the TabPanelItem to remove.
5674 removeTab : function(id){
5675 var items = this.items;
5676 var tab = items[id];
5677 if(!tab) { return; }
5678 var index = items.indexOf(tab);
5679 if(this.active == tab && items.length > 1){
5680 var newTab = this.getNextAvailable(index);
5685 this.stripEl.dom.removeChild(tab.pnode.dom);
5686 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5687 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5689 items.splice(index, 1);
5690 delete this.items[tab.id];
5691 tab.fireEvent("close", tab);
5692 tab.purgeListeners();
5693 this.autoSizeTabs();
5696 getNextAvailable : function(start){
5697 var items = this.items;
5699 // look for a next tab that will slide over to
5700 // replace the one being removed
5701 while(index < items.length){
5702 var item = items[++index];
5703 if(item && !item.isHidden()){
5707 // if one isn't found select the previous tab (on the left)
5710 var item = items[--index];
5711 if(item && !item.isHidden()){
5719 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5720 * @param {String/Number} id The id or index of the TabPanelItem to disable.
5722 disableTab : function(id){
5723 var tab = this.items[id];
5724 if(tab && this.active != tab){
5730 * Enables a {@link Roo.TabPanelItem} that is disabled.
5731 * @param {String/Number} id The id or index of the TabPanelItem to enable.
5733 enableTab : function(id){
5734 var tab = this.items[id];
5739 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5740 * @param {String/Number} id The id or index of the TabPanelItem to activate.
5741 * @return {Roo.TabPanelItem} The TabPanelItem.
5743 activate : function(id){
5744 var tab = this.items[id];
5748 if(tab == this.active || tab.disabled){
5752 this.fireEvent("beforetabchange", this, e, tab);
5753 if(e.cancel !== true && !tab.disabled){
5757 this.active = this.items[id];
5759 this.fireEvent("tabchange", this, this.active);
5765 * Gets the active {@link Roo.TabPanelItem}.
5766 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5768 getActiveTab : function(){
5773 * Updates the tab body element to fit the height of the container element
5774 * for overflow scrolling
5775 * @param {Number} targetHeight (optional) Override the starting height from the elements height
5777 syncHeight : function(targetHeight){
5778 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5779 var bm = this.bodyEl.getMargins();
5780 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5781 this.bodyEl.setHeight(newHeight);
5785 onResize : function(){
5786 if(this.monitorResize){
5787 this.autoSizeTabs();
5792 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5794 beginUpdate : function(){
5795 this.updating = true;
5799 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5801 endUpdate : function(){
5802 this.updating = false;
5803 this.autoSizeTabs();
5807 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5809 autoSizeTabs : function(){
5810 var count = this.items.length;
5811 var vcount = count - this.hiddenCount;
5812 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5815 var w = Math.max(this.el.getWidth() - this.cpad, 10);
5816 var availWidth = Math.floor(w / vcount);
5817 var b = this.stripBody;
5818 if(b.getWidth() > w){
5819 var tabs = this.items;
5820 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5821 if(availWidth < this.minTabWidth){
5822 /*if(!this.sleft){ // incomplete scrolling code
5823 this.createScrollButtons();
5826 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5829 if(this.currentTabWidth < this.preferredTabWidth){
5830 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5836 * Returns the number of tabs in this TabPanel.
5839 getCount : function(){
5840 return this.items.length;
5844 * Resizes all the tabs to the passed width
5845 * @param {Number} The new width
5847 setTabWidth : function(width){
5848 this.currentTabWidth = width;
5849 for(var i = 0, len = this.items.length; i < len; i++) {
5850 if(!this.items[i].isHidden()) {
5851 this.items[i].setWidth(width);
5857 * Destroys this TabPanel
5858 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5860 destroy : function(removeEl){
5861 Roo.EventManager.removeResizeListener(this.onResize, this);
5862 for(var i = 0, len = this.items.length; i < len; i++){
5863 this.items[i].purgeListeners();
5865 if(removeEl === true){
5873 * @class Roo.TabPanelItem
5874 * @extends Roo.util.Observable
5875 * Represents an individual item (tab plus body) in a TabPanel.
5876 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5877 * @param {String} id The id of this TabPanelItem
5878 * @param {String} text The text for the tab of this TabPanelItem
5879 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5881 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5883 * The {@link Roo.TabPanel} this TabPanelItem belongs to
5884 * @type Roo.TabPanel
5886 this.tabPanel = tabPanel;
5888 * The id for this TabPanelItem
5893 this.disabled = false;
5897 this.loaded = false;
5898 this.closable = closable;
5901 * The body element for this TabPanelItem.
5904 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5905 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5906 this.bodyEl.setStyle("display", "block");
5907 this.bodyEl.setStyle("zoom", "1");
5910 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5912 this.el = Roo.get(els.el, true);
5913 this.inner = Roo.get(els.inner, true);
5914 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5915 this.pnode = Roo.get(els.el.parentNode, true);
5916 this.el.on("mousedown", this.onTabMouseDown, this);
5917 this.el.on("click", this.onTabClick, this);
5920 var c = Roo.get(els.close, true);
5921 c.dom.title = this.closeText;
5922 c.addClassOnOver("close-over");
5923 c.on("click", this.closeClick, this);
5929 * Fires when this tab becomes the active tab.
5930 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5931 * @param {Roo.TabPanelItem} this
5935 * @event beforeclose
5936 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5937 * @param {Roo.TabPanelItem} this
5938 * @param {Object} e Set cancel to true on this object to cancel the close.
5940 "beforeclose": true,
5943 * Fires when this tab is closed.
5944 * @param {Roo.TabPanelItem} this
5949 * Fires when this tab is no longer the active tab.
5950 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5951 * @param {Roo.TabPanelItem} this
5955 this.hidden = false;
5957 Roo.TabPanelItem.superclass.constructor.call(this);
5960 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5961 purgeListeners : function(){
5962 Roo.util.Observable.prototype.purgeListeners.call(this);
5963 this.el.removeAllListeners();
5966 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5969 this.pnode.addClass("on");
5972 this.tabPanel.stripWrap.repaint();
5974 this.fireEvent("activate", this.tabPanel, this);
5978 * Returns true if this tab is the active tab.
5981 isActive : function(){
5982 return this.tabPanel.getActiveTab() == this;
5986 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5989 this.pnode.removeClass("on");
5991 this.fireEvent("deactivate", this.tabPanel, this);
5994 hideAction : function(){
5996 this.bodyEl.setStyle("position", "absolute");
5997 this.bodyEl.setLeft("-20000px");
5998 this.bodyEl.setTop("-20000px");
6001 showAction : function(){
6002 this.bodyEl.setStyle("position", "relative");
6003 this.bodyEl.setTop("");
6004 this.bodyEl.setLeft("");
6009 * Set the tooltip for the tab.
6010 * @param {String} tooltip The tab's tooltip
6012 setTooltip : function(text){
6013 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6014 this.textEl.dom.qtip = text;
6015 this.textEl.dom.removeAttribute('title');
6017 this.textEl.dom.title = text;
6021 onTabClick : function(e){
6023 this.tabPanel.activate(this.id);
6026 onTabMouseDown : function(e){
6028 this.tabPanel.activate(this.id);
6031 getWidth : function(){
6032 return this.inner.getWidth();
6035 setWidth : function(width){
6036 var iwidth = width - this.pnode.getPadding("lr");
6037 this.inner.setWidth(iwidth);
6038 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6039 this.pnode.setWidth(width);
6043 * Show or hide the tab
6044 * @param {Boolean} hidden True to hide or false to show.
6046 setHidden : function(hidden){
6047 this.hidden = hidden;
6048 this.pnode.setStyle("display", hidden ? "none" : "");
6052 * Returns true if this tab is "hidden"
6055 isHidden : function(){
6060 * Returns the text for this tab
6063 getText : function(){
6067 autoSize : function(){
6068 //this.el.beginMeasure();
6069 this.textEl.setWidth(1);
6071 * #2804 [new] Tabs in Roojs
6072 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6074 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6075 //this.el.endMeasure();
6079 * Sets the text for the tab (Note: this also sets the tooltip text)
6080 * @param {String} text The tab's text and tooltip
6082 setText : function(text){
6084 this.textEl.update(text);
6085 this.setTooltip(text);
6086 if(!this.tabPanel.resizeTabs){
6091 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6093 activate : function(){
6094 this.tabPanel.activate(this.id);
6098 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6100 disable : function(){
6101 if(this.tabPanel.active != this){
6102 this.disabled = true;
6103 this.pnode.addClass("disabled");
6108 * Enables this TabPanelItem if it was previously disabled.
6110 enable : function(){
6111 this.disabled = false;
6112 this.pnode.removeClass("disabled");
6116 * Sets the content for this TabPanelItem.
6117 * @param {String} content The content
6118 * @param {Boolean} loadScripts true to look for and load scripts
6120 setContent : function(content, loadScripts){
6121 this.bodyEl.update(content, loadScripts);
6125 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6126 * @return {Roo.UpdateManager} The UpdateManager
6128 getUpdateManager : function(){
6129 return this.bodyEl.getUpdateManager();
6133 * Set a URL to be used to load the content for this TabPanelItem.
6134 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6135 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6136 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6137 * @return {Roo.UpdateManager} The UpdateManager
6139 setUrl : function(url, params, loadOnce){
6140 if(this.refreshDelegate){
6141 this.un('activate', this.refreshDelegate);
6143 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6144 this.on("activate", this.refreshDelegate);
6145 return this.bodyEl.getUpdateManager();
6149 _handleRefresh : function(url, params, loadOnce){
6150 if(!loadOnce || !this.loaded){
6151 var updater = this.bodyEl.getUpdateManager();
6152 updater.update(url, params, this._setLoaded.createDelegate(this));
6157 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6158 * Will fail silently if the setUrl method has not been called.
6159 * This does not activate the panel, just updates its content.
6161 refresh : function(){
6162 if(this.refreshDelegate){
6163 this.loaded = false;
6164 this.refreshDelegate();
6169 _setLoaded : function(){
6174 closeClick : function(e){
6177 this.fireEvent("beforeclose", this, o);
6178 if(o.cancel !== true){
6179 this.tabPanel.removeTab(this.id);
6183 * The text displayed in the tooltip for the close icon.
6186 closeText : "Close this tab"
6190 Roo.TabPanel.prototype.createStrip = function(container){
6191 var strip = document.createElement("div");
6192 strip.className = "x-tabs-wrap";
6193 container.appendChild(strip);
6197 Roo.TabPanel.prototype.createStripList = function(strip){
6198 // div wrapper for retard IE
6199 // returns the "tr" element.
6200 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6201 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6202 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6203 return strip.firstChild.firstChild.firstChild.firstChild;
6206 Roo.TabPanel.prototype.createBody = function(container){
6207 var body = document.createElement("div");
6208 Roo.id(body, "tab-body");
6209 Roo.fly(body).addClass("x-tabs-body");
6210 container.appendChild(body);
6214 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6215 var body = Roo.getDom(id);
6217 body = document.createElement("div");
6220 Roo.fly(body).addClass("x-tabs-item-body");
6221 bodyEl.insertBefore(body, bodyEl.firstChild);
6225 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6226 var td = document.createElement("td");
6227 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6228 //stripEl.appendChild(td);
6230 td.className = "x-tabs-closable";
6232 this.closeTpl = new Roo.Template(
6233 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6234 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6235 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6238 var el = this.closeTpl.overwrite(td, {"text": text});
6239 var close = el.getElementsByTagName("div")[0];
6240 var inner = el.getElementsByTagName("em")[0];
6241 return {"el": el, "close": close, "inner": inner};
6244 this.tabTpl = new Roo.Template(
6245 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6246 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6249 var el = this.tabTpl.overwrite(td, {"text": text});
6250 var inner = el.getElementsByTagName("em")[0];
6251 return {"el": el, "inner": inner};
6255 * Ext JS Library 1.1.1
6256 * Copyright(c) 2006-2007, Ext JS, LLC.
6258 * Originally Released Under LGPL - original licence link has changed is not relivant.
6261 * <script type="text/javascript">
6266 * @extends Roo.util.Observable
6267 * Simple Button class
6268 * @cfg {String} text The button text
6269 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6270 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6271 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6272 * @cfg {Object} scope The scope of the handler
6273 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6274 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6275 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6276 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6277 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6278 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6279 applies if enableToggle = true)
6280 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6281 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6282 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6284 * Create a new button
6285 * @param {Object} config The config object
6287 Roo.Button = function(renderTo, config)
6291 renderTo = config.renderTo || false;
6294 Roo.apply(this, config);
6298 * Fires when this button is clicked
6299 * @param {Button} this
6300 * @param {EventObject} e The click event
6305 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6306 * @param {Button} this
6307 * @param {Boolean} pressed
6312 * Fires when the mouse hovers over the button
6313 * @param {Button} this
6314 * @param {Event} e The event object
6319 * Fires when the mouse exits the button
6320 * @param {Button} this
6321 * @param {Event} e The event object
6326 * Fires when the button is rendered
6327 * @param {Button} this
6332 this.menu = Roo.menu.MenuMgr.get(this.menu);
6334 // register listeners first!! - so render can be captured..
6335 Roo.util.Observable.call(this);
6337 this.render(renderTo);
6343 Roo.extend(Roo.Button, Roo.util.Observable, {
6349 * Read-only. True if this button is hidden
6354 * Read-only. True if this button is disabled
6359 * Read-only. True if this button is pressed (only if enableToggle = true)
6365 * @cfg {Number} tabIndex
6366 * The DOM tabIndex for this button (defaults to undefined)
6368 tabIndex : undefined,
6371 * @cfg {Boolean} enableToggle
6372 * True to enable pressed/not pressed toggling (defaults to false)
6374 enableToggle: false,
6376 * @cfg {Roo.menu.Menu} menu
6377 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6381 * @cfg {String} menuAlign
6382 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6384 menuAlign : "tl-bl?",
6387 * @cfg {String} iconCls
6388 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6390 iconCls : undefined,
6392 * @cfg {String} type
6393 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6398 menuClassTarget: 'tr',
6401 * @cfg {String} clickEvent
6402 * The type of event to map to the button's event handler (defaults to 'click')
6404 clickEvent : 'click',
6407 * @cfg {Boolean} handleMouseEvents
6408 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6410 handleMouseEvents : true,
6413 * @cfg {String} tooltipType
6414 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6416 tooltipType : 'qtip',
6420 * A CSS class to apply to the button's main element.
6424 * @cfg {Roo.Template} template (Optional)
6425 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6426 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6427 * require code modifications if required elements (e.g. a button) aren't present.
6431 render : function(renderTo){
6433 if(this.hideParent){
6434 this.parentEl = Roo.get(renderTo);
6438 if(!Roo.Button.buttonTemplate){
6439 // hideous table template
6440 Roo.Button.buttonTemplate = new Roo.Template(
6441 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6442 '<td class="x-btn-left"><i> </i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i> </i></td>',
6443 "</tr></tbody></table>");
6445 this.template = Roo.Button.buttonTemplate;
6447 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6448 var btnEl = btn.child("button:first");
6449 btnEl.on('focus', this.onFocus, this);
6450 btnEl.on('blur', this.onBlur, this);
6452 btn.addClass(this.cls);
6455 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6458 btnEl.addClass(this.iconCls);
6460 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6463 if(this.tabIndex !== undefined){
6464 btnEl.dom.tabIndex = this.tabIndex;
6467 if(typeof this.tooltip == 'object'){
6468 Roo.QuickTips.tips(Roo.apply({
6472 btnEl.dom[this.tooltipType] = this.tooltip;
6476 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6480 this.el.dom.id = this.el.id = this.id;
6483 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6484 this.menu.on("show", this.onMenuShow, this);
6485 this.menu.on("hide", this.onMenuHide, this);
6487 btn.addClass("x-btn");
6488 if(Roo.isIE && !Roo.isIE7){
6489 this.autoWidth.defer(1, this);
6493 if(this.handleMouseEvents){
6494 btn.on("mouseover", this.onMouseOver, this);
6495 btn.on("mouseout", this.onMouseOut, this);
6496 btn.on("mousedown", this.onMouseDown, this);
6498 btn.on(this.clickEvent, this.onClick, this);
6499 //btn.on("mouseup", this.onMouseUp, this);
6506 Roo.ButtonToggleMgr.register(this);
6508 this.el.addClass("x-btn-pressed");
6511 var repeater = new Roo.util.ClickRepeater(btn,
6512 typeof this.repeat == "object" ? this.repeat : {}
6514 repeater.on("click", this.onClick, this);
6517 this.fireEvent('render', this);
6521 * Returns the button's underlying element
6522 * @return {Roo.Element} The element
6529 * Destroys this Button and removes any listeners.
6531 destroy : function(){
6532 Roo.ButtonToggleMgr.unregister(this);
6533 this.el.removeAllListeners();
6534 this.purgeListeners();
6539 autoWidth : function(){
6541 this.el.setWidth("auto");
6542 if(Roo.isIE7 && Roo.isStrict){
6543 var ib = this.el.child('button');
6544 if(ib && ib.getWidth() > 20){
6546 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6551 this.el.beginMeasure();
6553 if(this.el.getWidth() < this.minWidth){
6554 this.el.setWidth(this.minWidth);
6557 this.el.endMeasure();
6564 * Assigns this button's click handler
6565 * @param {Function} handler The function to call when the button is clicked
6566 * @param {Object} scope (optional) Scope for the function passed in
6568 setHandler : function(handler, scope){
6569 this.handler = handler;
6574 * Sets this button's text
6575 * @param {String} text The button text
6577 setText : function(text){
6580 this.el.child("td.x-btn-center button.x-btn-text").update(text);
6586 * Gets the text for this button
6587 * @return {String} The button text
6589 getText : function(){
6597 this.hidden = false;
6599 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6609 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6614 * Convenience function for boolean show/hide
6615 * @param {Boolean} visible True to show, false to hide
6617 setVisible: function(visible){
6626 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6627 * @param {Boolean} state (optional) Force a particular state
6629 toggle : function(state){
6630 state = state === undefined ? !this.pressed : state;
6631 if(state != this.pressed){
6633 this.el.addClass("x-btn-pressed");
6634 this.pressed = true;
6635 this.fireEvent("toggle", this, true);
6637 this.el.removeClass("x-btn-pressed");
6638 this.pressed = false;
6639 this.fireEvent("toggle", this, false);
6641 if(this.toggleHandler){
6642 this.toggleHandler.call(this.scope || this, this, state);
6651 this.el.child('button:first').focus();
6655 * Disable this button
6657 disable : function(){
6659 this.el.addClass("x-btn-disabled");
6661 this.disabled = true;
6665 * Enable this button
6667 enable : function(){
6669 this.el.removeClass("x-btn-disabled");
6671 this.disabled = false;
6675 * Convenience function for boolean enable/disable
6676 * @param {Boolean} enabled True to enable, false to disable
6678 setDisabled : function(v){
6679 this[v !== true ? "enable" : "disable"]();
6683 onClick : function(e)
6692 if(this.enableToggle){
6695 if(this.menu && !this.menu.isVisible()){
6696 this.menu.show(this.el, this.menuAlign);
6698 this.fireEvent("click", this, e);
6700 this.el.removeClass("x-btn-over");
6701 this.handler.call(this.scope || this, this, e);
6706 onMouseOver : function(e){
6708 this.el.addClass("x-btn-over");
6709 this.fireEvent('mouseover', this, e);
6713 onMouseOut : function(e){
6714 if(!e.within(this.el, true)){
6715 this.el.removeClass("x-btn-over");
6716 this.fireEvent('mouseout', this, e);
6720 onFocus : function(e){
6722 this.el.addClass("x-btn-focus");
6726 onBlur : function(e){
6727 this.el.removeClass("x-btn-focus");
6730 onMouseDown : function(e){
6731 if(!this.disabled && e.button == 0){
6732 this.el.addClass("x-btn-click");
6733 Roo.get(document).on('mouseup', this.onMouseUp, this);
6737 onMouseUp : function(e){
6739 this.el.removeClass("x-btn-click");
6740 Roo.get(document).un('mouseup', this.onMouseUp, this);
6744 onMenuShow : function(e){
6745 this.el.addClass("x-btn-menu-active");
6748 onMenuHide : function(e){
6749 this.el.removeClass("x-btn-menu-active");
6753 // Private utility class used by Button
6754 Roo.ButtonToggleMgr = function(){
6757 function toggleGroup(btn, state){
6759 var g = groups[btn.toggleGroup];
6760 for(var i = 0, l = g.length; i < l; i++){
6769 register : function(btn){
6770 if(!btn.toggleGroup){
6773 var g = groups[btn.toggleGroup];
6775 g = groups[btn.toggleGroup] = [];
6778 btn.on("toggle", toggleGroup);
6781 unregister : function(btn){
6782 if(!btn.toggleGroup){
6785 var g = groups[btn.toggleGroup];
6788 btn.un("toggle", toggleGroup);
6794 * Ext JS Library 1.1.1
6795 * Copyright(c) 2006-2007, Ext JS, LLC.
6797 * Originally Released Under LGPL - original licence link has changed is not relivant.
6800 * <script type="text/javascript">
6804 * @class Roo.SplitButton
6805 * @extends Roo.Button
6806 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6807 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
6808 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6809 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6810 * @cfg {String} arrowTooltip The title attribute of the arrow
6812 * Create a new menu button
6813 * @param {String/HTMLElement/Element} renderTo The element to append the button to
6814 * @param {Object} config The config object
6816 Roo.SplitButton = function(renderTo, config){
6817 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6820 * Fires when this button's arrow is clicked
6821 * @param {SplitButton} this
6822 * @param {EventObject} e The click event
6824 this.addEvents({"arrowclick":true});
6827 Roo.extend(Roo.SplitButton, Roo.Button, {
6828 render : function(renderTo){
6829 // this is one sweet looking template!
6830 var tpl = new Roo.Template(
6831 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6832 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6833 '<tr><td class="x-btn-left"><i> </i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
6834 "</tbody></table></td><td>",
6835 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6836 '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button"> </button></td><td class="x-btn-right"><i> </i></td></tr>',
6837 "</tbody></table></td></tr></table>"
6839 var btn = tpl.append(renderTo, [this.text, this.type], true);
6840 var btnEl = btn.child("button");
6842 btn.addClass(this.cls);
6845 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6848 btnEl.addClass(this.iconCls);
6850 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6854 if(this.handleMouseEvents){
6855 btn.on("mouseover", this.onMouseOver, this);
6856 btn.on("mouseout", this.onMouseOut, this);
6857 btn.on("mousedown", this.onMouseDown, this);
6858 btn.on("mouseup", this.onMouseUp, this);
6860 btn.on(this.clickEvent, this.onClick, this);
6862 if(typeof this.tooltip == 'object'){
6863 Roo.QuickTips.tips(Roo.apply({
6867 btnEl.dom[this.tooltipType] = this.tooltip;
6870 if(this.arrowTooltip){
6871 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6880 this.el.addClass("x-btn-pressed");
6882 if(Roo.isIE && !Roo.isIE7){
6883 this.autoWidth.defer(1, this);
6888 this.menu.on("show", this.onMenuShow, this);
6889 this.menu.on("hide", this.onMenuHide, this);
6891 this.fireEvent('render', this);
6895 autoWidth : function(){
6897 var tbl = this.el.child("table:first");
6898 var tbl2 = this.el.child("table:last");
6899 this.el.setWidth("auto");
6900 tbl.setWidth("auto");
6901 if(Roo.isIE7 && Roo.isStrict){
6902 var ib = this.el.child('button:first');
6903 if(ib && ib.getWidth() > 20){
6905 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6910 this.el.beginMeasure();
6912 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6913 tbl.setWidth(this.minWidth-tbl2.getWidth());
6916 this.el.endMeasure();
6919 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6923 * Sets this button's click handler
6924 * @param {Function} handler The function to call when the button is clicked
6925 * @param {Object} scope (optional) Scope for the function passed above
6927 setHandler : function(handler, scope){
6928 this.handler = handler;
6933 * Sets this button's arrow click handler
6934 * @param {Function} handler The function to call when the arrow is clicked
6935 * @param {Object} scope (optional) Scope for the function passed above
6937 setArrowHandler : function(handler, scope){
6938 this.arrowHandler = handler;
6947 this.el.child("button:first").focus();
6952 onClick : function(e){
6955 if(e.getTarget(".x-btn-menu-arrow-wrap")){
6956 if(this.menu && !this.menu.isVisible()){
6957 this.menu.show(this.el, this.menuAlign);
6959 this.fireEvent("arrowclick", this, e);
6960 if(this.arrowHandler){
6961 this.arrowHandler.call(this.scope || this, this, e);
6964 this.fireEvent("click", this, e);
6966 this.handler.call(this.scope || this, this, e);
6972 onMouseDown : function(e){
6974 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6978 onMouseUp : function(e){
6979 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6985 Roo.MenuButton = Roo.SplitButton;/*
6987 * Ext JS Library 1.1.1
6988 * Copyright(c) 2006-2007, Ext JS, LLC.
6990 * Originally Released Under LGPL - original licence link has changed is not relivant.
6993 * <script type="text/javascript">
6997 * @class Roo.Toolbar
6998 * @children Roo.Toolbar.Item Roo.form.Field
6999 * Basic Toolbar class.
7001 * Creates a new Toolbar
7002 * @param {Object} container The config object
7004 Roo.Toolbar = function(container, buttons, config)
7006 /// old consturctor format still supported..
7007 if(container instanceof Array){ // omit the container for later rendering
7008 buttons = container;
7012 if (typeof(container) == 'object' && container.xtype) {
7014 container = config.container;
7015 buttons = config.buttons || []; // not really - use items!!
7018 if (config && config.items) {
7019 xitems = config.items;
7020 delete config.items;
7022 Roo.apply(this, config);
7023 this.buttons = buttons;
7026 this.render(container);
7028 this.xitems = xitems;
7029 Roo.each(xitems, function(b) {
7035 Roo.Toolbar.prototype = {
7037 * @cfg {Array} items
7038 * array of button configs or elements to add (will be converted to a MixedCollection)
7042 * @cfg {String/HTMLElement/Element} container
7043 * The id or element that will contain the toolbar
7046 render : function(ct){
7047 this.el = Roo.get(ct);
7049 this.el.addClass(this.cls);
7051 // using a table allows for vertical alignment
7052 // 100% width is needed by Safari...
7053 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7054 this.tr = this.el.child("tr", true);
7056 this.items = new Roo.util.MixedCollection(false, function(o){
7057 return o.id || ("item" + (++autoId));
7060 this.add.apply(this, this.buttons);
7061 delete this.buttons;
7066 * Adds element(s) to the toolbar -- this function takes a variable number of
7067 * arguments of mixed type and adds them to the toolbar.
7068 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7070 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7071 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7072 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7073 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7074 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7075 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7076 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7077 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7078 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7080 * @param {Mixed} arg2
7081 * @param {Mixed} etc.
7084 var a = arguments, l = a.length;
7085 for(var i = 0; i < l; i++){
7090 _add : function(el) {
7093 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7096 if (el.applyTo){ // some kind of form field
7097 return this.addField(el);
7099 if (el.render){ // some kind of Toolbar.Item
7100 return this.addItem(el);
7102 if (typeof el == "string"){ // string
7103 if(el == "separator" || el == "-"){
7104 return this.addSeparator();
7107 return this.addSpacer();
7110 return this.addFill();
7112 return this.addText(el);
7115 if(el.tagName){ // element
7116 return this.addElement(el);
7118 if(typeof el == "object"){ // must be button config?
7119 return this.addButton(el);
7127 * Add an Xtype element
7128 * @param {Object} xtype Xtype Object
7129 * @return {Object} created Object
7131 addxtype : function(e){
7136 * Returns the Element for this toolbar.
7137 * @return {Roo.Element}
7145 * @return {Roo.Toolbar.Item} The separator item
7147 addSeparator : function(){
7148 return this.addItem(new Roo.Toolbar.Separator());
7152 * Adds a spacer element
7153 * @return {Roo.Toolbar.Spacer} The spacer item
7155 addSpacer : function(){
7156 return this.addItem(new Roo.Toolbar.Spacer());
7160 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7161 * @return {Roo.Toolbar.Fill} The fill item
7163 addFill : function(){
7164 return this.addItem(new Roo.Toolbar.Fill());
7168 * Adds any standard HTML element to the toolbar
7169 * @param {String/HTMLElement/Element} el The element or id of the element to add
7170 * @return {Roo.Toolbar.Item} The element's item
7172 addElement : function(el){
7173 return this.addItem(new Roo.Toolbar.Item(el));
7176 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7177 * @type Roo.util.MixedCollection
7182 * Adds any Toolbar.Item or subclass
7183 * @param {Roo.Toolbar.Item} item
7184 * @return {Roo.Toolbar.Item} The item
7186 addItem : function(item){
7187 var td = this.nextBlock();
7189 this.items.add(item);
7194 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7195 * @param {Object/Array} config A button config or array of configs
7196 * @return {Roo.Toolbar.Button/Array}
7198 addButton : function(config){
7199 if(config instanceof Array){
7201 for(var i = 0, len = config.length; i < len; i++) {
7202 buttons.push(this.addButton(config[i]));
7207 if(!(config instanceof Roo.Toolbar.Button)){
7209 new Roo.Toolbar.SplitButton(config) :
7210 new Roo.Toolbar.Button(config);
7212 var td = this.nextBlock();
7219 * Adds text to the toolbar
7220 * @param {String} text The text to add
7221 * @return {Roo.Toolbar.Item} The element's item
7223 addText : function(text){
7224 return this.addItem(new Roo.Toolbar.TextItem(text));
7228 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7229 * @param {Number} index The index where the item is to be inserted
7230 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7231 * @return {Roo.Toolbar.Button/Item}
7233 insertButton : function(index, item){
7234 if(item instanceof Array){
7236 for(var i = 0, len = item.length; i < len; i++) {
7237 buttons.push(this.insertButton(index + i, item[i]));
7241 if (!(item instanceof Roo.Toolbar.Button)){
7242 item = new Roo.Toolbar.Button(item);
7244 var td = document.createElement("td");
7245 this.tr.insertBefore(td, this.tr.childNodes[index]);
7247 this.items.insert(index, item);
7252 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7253 * @param {Object} config
7254 * @return {Roo.Toolbar.Item} The element's item
7256 addDom : function(config, returnEl){
7257 var td = this.nextBlock();
7258 Roo.DomHelper.overwrite(td, config);
7259 var ti = new Roo.Toolbar.Item(td.firstChild);
7266 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7267 * @type Roo.util.MixedCollection
7272 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7273 * Note: the field should not have been rendered yet. For a field that has already been
7274 * rendered, use {@link #addElement}.
7275 * @param {Roo.form.Field} field
7276 * @return {Roo.ToolbarItem}
7280 addField : function(field) {
7283 this.fields = new Roo.util.MixedCollection(false, function(o){
7284 return o.id || ("item" + (++autoId));
7289 var td = this.nextBlock();
7291 var ti = new Roo.Toolbar.Item(td.firstChild);
7294 this.fields.add(field);
7305 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7306 this.el.child('div').hide();
7314 this.el.child('div').show();
7318 nextBlock : function(){
7319 var td = document.createElement("td");
7320 this.tr.appendChild(td);
7325 destroy : function(){
7326 if(this.items){ // rendered?
7327 Roo.destroy.apply(Roo, this.items.items);
7329 if(this.fields){ // rendered?
7330 Roo.destroy.apply(Roo, this.fields.items);
7332 Roo.Element.uncache(this.el, this.tr);
7337 * @class Roo.Toolbar.Item
7338 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7340 * Creates a new Item
7341 * @param {HTMLElement} el
7343 Roo.Toolbar.Item = function(el){
7345 if (typeof (el.xtype) != 'undefined') {
7350 this.el = Roo.getDom(el);
7351 this.id = Roo.id(this.el);
7352 this.hidden = false;
7357 * Fires when the button is rendered
7358 * @param {Button} this
7362 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7364 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7365 //Roo.Toolbar.Item.prototype = {
7368 * Get this item's HTML Element
7369 * @return {HTMLElement}
7376 render : function(td){
7379 td.appendChild(this.el);
7381 this.fireEvent('render', this);
7385 * Removes and destroys this item.
7387 destroy : function(){
7388 this.td.parentNode.removeChild(this.td);
7395 this.hidden = false;
7396 this.td.style.display = "";
7404 this.td.style.display = "none";
7408 * Convenience function for boolean show/hide.
7409 * @param {Boolean} visible true to show/false to hide
7411 setVisible: function(visible){
7420 * Try to focus this item.
7423 Roo.fly(this.el).focus();
7427 * Disables this item.
7429 disable : function(){
7430 Roo.fly(this.td).addClass("x-item-disabled");
7431 this.disabled = true;
7432 this.el.disabled = true;
7436 * Enables this item.
7438 enable : function(){
7439 Roo.fly(this.td).removeClass("x-item-disabled");
7440 this.disabled = false;
7441 this.el.disabled = false;
7447 * @class Roo.Toolbar.Separator
7448 * @extends Roo.Toolbar.Item
7449 * A simple toolbar separator class
7451 * Creates a new Separator
7453 Roo.Toolbar.Separator = function(cfg){
7455 var s = document.createElement("span");
7456 s.className = "ytb-sep";
7461 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7463 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7465 disable:Roo.emptyFn,
7470 * @class Roo.Toolbar.Spacer
7471 * @extends Roo.Toolbar.Item
7472 * A simple element that adds extra horizontal space to a toolbar.
7474 * Creates a new Spacer
7476 Roo.Toolbar.Spacer = function(cfg){
7477 var s = document.createElement("div");
7478 s.className = "ytb-spacer";
7482 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7484 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7486 disable:Roo.emptyFn,
7491 * @class Roo.Toolbar.Fill
7492 * @extends Roo.Toolbar.Spacer
7493 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7495 * Creates a new Spacer
7497 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7499 render : function(td){
7500 td.style.width = '100%';
7501 Roo.Toolbar.Fill.superclass.render.call(this, td);
7506 * @class Roo.Toolbar.TextItem
7507 * @extends Roo.Toolbar.Item
7508 * A simple class that renders text directly into a toolbar.
7510 * Creates a new TextItem
7511 * @cfg {string} text
7513 Roo.Toolbar.TextItem = function(cfg){
7514 var text = cfg || "";
7515 if (typeof(cfg) == 'object') {
7516 text = cfg.text || "";
7520 var s = document.createElement("span");
7521 s.className = "ytb-text";
7527 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7529 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7533 disable:Roo.emptyFn,
7538 * @class Roo.Toolbar.Button
7539 * @extends Roo.Button
7540 * A button that renders into a toolbar.
7542 * Creates a new Button
7543 * @param {Object} config A standard {@link Roo.Button} config object
7545 Roo.Toolbar.Button = function(config){
7546 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7548 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7552 render : function(td){
7554 Roo.Toolbar.Button.superclass.render.call(this, td);
7558 * Removes and destroys this button
7560 destroy : function(){
7561 Roo.Toolbar.Button.superclass.destroy.call(this);
7562 this.td.parentNode.removeChild(this.td);
7569 this.hidden = false;
7570 this.td.style.display = "";
7578 this.td.style.display = "none";
7582 * Disables this item
7584 disable : function(){
7585 Roo.fly(this.td).addClass("x-item-disabled");
7586 this.disabled = true;
7592 enable : function(){
7593 Roo.fly(this.td).removeClass("x-item-disabled");
7594 this.disabled = false;
7598 Roo.ToolbarButton = Roo.Toolbar.Button;
7601 * @class Roo.Toolbar.SplitButton
7602 * @extends Roo.SplitButton
7603 * A menu button that renders into a toolbar.
7605 * Creates a new SplitButton
7606 * @param {Object} config A standard {@link Roo.SplitButton} config object
7608 Roo.Toolbar.SplitButton = function(config){
7609 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7611 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7612 render : function(td){
7614 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7618 * Removes and destroys this button
7620 destroy : function(){
7621 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7622 this.td.parentNode.removeChild(this.td);
7629 this.hidden = false;
7630 this.td.style.display = "";
7638 this.td.style.display = "none";
7643 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7645 * Ext JS Library 1.1.1
7646 * Copyright(c) 2006-2007, Ext JS, LLC.
7648 * Originally Released Under LGPL - original licence link has changed is not relivant.
7651 * <script type="text/javascript">
7655 * @class Roo.PagingToolbar
7656 * @extends Roo.Toolbar
7657 * @children Roo.Toolbar.Item Roo.form.Field
7658 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7660 * Create a new PagingToolbar
7661 * @param {Object} config The config object
7663 Roo.PagingToolbar = function(el, ds, config)
7665 // old args format still supported... - xtype is prefered..
7666 if (typeof(el) == 'object' && el.xtype) {
7667 // created from xtype...
7670 el = config.container;
7674 items = config.items;
7678 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7681 this.renderButtons(this.el);
7684 // supprot items array.
7686 Roo.each(items, function(e) {
7687 this.add(Roo.factory(e));
7692 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7694 * @cfg {Roo.data.Store} dataSource
7695 * The underlying data store providing the paged data
7698 * @cfg {String/HTMLElement/Element} container
7699 * container The id or element that will contain the toolbar
7702 * @cfg {Boolean} displayInfo
7703 * True to display the displayMsg (defaults to false)
7706 * @cfg {Number} pageSize
7707 * The number of records to display per page (defaults to 20)
7711 * @cfg {String} displayMsg
7712 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7714 displayMsg : 'Displaying {0} - {1} of {2}',
7716 * @cfg {String} emptyMsg
7717 * The message to display when no records are found (defaults to "No data to display")
7719 emptyMsg : 'No data to display',
7721 * Customizable piece of the default paging text (defaults to "Page")
7724 beforePageText : "Page",
7726 * Customizable piece of the default paging text (defaults to "of %0")
7729 afterPageText : "of {0}",
7731 * Customizable piece of the default paging text (defaults to "First Page")
7734 firstText : "First Page",
7736 * Customizable piece of the default paging text (defaults to "Previous Page")
7739 prevText : "Previous Page",
7741 * Customizable piece of the default paging text (defaults to "Next Page")
7744 nextText : "Next Page",
7746 * Customizable piece of the default paging text (defaults to "Last Page")
7749 lastText : "Last Page",
7751 * Customizable piece of the default paging text (defaults to "Refresh")
7754 refreshText : "Refresh",
7757 renderButtons : function(el){
7758 Roo.PagingToolbar.superclass.render.call(this, el);
7759 this.first = this.addButton({
7760 tooltip: this.firstText,
7761 cls: "x-btn-icon x-grid-page-first",
7763 handler: this.onClick.createDelegate(this, ["first"])
7765 this.prev = this.addButton({
7766 tooltip: this.prevText,
7767 cls: "x-btn-icon x-grid-page-prev",
7769 handler: this.onClick.createDelegate(this, ["prev"])
7771 //this.addSeparator();
7772 this.add(this.beforePageText);
7773 this.field = Roo.get(this.addDom({
7778 cls: "x-grid-page-number"
7780 this.field.on("keydown", this.onPagingKeydown, this);
7781 this.field.on("focus", function(){this.dom.select();});
7782 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7783 this.field.setHeight(18);
7784 //this.addSeparator();
7785 this.next = this.addButton({
7786 tooltip: this.nextText,
7787 cls: "x-btn-icon x-grid-page-next",
7789 handler: this.onClick.createDelegate(this, ["next"])
7791 this.last = this.addButton({
7792 tooltip: this.lastText,
7793 cls: "x-btn-icon x-grid-page-last",
7795 handler: this.onClick.createDelegate(this, ["last"])
7797 //this.addSeparator();
7798 this.loading = this.addButton({
7799 tooltip: this.refreshText,
7800 cls: "x-btn-icon x-grid-loading",
7801 handler: this.onClick.createDelegate(this, ["refresh"])
7804 if(this.displayInfo){
7805 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7810 updateInfo : function(){
7812 var count = this.ds.getCount();
7813 var msg = count == 0 ?
7817 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
7819 this.displayEl.update(msg);
7824 onLoad : function(ds, r, o){
7825 this.cursor = o.params ? o.params.start : 0;
7826 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7828 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7829 this.field.dom.value = ap;
7830 this.first.setDisabled(ap == 1);
7831 this.prev.setDisabled(ap == 1);
7832 this.next.setDisabled(ap == ps);
7833 this.last.setDisabled(ap == ps);
7834 this.loading.enable();
7839 getPageData : function(){
7840 var total = this.ds.getTotalCount();
7843 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7844 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7849 onLoadError : function(){
7850 this.loading.enable();
7854 onPagingKeydown : function(e){
7856 var d = this.getPageData();
7858 var v = this.field.dom.value, pageNum;
7859 if(!v || isNaN(pageNum = parseInt(v, 10))){
7860 this.field.dom.value = d.activePage;
7863 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7864 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7867 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
7869 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7870 this.field.dom.value = pageNum;
7871 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7874 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7876 var v = this.field.dom.value, pageNum;
7877 var increment = (e.shiftKey) ? 10 : 1;
7878 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7881 if(!v || isNaN(pageNum = parseInt(v, 10))) {
7882 this.field.dom.value = d.activePage;
7885 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7887 this.field.dom.value = parseInt(v, 10) + increment;
7888 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7889 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7896 beforeLoad : function(){
7898 this.loading.disable();
7903 onClick : function(which){
7907 ds.load({params:{start: 0, limit: this.pageSize}});
7910 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7913 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7916 var total = ds.getTotalCount();
7917 var extra = total % this.pageSize;
7918 var lastStart = extra ? (total - extra) : total-this.pageSize;
7919 ds.load({params:{start: lastStart, limit: this.pageSize}});
7922 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7928 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7929 * @param {Roo.data.Store} store The data store to unbind
7931 unbind : function(ds){
7932 ds.un("beforeload", this.beforeLoad, this);
7933 ds.un("load", this.onLoad, this);
7934 ds.un("loadexception", this.onLoadError, this);
7935 ds.un("remove", this.updateInfo, this);
7936 ds.un("add", this.updateInfo, this);
7937 this.ds = undefined;
7941 * Binds the paging toolbar to the specified {@link Roo.data.Store}
7942 * @param {Roo.data.Store} store The data store to bind
7944 bind : function(ds){
7945 ds.on("beforeload", this.beforeLoad, this);
7946 ds.on("load", this.onLoad, this);
7947 ds.on("loadexception", this.onLoadError, this);
7948 ds.on("remove", this.updateInfo, this);
7949 ds.on("add", this.updateInfo, this);
7954 * Ext JS Library 1.1.1
7955 * Copyright(c) 2006-2007, Ext JS, LLC.
7957 * Originally Released Under LGPL - original licence link has changed is not relivant.
7960 * <script type="text/javascript">
7964 * @class Roo.Resizable
7965 * @extends Roo.util.Observable
7966 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7967 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7968 * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
7969 * the element will be wrapped for you automatically.</p>
7970 * <p>Here is the list of valid resize handles:</p>
7973 ------ -------------------
7982 'hd' horizontal drag
7985 * <p>Here's an example showing the creation of a typical Resizable:</p>
7987 var resizer = new Roo.Resizable("element-id", {
7995 resizer.on("resize", myHandler);
7997 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7998 * resizer.east.setDisplayed(false);</p>
7999 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8000 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8001 * resize operation's new size (defaults to [0, 0])
8002 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8003 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8004 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8005 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8006 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8007 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8008 * @cfg {Number} width The width of the element in pixels (defaults to null)
8009 * @cfg {Number} height The height of the element in pixels (defaults to null)
8010 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8011 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8012 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8013 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8014 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8015 * in favor of the handles config option (defaults to false)
8016 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8017 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8018 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8019 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8020 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8021 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8022 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8023 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8024 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8025 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8026 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8028 * Create a new resizable component
8029 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8030 * @param {Object} config configuration options
8032 Roo.Resizable = function(el, config)
8034 this.el = Roo.get(el);
8036 if(config && config.wrap){
8037 config.resizeChild = this.el;
8038 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8039 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8040 this.el.setStyle("overflow", "hidden");
8041 this.el.setPositioning(config.resizeChild.getPositioning());
8042 config.resizeChild.clearPositioning();
8043 if(!config.width || !config.height){
8044 var csize = config.resizeChild.getSize();
8045 this.el.setSize(csize.width, csize.height);
8047 if(config.pinned && !config.adjustments){
8048 config.adjustments = "auto";
8052 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8053 this.proxy.unselectable();
8054 this.proxy.enableDisplayMode('block');
8056 Roo.apply(this, config);
8059 this.disableTrackOver = true;
8060 this.el.addClass("x-resizable-pinned");
8062 // if the element isn't positioned, make it relative
8063 var position = this.el.getStyle("position");
8064 if(position != "absolute" && position != "fixed"){
8065 this.el.setStyle("position", "relative");
8067 if(!this.handles){ // no handles passed, must be legacy style
8068 this.handles = 's,e,se';
8069 if(this.multiDirectional){
8070 this.handles += ',n,w';
8073 if(this.handles == "all"){
8074 this.handles = "n s e w ne nw se sw";
8076 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8077 var ps = Roo.Resizable.positions;
8078 for(var i = 0, len = hs.length; i < len; i++){
8079 if(hs[i] && ps[hs[i]]){
8080 var pos = ps[hs[i]];
8081 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8085 this.corner = this.southeast;
8087 // updateBox = the box can move..
8088 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8089 this.updateBox = true;
8092 this.activeHandle = null;
8094 if(this.resizeChild){
8095 if(typeof this.resizeChild == "boolean"){
8096 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8098 this.resizeChild = Roo.get(this.resizeChild, true);
8102 if(this.adjustments == "auto"){
8103 var rc = this.resizeChild;
8104 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8105 if(rc && (hw || hn)){
8106 rc.position("relative");
8107 rc.setLeft(hw ? hw.el.getWidth() : 0);
8108 rc.setTop(hn ? hn.el.getHeight() : 0);
8110 this.adjustments = [
8111 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8112 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8117 this.dd = this.dynamic ?
8118 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8119 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8125 * @event beforeresize
8126 * Fired before resize is allowed. Set enabled to false to cancel resize.
8127 * @param {Roo.Resizable} this
8128 * @param {Roo.EventObject} e The mousedown event
8130 "beforeresize" : true,
8134 * @param {Roo.Resizable} this
8135 * @param {Number} x The new x position
8136 * @param {Number} y The new y position
8137 * @param {Number} w The new w width
8138 * @param {Number} h The new h hight
8139 * @param {Roo.EventObject} e The mouseup event
8144 * Fired after a resize.
8145 * @param {Roo.Resizable} this
8146 * @param {Number} width The new width
8147 * @param {Number} height The new height
8148 * @param {Roo.EventObject} e The mouseup event
8153 if(this.width !== null && this.height !== null){
8154 this.resizeTo(this.width, this.height);
8156 this.updateChildSize();
8159 this.el.dom.style.zoom = 1;
8161 Roo.Resizable.superclass.constructor.call(this);
8164 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8165 resizeChild : false,
8166 adjustments : [0, 0],
8176 multiDirectional : false,
8177 disableTrackOver : false,
8178 easing : 'easeOutStrong',
8180 heightIncrement : 0,
8184 preserveRatio : false,
8191 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8193 constrainTo: undefined,
8195 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8197 resizeRegion: undefined,
8201 * Perform a manual resize
8202 * @param {Number} width
8203 * @param {Number} height
8205 resizeTo : function(width, height){
8206 this.el.setSize(width, height);
8207 this.updateChildSize();
8208 this.fireEvent("resize", this, width, height, null);
8212 startSizing : function(e, handle){
8213 this.fireEvent("beforeresize", this, e);
8214 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8217 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8218 this.overlay.unselectable();
8219 this.overlay.enableDisplayMode("block");
8220 this.overlay.on("mousemove", this.onMouseMove, this);
8221 this.overlay.on("mouseup", this.onMouseUp, this);
8223 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8225 this.resizing = true;
8226 this.startBox = this.el.getBox();
8227 this.startPoint = e.getXY();
8228 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8229 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8231 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8232 this.overlay.show();
8234 if(this.constrainTo) {
8235 var ct = Roo.get(this.constrainTo);
8236 this.resizeRegion = ct.getRegion().adjust(
8237 ct.getFrameWidth('t'),
8238 ct.getFrameWidth('l'),
8239 -ct.getFrameWidth('b'),
8240 -ct.getFrameWidth('r')
8244 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8246 this.proxy.setBox(this.startBox);
8248 this.proxy.setStyle('visibility', 'visible');
8254 onMouseDown : function(handle, e){
8257 this.activeHandle = handle;
8258 this.startSizing(e, handle);
8263 onMouseUp : function(e){
8264 var size = this.resizeElement();
8265 this.resizing = false;
8267 this.overlay.hide();
8269 this.fireEvent("resize", this, size.width, size.height, e);
8273 updateChildSize : function(){
8275 if(this.resizeChild){
8277 var child = this.resizeChild;
8278 var adj = this.adjustments;
8279 if(el.dom.offsetWidth){
8280 var b = el.getSize(true);
8281 child.setSize(b.width+adj[0], b.height+adj[1]);
8283 // Second call here for IE
8284 // The first call enables instant resizing and
8285 // the second call corrects scroll bars if they
8288 setTimeout(function(){
8289 if(el.dom.offsetWidth){
8290 var b = el.getSize(true);
8291 child.setSize(b.width+adj[0], b.height+adj[1]);
8299 snap : function(value, inc, min){
8300 if(!inc || !value) {
8303 var newValue = value;
8304 var m = value % inc;
8307 newValue = value + (inc-m);
8309 newValue = value - m;
8312 return Math.max(min, newValue);
8316 resizeElement : function(){
8317 var box = this.proxy.getBox();
8319 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8321 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8323 this.updateChildSize();
8331 constrain : function(v, diff, m, mx){
8334 }else if(v - diff > mx){
8341 onMouseMove : function(e){
8344 try{// try catch so if something goes wrong the user doesn't get hung
8346 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8350 //var curXY = this.startPoint;
8351 var curSize = this.curSize || this.startBox;
8352 var x = this.startBox.x, y = this.startBox.y;
8354 var w = curSize.width, h = curSize.height;
8356 var mw = this.minWidth, mh = this.minHeight;
8357 var mxw = this.maxWidth, mxh = this.maxHeight;
8358 var wi = this.widthIncrement;
8359 var hi = this.heightIncrement;
8361 var eventXY = e.getXY();
8362 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8363 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8365 var pos = this.activeHandle.position;
8370 w = Math.min(Math.max(mw, w), mxw);
8375 h = Math.min(Math.max(mh, h), mxh);
8380 w = Math.min(Math.max(mw, w), mxw);
8381 h = Math.min(Math.max(mh, h), mxh);
8384 diffY = this.constrain(h, diffY, mh, mxh);
8391 var adiffX = Math.abs(diffX);
8392 var sub = (adiffX % wi); // how much
8393 if (sub > (wi/2)) { // far enough to snap
8394 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8396 // remove difference..
8397 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8401 x = Math.max(this.minX, x);
8404 diffX = this.constrain(w, diffX, mw, mxw);
8410 w = Math.min(Math.max(mw, w), mxw);
8411 diffY = this.constrain(h, diffY, mh, mxh);
8416 diffX = this.constrain(w, diffX, mw, mxw);
8417 diffY = this.constrain(h, diffY, mh, mxh);
8424 diffX = this.constrain(w, diffX, mw, mxw);
8426 h = Math.min(Math.max(mh, h), mxh);
8432 var sw = this.snap(w, wi, mw);
8433 var sh = this.snap(h, hi, mh);
8434 if(sw != w || sh != h){
8457 if(this.preserveRatio){
8462 h = Math.min(Math.max(mh, h), mxh);
8467 w = Math.min(Math.max(mw, w), mxw);
8472 w = Math.min(Math.max(mw, w), mxw);
8478 w = Math.min(Math.max(mw, w), mxw);
8484 h = Math.min(Math.max(mh, h), mxh);
8492 h = Math.min(Math.max(mh, h), mxh);
8502 h = Math.min(Math.max(mh, h), mxh);
8510 if (pos == 'hdrag') {
8513 this.proxy.setBounds(x, y, w, h);
8515 this.resizeElement();
8519 this.fireEvent("resizing", this, x, y, w, h, e);
8523 handleOver : function(){
8525 this.el.addClass("x-resizable-over");
8530 handleOut : function(){
8532 this.el.removeClass("x-resizable-over");
8537 * Returns the element this component is bound to.
8538 * @return {Roo.Element}
8545 * Returns the resizeChild element (or null).
8546 * @return {Roo.Element}
8548 getResizeChild : function(){
8549 return this.resizeChild;
8551 groupHandler : function()
8556 * Destroys this resizable. If the element was wrapped and
8557 * removeEl is not true then the element remains.
8558 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8560 destroy : function(removeEl){
8561 this.proxy.remove();
8563 this.overlay.removeAllListeners();
8564 this.overlay.remove();
8566 var ps = Roo.Resizable.positions;
8568 if(typeof ps[k] != "function" && this[ps[k]]){
8569 var h = this[ps[k]];
8570 h.el.removeAllListeners();
8582 // hash to map config positions to true positions
8583 Roo.Resizable.positions = {
8584 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
8589 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8591 // only initialize the template if resizable is used
8592 var tpl = Roo.DomHelper.createTemplate(
8593 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8596 Roo.Resizable.Handle.prototype.tpl = tpl;
8598 this.position = pos;
8600 // show north drag fro topdra
8601 var handlepos = pos == 'hdrag' ? 'north' : pos;
8603 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8604 if (pos == 'hdrag') {
8605 this.el.setStyle('cursor', 'pointer');
8607 this.el.unselectable();
8609 this.el.setOpacity(0);
8611 this.el.on("mousedown", this.onMouseDown, this);
8612 if(!disableTrackOver){
8613 this.el.on("mouseover", this.onMouseOver, this);
8614 this.el.on("mouseout", this.onMouseOut, this);
8619 Roo.Resizable.Handle.prototype = {
8620 afterResize : function(rz){
8625 onMouseDown : function(e){
8626 this.rz.onMouseDown(this, e);
8629 onMouseOver : function(e){
8630 this.rz.handleOver(this, e);
8633 onMouseOut : function(e){
8634 this.rz.handleOut(this, e);
8638 * Ext JS Library 1.1.1
8639 * Copyright(c) 2006-2007, Ext JS, LLC.
8641 * Originally Released Under LGPL - original licence link has changed is not relivant.
8644 * <script type="text/javascript">
8649 * @extends Roo.Component
8650 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8652 * Create a new Editor
8653 * @param {Roo.form.Field} field The Field object (or descendant)
8654 * @param {Object} config The config object
8656 Roo.Editor = function(field, config){
8657 Roo.Editor.superclass.constructor.call(this, config);
8661 * @event beforestartedit
8662 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
8663 * false from the handler of this event.
8664 * @param {Editor} this
8665 * @param {Roo.Element} boundEl The underlying element bound to this editor
8666 * @param {Mixed} value The field value being set
8668 "beforestartedit" : true,
8671 * Fires when this editor is displayed
8672 * @param {Roo.Element} boundEl The underlying element bound to this editor
8673 * @param {Mixed} value The starting field value
8677 * @event beforecomplete
8678 * Fires after a change has been made to the field, but before the change is reflected in the underlying
8679 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
8680 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8681 * event will not fire since no edit actually occurred.
8682 * @param {Editor} this
8683 * @param {Mixed} value The current field value
8684 * @param {Mixed} startValue The original field value
8686 "beforecomplete" : true,
8689 * Fires after editing is complete and any changed value has been written to the underlying field.
8690 * @param {Editor} this
8691 * @param {Mixed} value The current field value
8692 * @param {Mixed} startValue The original field value
8697 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8698 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8699 * @param {Roo.form.Field} this
8700 * @param {Roo.EventObject} e The event object
8706 Roo.extend(Roo.Editor, Roo.Component, {
8708 * @cfg {Boolean/String} autosize
8709 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8710 * or "height" to adopt the height only (defaults to false)
8713 * @cfg {Boolean} revertInvalid
8714 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8715 * validation fails (defaults to true)
8718 * @cfg {Boolean} ignoreNoChange
8719 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8720 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
8721 * will never be ignored.
8724 * @cfg {Boolean} hideEl
8725 * False to keep the bound element visible while the editor is displayed (defaults to true)
8728 * @cfg {Mixed} value
8729 * The data value of the underlying field (defaults to "")
8733 * @cfg {String} alignment
8734 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8738 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8739 * for bottom-right shadow (defaults to "frame")
8743 * @cfg {Boolean} constrain True to constrain the editor to the viewport
8747 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8749 completeOnEnter : false,
8751 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8753 cancelOnEsc : false,
8755 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8760 onRender : function(ct, position){
8761 this.el = new Roo.Layer({
8762 shadow: this.shadow,
8768 constrain: this.constrain
8770 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8771 if(this.field.msgTarget != 'title'){
8772 this.field.msgTarget = 'qtip';
8774 this.field.render(this.el);
8776 this.field.el.dom.setAttribute('autocomplete', 'off');
8778 this.field.on("specialkey", this.onSpecialKey, this);
8779 if(this.swallowKeys){
8780 this.field.el.swallowEvent(['keydown','keypress']);
8783 this.field.on("blur", this.onBlur, this);
8784 if(this.field.grow){
8785 this.field.on("autosize", this.el.sync, this.el, {delay:1});
8789 onSpecialKey : function(field, e)
8791 //Roo.log('editor onSpecialKey');
8792 if(this.completeOnEnter && e.getKey() == e.ENTER){
8794 this.completeEdit();
8797 // do not fire special key otherwise it might hide close the editor...
8798 if(e.getKey() == e.ENTER){
8801 if(this.cancelOnEsc && e.getKey() == e.ESC){
8805 this.fireEvent('specialkey', field, e);
8810 * Starts the editing process and shows the editor.
8811 * @param {String/HTMLElement/Element} el The element to edit
8812 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8813 * to the innerHTML of el.
8815 startEdit : function(el, value){
8817 this.completeEdit();
8819 this.boundEl = Roo.get(el);
8820 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8822 this.render(this.parentEl || document.body);
8824 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8827 this.startValue = v;
8828 this.field.setValue(v);
8830 var sz = this.boundEl.getSize();
8831 switch(this.autoSize){
8833 this.setSize(sz.width, "");
8836 this.setSize("", sz.height);
8839 this.setSize(sz.width, sz.height);
8842 this.el.alignTo(this.boundEl, this.alignment);
8843 this.editing = true;
8845 Roo.QuickTips.disable();
8851 * Sets the height and width of this editor.
8852 * @param {Number} width The new width
8853 * @param {Number} height The new height
8855 setSize : function(w, h){
8856 this.field.setSize(w, h);
8863 * Realigns the editor to the bound field based on the current alignment config value.
8865 realign : function(){
8866 this.el.alignTo(this.boundEl, this.alignment);
8870 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8871 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8873 completeEdit : function(remainVisible){
8877 var v = this.getValue();
8878 if(this.revertInvalid !== false && !this.field.isValid()){
8879 v = this.startValue;
8880 this.cancelEdit(true);
8882 if(String(v) === String(this.startValue) && this.ignoreNoChange){
8883 this.editing = false;
8887 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8888 this.editing = false;
8889 if(this.updateEl && this.boundEl){
8890 this.boundEl.update(v);
8892 if(remainVisible !== true){
8895 this.fireEvent("complete", this, v, this.startValue);
8900 onShow : function(){
8902 if(this.hideEl !== false){
8903 this.boundEl.hide();
8906 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8907 this.fixIEFocus = true;
8908 this.deferredFocus.defer(50, this);
8912 this.fireEvent("startedit", this.boundEl, this.startValue);
8915 deferredFocus : function(){
8922 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
8923 * reverted to the original starting value.
8924 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8925 * cancel (defaults to false)
8927 cancelEdit : function(remainVisible){
8929 this.setValue(this.startValue);
8930 if(remainVisible !== true){
8937 onBlur : function(){
8938 if(this.allowBlur !== true && this.editing){
8939 this.completeEdit();
8944 onHide : function(){
8946 this.completeEdit();
8950 if(this.field.collapse){
8951 this.field.collapse();
8954 if(this.hideEl !== false){
8955 this.boundEl.show();
8958 Roo.QuickTips.enable();
8963 * Sets the data value of the editor
8964 * @param {Mixed} value Any valid value supported by the underlying field
8966 setValue : function(v){
8967 this.field.setValue(v);
8971 * Gets the data value of the editor
8972 * @return {Mixed} The data value
8974 getValue : function(){
8975 return this.field.getValue();
8979 * Ext JS Library 1.1.1
8980 * Copyright(c) 2006-2007, Ext JS, LLC.
8982 * Originally Released Under LGPL - original licence link has changed is not relivant.
8985 * <script type="text/javascript">
8989 * @class Roo.BasicDialog
8990 * @extends Roo.util.Observable
8991 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
8993 var dlg = new Roo.BasicDialog("my-dlg", {
9002 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9003 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9004 dlg.addButton('Cancel', dlg.hide, dlg);
9007 <b>A Dialog should always be a direct child of the body element.</b>
9008 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9009 * @cfg {String} title Default text to display in the title bar (defaults to null)
9010 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9011 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9012 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9013 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9014 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9015 * (defaults to null with no animation)
9016 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9017 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9018 * property for valid values (defaults to 'all')
9019 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9020 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9021 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9022 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9023 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9024 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9025 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9026 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9027 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9028 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9029 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9030 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9031 * draggable = true (defaults to false)
9032 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9033 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9034 * shadow (defaults to false)
9035 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9036 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9037 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9038 * @cfg {Array} buttons Array of buttons
9039 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9041 * Create a new BasicDialog.
9042 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9043 * @param {Object} config Configuration options
9045 Roo.BasicDialog = function(el, config){
9046 this.el = Roo.get(el);
9047 var dh = Roo.DomHelper;
9048 if(!this.el && config && config.autoCreate){
9049 if(typeof config.autoCreate == "object"){
9050 if(!config.autoCreate.id){
9051 config.autoCreate.id = el;
9053 this.el = dh.append(document.body,
9054 config.autoCreate, true);
9056 this.el = dh.append(document.body,
9057 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9061 el.setDisplayed(true);
9062 el.hide = this.hideAction;
9064 el.addClass("x-dlg");
9066 Roo.apply(this, config);
9068 this.proxy = el.createProxy("x-dlg-proxy");
9069 this.proxy.hide = this.hideAction;
9070 this.proxy.setOpacity(.5);
9074 el.setWidth(config.width);
9077 el.setHeight(config.height);
9079 this.size = el.getSize();
9080 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9081 this.xy = [config.x,config.y];
9083 this.xy = el.getCenterXY(true);
9085 /** The header element @type Roo.Element */
9086 this.header = el.child("> .x-dlg-hd");
9087 /** The body element @type Roo.Element */
9088 this.body = el.child("> .x-dlg-bd");
9089 /** The footer element @type Roo.Element */
9090 this.footer = el.child("> .x-dlg-ft");
9093 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9096 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9099 this.header.unselectable();
9101 this.header.update(this.title);
9103 // this element allows the dialog to be focused for keyboard event
9104 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9105 this.focusEl.swallowEvent("click", true);
9107 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9109 // wrap the body and footer for special rendering
9110 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9112 this.bwrap.dom.appendChild(this.footer.dom);
9115 this.bg = this.el.createChild({
9116 tag: "div", cls:"x-dlg-bg",
9117 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9119 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9122 if(this.autoScroll !== false && !this.autoTabs){
9123 this.body.setStyle("overflow", "auto");
9126 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9128 if(this.closable !== false){
9129 this.el.addClass("x-dlg-closable");
9130 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9131 this.close.on("click", this.closeClick, this);
9132 this.close.addClassOnOver("x-dlg-close-over");
9134 if(this.collapsible !== false){
9135 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9136 this.collapseBtn.on("click", this.collapseClick, this);
9137 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9138 this.header.on("dblclick", this.collapseClick, this);
9140 if(this.resizable !== false){
9141 this.el.addClass("x-dlg-resizable");
9142 this.resizer = new Roo.Resizable(el, {
9143 minWidth: this.minWidth || 80,
9144 minHeight:this.minHeight || 80,
9145 handles: this.resizeHandles || "all",
9148 this.resizer.on("beforeresize", this.beforeResize, this);
9149 this.resizer.on("resize", this.onResize, this);
9151 if(this.draggable !== false){
9152 el.addClass("x-dlg-draggable");
9153 if (!this.proxyDrag) {
9154 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9157 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9159 dd.setHandleElId(this.header.id);
9160 dd.endDrag = this.endMove.createDelegate(this);
9161 dd.startDrag = this.startMove.createDelegate(this);
9162 dd.onDrag = this.onDrag.createDelegate(this);
9167 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9168 this.mask.enableDisplayMode("block");
9170 this.el.addClass("x-dlg-modal");
9173 this.shadow = new Roo.Shadow({
9174 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9175 offset : this.shadowOffset
9178 this.shadowOffset = 0;
9180 if(Roo.useShims && this.shim !== false){
9181 this.shim = this.el.createShim();
9182 this.shim.hide = this.hideAction;
9191 var bts= this.buttons;
9193 Roo.each(bts, function(b) {
9202 * Fires when a key is pressed
9203 * @param {Roo.BasicDialog} this
9204 * @param {Roo.EventObject} e
9209 * Fires when this dialog is moved by the user.
9210 * @param {Roo.BasicDialog} this
9211 * @param {Number} x The new page X
9212 * @param {Number} y The new page Y
9217 * Fires when this dialog is resized by the user.
9218 * @param {Roo.BasicDialog} this
9219 * @param {Number} width The new width
9220 * @param {Number} height The new height
9225 * Fires before this dialog is hidden.
9226 * @param {Roo.BasicDialog} this
9228 "beforehide" : true,
9231 * Fires when this dialog is hidden.
9232 * @param {Roo.BasicDialog} this
9237 * Fires before this dialog is shown.
9238 * @param {Roo.BasicDialog} this
9240 "beforeshow" : true,
9243 * Fires when this dialog is shown.
9244 * @param {Roo.BasicDialog} this
9248 el.on("keydown", this.onKeyDown, this);
9249 el.on("mousedown", this.toFront, this);
9250 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9252 Roo.DialogManager.register(this);
9253 Roo.BasicDialog.superclass.constructor.call(this);
9256 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9257 shadowOffset: Roo.isIE ? 6 : 5,
9261 defaultButton: null,
9262 buttonAlign: "right",
9267 * Sets the dialog title text
9268 * @param {String} text The title text to display
9269 * @return {Roo.BasicDialog} this
9271 setTitle : function(text){
9272 this.header.update(text);
9277 closeClick : function(){
9282 collapseClick : function(){
9283 this[this.collapsed ? "expand" : "collapse"]();
9287 * Collapses the dialog to its minimized state (only the title bar is visible).
9288 * Equivalent to the user clicking the collapse dialog button.
9290 collapse : function(){
9291 if(!this.collapsed){
9292 this.collapsed = true;
9293 this.el.addClass("x-dlg-collapsed");
9294 this.restoreHeight = this.el.getHeight();
9295 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9300 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9301 * clicking the expand dialog button.
9303 expand : function(){
9305 this.collapsed = false;
9306 this.el.removeClass("x-dlg-collapsed");
9307 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9312 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9313 * @return {Roo.TabPanel} The tabs component
9315 initTabs : function(){
9316 var tabs = this.getTabs();
9317 while(tabs.getTab(0)){
9320 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9322 tabs.addTab(Roo.id(dom), dom.title);
9330 beforeResize : function(){
9331 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9335 onResize : function(){
9337 this.syncBodyHeight();
9338 this.adjustAssets();
9340 this.fireEvent("resize", this, this.size.width, this.size.height);
9344 onKeyDown : function(e){
9345 if(this.isVisible()){
9346 this.fireEvent("keydown", this, e);
9351 * Resizes the dialog.
9352 * @param {Number} width
9353 * @param {Number} height
9354 * @return {Roo.BasicDialog} this
9356 resizeTo : function(width, height){
9357 this.el.setSize(width, height);
9358 this.size = {width: width, height: height};
9359 this.syncBodyHeight();
9360 if(this.fixedcenter){
9363 if(this.isVisible()){
9365 this.adjustAssets();
9367 this.fireEvent("resize", this, width, height);
9373 * Resizes the dialog to fit the specified content size.
9374 * @param {Number} width
9375 * @param {Number} height
9376 * @return {Roo.BasicDialog} this
9378 setContentSize : function(w, h){
9379 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9380 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9381 //if(!this.el.isBorderBox()){
9382 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9383 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9386 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9387 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9389 this.resizeTo(w, h);
9394 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9395 * executed in response to a particular key being pressed while the dialog is active.
9396 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9397 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9398 * @param {Function} fn The function to call
9399 * @param {Object} scope (optional) The scope of the function
9400 * @return {Roo.BasicDialog} this
9402 addKeyListener : function(key, fn, scope){
9403 var keyCode, shift, ctrl, alt;
9404 if(typeof key == "object" && !(key instanceof Array)){
9405 keyCode = key["key"];
9406 shift = key["shift"];
9412 var handler = function(dlg, e){
9413 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9415 if(keyCode instanceof Array){
9416 for(var i = 0, len = keyCode.length; i < len; i++){
9417 if(keyCode[i] == k){
9418 fn.call(scope || window, dlg, k, e);
9424 fn.call(scope || window, dlg, k, e);
9429 this.on("keydown", handler);
9434 * Returns the TabPanel component (creates it if it doesn't exist).
9435 * Note: If you wish to simply check for the existence of tabs without creating them,
9436 * check for a null 'tabs' property.
9437 * @return {Roo.TabPanel} The tabs component
9439 getTabs : function(){
9441 this.el.addClass("x-dlg-auto-tabs");
9442 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9443 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9449 * Adds a button to the footer section of the dialog.
9450 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9451 * object or a valid Roo.DomHelper element config
9452 * @param {Function} handler The function called when the button is clicked
9453 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9454 * @return {Roo.Button} The new button
9456 addButton : function(config, handler, scope){
9457 var dh = Roo.DomHelper;
9459 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9461 if(!this.btnContainer){
9462 var tb = this.footer.createChild({
9464 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9465 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9467 this.btnContainer = tb.firstChild.firstChild.firstChild;
9472 minWidth: this.minButtonWidth,
9475 if(typeof config == "string"){
9476 bconfig.text = config;
9479 bconfig.dhconfig = config;
9481 Roo.apply(bconfig, config);
9485 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9486 bconfig.position = Math.max(0, bconfig.position);
9487 fc = this.btnContainer.childNodes[bconfig.position];
9490 var btn = new Roo.Button(
9492 this.btnContainer.insertBefore(document.createElement("td"),fc)
9493 : this.btnContainer.appendChild(document.createElement("td")),
9494 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9497 this.syncBodyHeight();
9500 * Array of all the buttons that have been added to this dialog via addButton
9505 this.buttons.push(btn);
9510 * Sets the default button to be focused when the dialog is displayed.
9511 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9512 * @return {Roo.BasicDialog} this
9514 setDefaultButton : function(btn){
9515 this.defaultButton = btn;
9520 getHeaderFooterHeight : function(safe){
9523 height += this.header.getHeight();
9526 var fm = this.footer.getMargins();
9527 height += (this.footer.getHeight()+fm.top+fm.bottom);
9529 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9530 height += this.centerBg.getPadding("tb");
9535 syncBodyHeight : function()
9537 var bd = this.body, // the text
9538 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9540 var height = this.size.height - this.getHeaderFooterHeight(false);
9541 bd.setHeight(height-bd.getMargins("tb"));
9542 var hh = this.header.getHeight();
9543 var h = this.size.height-hh;
9546 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9547 bw.setHeight(h-cb.getPadding("tb"));
9549 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9550 bd.setWidth(bw.getWidth(true));
9552 this.tabs.syncHeight();
9554 this.tabs.el.repaint();
9560 * Restores the previous state of the dialog if Roo.state is configured.
9561 * @return {Roo.BasicDialog} this
9563 restoreState : function(){
9564 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9565 if(box && box.width){
9566 this.xy = [box.x, box.y];
9567 this.resizeTo(box.width, box.height);
9573 beforeShow : function(){
9575 if(this.fixedcenter){
9576 this.xy = this.el.getCenterXY(true);
9579 Roo.get(document.body).addClass("x-body-masked");
9580 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9587 animShow : function(){
9588 var b = Roo.get(this.animateTarget).getBox();
9589 this.proxy.setSize(b.width, b.height);
9590 this.proxy.setLocation(b.x, b.y);
9592 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9593 true, .35, this.showEl.createDelegate(this));
9598 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9599 * @return {Roo.BasicDialog} this
9601 show : function(animateTarget){
9602 if (this.fireEvent("beforeshow", this) === false){
9605 if(this.syncHeightBeforeShow){
9606 this.syncBodyHeight();
9607 }else if(this.firstShow){
9608 this.firstShow = false;
9609 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9611 this.animateTarget = animateTarget || this.animateTarget;
9612 if(!this.el.isVisible()){
9614 if(this.animateTarget && Roo.get(this.animateTarget)){
9624 showEl : function(){
9626 this.el.setXY(this.xy);
9628 this.adjustAssets(true);
9631 // IE peekaboo bug - fix found by Dave Fenwick
9635 this.fireEvent("show", this);
9639 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
9640 * dialog itself will receive focus.
9643 if(this.defaultButton){
9644 this.defaultButton.focus();
9646 this.focusEl.focus();
9651 constrainXY : function(){
9652 if(this.constraintoviewport !== false){
9655 var s = this.container.getSize();
9656 this.viewSize = [s.width, s.height];
9658 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9661 var s = Roo.get(this.container||document).getScroll();
9663 var x = this.xy[0], y = this.xy[1];
9664 var w = this.size.width, h = this.size.height;
9665 var vw = this.viewSize[0], vh = this.viewSize[1];
9666 // only move it if it needs it
9668 // first validate right/bottom
9669 if(x + w > vw+s.left){
9673 if(y + h > vh+s.top){
9677 // then make sure top/left isn't negative
9689 if(this.isVisible()){
9690 this.el.setLocation(x, y);
9691 this.adjustAssets();
9698 onDrag : function(){
9699 if(!this.proxyDrag){
9700 this.xy = this.el.getXY();
9701 this.adjustAssets();
9706 adjustAssets : function(doShow){
9707 var x = this.xy[0], y = this.xy[1];
9708 var w = this.size.width, h = this.size.height;
9709 if(doShow === true){
9711 this.shadow.show(this.el);
9717 if(this.shadow && this.shadow.isVisible()){
9718 this.shadow.show(this.el);
9720 if(this.shim && this.shim.isVisible()){
9721 this.shim.setBounds(x, y, w, h);
9726 adjustViewport : function(w, h){
9728 w = Roo.lib.Dom.getViewWidth();
9729 h = Roo.lib.Dom.getViewHeight();
9732 this.viewSize = [w, h];
9733 if(this.modal && this.mask.isVisible()){
9734 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9735 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9737 if(this.isVisible()){
9743 * Destroys this dialog and all its supporting elements (including any tabs, shim,
9744 * shadow, proxy, mask, etc.) Also removes all event listeners.
9745 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9747 destroy : function(removeEl){
9748 if(this.isVisible()){
9749 this.animateTarget = null;
9752 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9754 this.tabs.destroy(removeEl);
9767 for(var i = 0, len = this.buttons.length; i < len; i++){
9768 this.buttons[i].destroy();
9771 this.el.removeAllListeners();
9772 if(removeEl === true){
9776 Roo.DialogManager.unregister(this);
9780 startMove : function(){
9784 if(this.constraintoviewport !== false){
9785 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9790 endMove : function(){
9791 if(!this.proxyDrag){
9792 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9794 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9798 this.adjustAssets();
9800 this.fireEvent("move", this, this.xy[0], this.xy[1]);
9804 * Brings this dialog to the front of any other visible dialogs
9805 * @return {Roo.BasicDialog} this
9807 toFront : function(){
9808 Roo.DialogManager.bringToFront(this);
9813 * Sends this dialog to the back (under) of any other visible dialogs
9814 * @return {Roo.BasicDialog} this
9816 toBack : function(){
9817 Roo.DialogManager.sendToBack(this);
9822 * Centers this dialog in the viewport
9823 * @return {Roo.BasicDialog} this
9825 center : function(){
9826 var xy = this.el.getCenterXY(true);
9827 this.moveTo(xy[0], xy[1]);
9832 * Moves the dialog's top-left corner to the specified point
9835 * @return {Roo.BasicDialog} this
9837 moveTo : function(x, y){
9839 if(this.isVisible()){
9840 this.el.setXY(this.xy);
9841 this.adjustAssets();
9847 * Aligns the dialog to the specified element
9848 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9849 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9850 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9851 * @return {Roo.BasicDialog} this
9853 alignTo : function(element, position, offsets){
9854 this.xy = this.el.getAlignToXY(element, position, offsets);
9855 if(this.isVisible()){
9856 this.el.setXY(this.xy);
9857 this.adjustAssets();
9863 * Anchors an element to another element and realigns it when the window is resized.
9864 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9865 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9866 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9867 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9868 * is a number, it is used as the buffer delay (defaults to 50ms).
9869 * @return {Roo.BasicDialog} this
9871 anchorTo : function(el, alignment, offsets, monitorScroll){
9872 var action = function(){
9873 this.alignTo(el, alignment, offsets);
9875 Roo.EventManager.onWindowResize(action, this);
9876 var tm = typeof monitorScroll;
9877 if(tm != 'undefined'){
9878 Roo.EventManager.on(window, 'scroll', action, this,
9879 {buffer: tm == 'number' ? monitorScroll : 50});
9886 * Returns true if the dialog is visible
9889 isVisible : function(){
9890 return this.el.isVisible();
9894 animHide : function(callback){
9895 var b = Roo.get(this.animateTarget).getBox();
9897 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9899 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9900 this.hideEl.createDelegate(this, [callback]));
9905 * @param {Function} callback (optional) Function to call when the dialog is hidden
9906 * @return {Roo.BasicDialog} this
9908 hide : function(callback){
9909 if (this.fireEvent("beforehide", this) === false){
9918 // sometimes animateTarget seems to get set.. causing problems...
9919 // this just double checks..
9920 if(this.animateTarget && Roo.get(this.animateTarget)) {
9921 this.animHide(callback);
9924 this.hideEl(callback);
9930 hideEl : function(callback){
9934 Roo.get(document.body).removeClass("x-body-masked");
9936 this.fireEvent("hide", this);
9937 if(typeof callback == "function"){
9943 hideAction : function(){
9944 this.setLeft("-10000px");
9945 this.setTop("-10000px");
9946 this.setStyle("visibility", "hidden");
9950 refreshSize : function(){
9951 this.size = this.el.getSize();
9952 this.xy = this.el.getXY();
9953 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9957 // z-index is managed by the DialogManager and may be overwritten at any time
9958 setZIndex : function(index){
9960 this.mask.setStyle("z-index", index);
9963 this.shim.setStyle("z-index", ++index);
9966 this.shadow.setZIndex(++index);
9968 this.el.setStyle("z-index", ++index);
9970 this.proxy.setStyle("z-index", ++index);
9973 this.resizer.proxy.setStyle("z-index", ++index);
9976 this.lastZIndex = index;
9980 * Returns the element for this dialog
9981 * @return {Roo.Element} The underlying dialog Element
9989 * @class Roo.DialogManager
9990 * Provides global access to BasicDialogs that have been created and
9991 * support for z-indexing (layering) multiple open dialogs.
9993 Roo.DialogManager = function(){
9995 var accessList = [];
9999 var sortDialogs = function(d1, d2){
10000 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10004 var orderDialogs = function(){
10005 accessList.sort(sortDialogs);
10006 var seed = Roo.DialogManager.zseed;
10007 for(var i = 0, len = accessList.length; i < len; i++){
10008 var dlg = accessList[i];
10010 dlg.setZIndex(seed + (i*10));
10017 * The starting z-index for BasicDialogs (defaults to 9000)
10018 * @type Number The z-index value
10023 register : function(dlg){
10024 list[dlg.id] = dlg;
10025 accessList.push(dlg);
10029 unregister : function(dlg){
10030 delete list[dlg.id];
10033 if(!accessList.indexOf){
10034 for( i = 0, len = accessList.length; i < len; i++){
10035 if(accessList[i] == dlg){
10036 accessList.splice(i, 1);
10041 i = accessList.indexOf(dlg);
10043 accessList.splice(i, 1);
10049 * Gets a registered dialog by id
10050 * @param {String/Object} id The id of the dialog or a dialog
10051 * @return {Roo.BasicDialog} this
10053 get : function(id){
10054 return typeof id == "object" ? id : list[id];
10058 * Brings the specified dialog to the front
10059 * @param {String/Object} dlg The id of the dialog or a dialog
10060 * @return {Roo.BasicDialog} this
10062 bringToFront : function(dlg){
10063 dlg = this.get(dlg);
10066 dlg._lastAccess = new Date().getTime();
10073 * Sends the specified dialog to the back
10074 * @param {String/Object} dlg The id of the dialog or a dialog
10075 * @return {Roo.BasicDialog} this
10077 sendToBack : function(dlg){
10078 dlg = this.get(dlg);
10079 dlg._lastAccess = -(new Date().getTime());
10085 * Hides all dialogs
10087 hideAll : function(){
10088 for(var id in list){
10089 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10098 * @class Roo.LayoutDialog
10099 * @extends Roo.BasicDialog
10100 * @children Roo.ContentPanel
10102 * Dialog which provides adjustments for working with a layout in a Dialog.
10103 * Add your necessary layout config options to the dialog's config.<br>
10104 * Example usage (including a nested layout):
10107 dialog = new Roo.LayoutDialog("download-dlg", {
10116 // layout config merges with the dialog config
10118 tabPosition: "top",
10119 alwaysShowTabs: true
10122 dialog.addKeyListener(27, dialog.hide, dialog);
10123 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10124 dialog.addButton("Build It!", this.getDownload, this);
10126 // we can even add nested layouts
10127 var innerLayout = new Roo.BorderLayout("dl-inner", {
10137 innerLayout.beginUpdate();
10138 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10139 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10140 innerLayout.endUpdate(true);
10142 var layout = dialog.getLayout();
10143 layout.beginUpdate();
10144 layout.add("center", new Roo.ContentPanel("standard-panel",
10145 {title: "Download the Source", fitToFrame:true}));
10146 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10147 {title: "Build your own roo.js"}));
10148 layout.getRegion("center").showPanel(sp);
10149 layout.endUpdate();
10153 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10154 * @param {Object} config configuration options
10156 Roo.LayoutDialog = function(el, cfg){
10159 if (typeof(cfg) == 'undefined') {
10160 config = Roo.apply({}, el);
10161 // not sure why we use documentElement here.. - it should always be body.
10162 // IE7 borks horribly if we use documentElement.
10163 // webkit also does not like documentElement - it creates a body element...
10164 el = Roo.get( document.body || document.documentElement ).createChild();
10165 //config.autoCreate = true;
10169 config.autoTabs = false;
10170 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10171 this.body.setStyle({overflow:"hidden", position:"relative"});
10172 this.layout = new Roo.BorderLayout(this.body.dom, config);
10173 this.layout.monitorWindowResize = false;
10174 this.el.addClass("x-dlg-auto-layout");
10175 // fix case when center region overwrites center function
10176 this.center = Roo.BasicDialog.prototype.center;
10177 this.on("show", this.layout.layout, this.layout, true);
10178 if (config.items) {
10179 var xitems = config.items;
10180 delete config.items;
10181 Roo.each(xitems, this.addxtype, this);
10186 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10190 * @cfg {Roo.LayoutRegion} east
10193 * @cfg {Roo.LayoutRegion} west
10196 * @cfg {Roo.LayoutRegion} south
10199 * @cfg {Roo.LayoutRegion} north
10202 * @cfg {Roo.LayoutRegion} center
10205 * @cfg {Roo.Button} buttons[] Bottom buttons..
10210 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10213 endUpdate : function(){
10214 this.layout.endUpdate();
10218 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10221 beginUpdate : function(){
10222 this.layout.beginUpdate();
10226 * Get the BorderLayout for this dialog
10227 * @return {Roo.BorderLayout}
10229 getLayout : function(){
10230 return this.layout;
10233 showEl : function(){
10234 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10236 this.layout.layout();
10241 // Use the syncHeightBeforeShow config option to control this automatically
10242 syncBodyHeight : function(){
10243 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10244 if(this.layout){this.layout.layout();}
10248 * Add an xtype element (actually adds to the layout.)
10249 * @return {Object} xdata xtype object data.
10252 addxtype : function(c) {
10253 return this.layout.addxtype(c);
10257 * Ext JS Library 1.1.1
10258 * Copyright(c) 2006-2007, Ext JS, LLC.
10260 * Originally Released Under LGPL - original licence link has changed is not relivant.
10263 * <script type="text/javascript">
10267 * @class Roo.MessageBox
10268 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10272 Roo.Msg.alert('Status', 'Changes saved successfully.');
10274 // Prompt for user data:
10275 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10277 // process text value...
10281 // Show a dialog using config options:
10283 title:'Save Changes?',
10284 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10285 buttons: Roo.Msg.YESNOCANCEL,
10292 Roo.MessageBox = function(){
10293 var dlg, opt, mask, waitTimer;
10294 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10295 var buttons, activeTextEl, bwidth;
10298 var handleButton = function(button){
10300 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10304 var handleHide = function(){
10305 if(opt && opt.cls){
10306 dlg.el.removeClass(opt.cls);
10309 Roo.TaskMgr.stop(waitTimer);
10315 var updateButtons = function(b){
10318 buttons["ok"].hide();
10319 buttons["cancel"].hide();
10320 buttons["yes"].hide();
10321 buttons["no"].hide();
10322 dlg.footer.dom.style.display = 'none';
10325 dlg.footer.dom.style.display = '';
10326 for(var k in buttons){
10327 if(typeof buttons[k] != "function"){
10330 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10331 width += buttons[k].el.getWidth()+15;
10341 var handleEsc = function(d, k, e){
10342 if(opt && opt.closable !== false){
10352 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10353 * @return {Roo.BasicDialog} The BasicDialog element
10355 getDialog : function(){
10357 dlg = new Roo.BasicDialog("x-msg-box", {
10362 constraintoviewport:false,
10364 collapsible : false,
10367 width:400, height:100,
10368 buttonAlign:"center",
10369 closeClick : function(){
10370 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10371 handleButton("no");
10373 handleButton("cancel");
10377 dlg.on("hide", handleHide);
10379 dlg.addKeyListener(27, handleEsc);
10381 var bt = this.buttonText;
10382 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10383 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10384 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10385 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10386 bodyEl = dlg.body.createChild({
10388 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
10390 msgEl = bodyEl.dom.firstChild;
10391 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10392 textboxEl.enableDisplayMode();
10393 textboxEl.addKeyListener([10,13], function(){
10394 if(dlg.isVisible() && opt && opt.buttons){
10395 if(opt.buttons.ok){
10396 handleButton("ok");
10397 }else if(opt.buttons.yes){
10398 handleButton("yes");
10402 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10403 textareaEl.enableDisplayMode();
10404 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10405 progressEl.enableDisplayMode();
10406 var pf = progressEl.dom.firstChild;
10408 pp = Roo.get(pf.firstChild);
10409 pp.setHeight(pf.offsetHeight);
10417 * Updates the message box body text
10418 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10419 * the XHTML-compliant non-breaking space character '&#160;')
10420 * @return {Roo.MessageBox} This message box
10422 updateText : function(text){
10423 if(!dlg.isVisible() && !opt.width){
10424 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10426 msgEl.innerHTML = text || ' ';
10428 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10429 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10431 Math.min(opt.width || cw , this.maxWidth),
10432 Math.max(opt.minWidth || this.minWidth, bwidth)
10435 activeTextEl.setWidth(w);
10437 if(dlg.isVisible()){
10438 dlg.fixedcenter = false;
10440 // to big, make it scroll. = But as usual stupid IE does not support
10443 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10444 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10445 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10447 bodyEl.dom.style.height = '';
10448 bodyEl.dom.style.overflowY = '';
10451 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10453 bodyEl.dom.style.overflowX = '';
10456 dlg.setContentSize(w, bodyEl.getHeight());
10457 if(dlg.isVisible()){
10458 dlg.fixedcenter = true;
10464 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10465 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10466 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10467 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10468 * @return {Roo.MessageBox} This message box
10470 updateProgress : function(value, text){
10472 this.updateText(text);
10474 if (pp) { // weird bug on my firefox - for some reason this is not defined
10475 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10481 * Returns true if the message box is currently displayed
10482 * @return {Boolean} True if the message box is visible, else false
10484 isVisible : function(){
10485 return dlg && dlg.isVisible();
10489 * Hides the message box if it is displayed
10492 if(this.isVisible()){
10498 * Displays a new message box, or reinitializes an existing message box, based on the config options
10499 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10500 * The following config object properties are supported:
10502 Property Type Description
10503 ---------- --------------- ------------------------------------------------------------------------------------
10504 animEl String/Element An id or Element from which the message box should animate as it opens and
10505 closes (defaults to undefined)
10506 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10507 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10508 closable Boolean False to hide the top-right close button (defaults to true). Note that
10509 progress and wait dialogs will ignore this property and always hide the
10510 close button as they can only be closed programmatically.
10511 cls String A custom CSS class to apply to the message box element
10512 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10513 displayed (defaults to 75)
10514 fn Function A callback function to execute after closing the dialog. The arguments to the
10515 function will be btn (the name of the button that was clicked, if applicable,
10516 e.g. "ok"), and text (the value of the active text field, if applicable).
10517 Progress and wait dialogs will ignore this option since they do not respond to
10518 user actions and can only be closed programmatically, so any required function
10519 should be called by the same code after it closes the dialog.
10520 icon String A CSS class that provides a background image to be used as an icon for
10521 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10522 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10523 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10524 modal Boolean False to allow user interaction with the page while the message box is
10525 displayed (defaults to true)
10526 msg String A string that will replace the existing message box body text (defaults
10527 to the XHTML-compliant non-breaking space character ' ')
10528 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10529 progress Boolean True to display a progress bar (defaults to false)
10530 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10531 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10532 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10533 title String The title text
10534 value String The string value to set into the active textbox element if displayed
10535 wait Boolean True to display a progress bar (defaults to false)
10536 width Number The width of the dialog in pixels
10543 msg: 'Please enter your address:',
10545 buttons: Roo.MessageBox.OKCANCEL,
10548 animEl: 'addAddressBtn'
10551 * @param {Object} config Configuration options
10552 * @return {Roo.MessageBox} This message box
10554 show : function(options)
10557 // this causes nightmares if you show one dialog after another
10558 // especially on callbacks..
10560 if(this.isVisible()){
10563 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10564 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10565 Roo.log("New Dialog Message:" + options.msg )
10566 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10567 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10570 var d = this.getDialog();
10572 d.setTitle(opt.title || " ");
10573 d.close.setDisplayed(opt.closable !== false);
10574 activeTextEl = textboxEl;
10575 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10580 textareaEl.setHeight(typeof opt.multiline == "number" ?
10581 opt.multiline : this.defaultTextHeight);
10582 activeTextEl = textareaEl;
10591 progressEl.setDisplayed(opt.progress === true);
10592 this.updateProgress(0);
10593 activeTextEl.dom.value = opt.value || "";
10595 dlg.setDefaultButton(activeTextEl);
10597 var bs = opt.buttons;
10600 db = buttons["ok"];
10601 }else if(bs && bs.yes){
10602 db = buttons["yes"];
10604 dlg.setDefaultButton(db);
10606 bwidth = updateButtons(opt.buttons);
10607 this.updateText(opt.msg);
10609 d.el.addClass(opt.cls);
10611 d.proxyDrag = opt.proxyDrag === true;
10612 d.modal = opt.modal !== false;
10613 d.mask = opt.modal !== false ? mask : false;
10614 if(!d.isVisible()){
10615 // force it to the end of the z-index stack so it gets a cursor in FF
10616 document.body.appendChild(dlg.el.dom);
10617 d.animateTarget = null;
10618 d.show(options.animEl);
10624 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
10625 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10626 * and closing the message box when the process is complete.
10627 * @param {String} title The title bar text
10628 * @param {String} msg The message box body text
10629 * @return {Roo.MessageBox} This message box
10631 progress : function(title, msg){
10638 minWidth: this.minProgressWidth,
10645 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10646 * If a callback function is passed it will be called after the user clicks the button, and the
10647 * id of the button that was clicked will be passed as the only parameter to the callback
10648 * (could also be the top-right close button).
10649 * @param {String} title The title bar text
10650 * @param {String} msg The message box body text
10651 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10652 * @param {Object} scope (optional) The scope of the callback function
10653 * @return {Roo.MessageBox} This message box
10655 alert : function(title, msg, fn, scope){
10668 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
10669 * interaction while waiting for a long-running process to complete that does not have defined intervals.
10670 * You are responsible for closing the message box when the process is complete.
10671 * @param {String} msg The message box body text
10672 * @param {String} title (optional) The title bar text
10673 * @return {Roo.MessageBox} This message box
10675 wait : function(msg, title){
10686 waitTimer = Roo.TaskMgr.start({
10688 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10696 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10697 * If a callback function is passed it will be called after the user clicks either button, and the id of the
10698 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10699 * @param {String} title The title bar text
10700 * @param {String} msg The message box body text
10701 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10702 * @param {Object} scope (optional) The scope of the callback function
10703 * @return {Roo.MessageBox} This message box
10705 confirm : function(title, msg, fn, scope){
10709 buttons: this.YESNO,
10718 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10719 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
10720 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10721 * (could also be the top-right close button) and the text that was entered will be passed as the two
10722 * parameters to the callback.
10723 * @param {String} title The title bar text
10724 * @param {String} msg The message box body text
10725 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10726 * @param {Object} scope (optional) The scope of the callback function
10727 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10728 * property, or the height in pixels to create the textbox (defaults to false / single-line)
10729 * @return {Roo.MessageBox} This message box
10731 prompt : function(title, msg, fn, scope, multiline){
10735 buttons: this.OKCANCEL,
10740 multiline: multiline,
10747 * Button config that displays a single OK button
10752 * Button config that displays Yes and No buttons
10755 YESNO : {yes:true, no:true},
10757 * Button config that displays OK and Cancel buttons
10760 OKCANCEL : {ok:true, cancel:true},
10762 * Button config that displays Yes, No and Cancel buttons
10765 YESNOCANCEL : {yes:true, no:true, cancel:true},
10768 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10771 defaultTextHeight : 75,
10773 * The maximum width in pixels of the message box (defaults to 600)
10778 * The minimum width in pixels of the message box (defaults to 100)
10783 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
10784 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10787 minProgressWidth : 250,
10789 * An object containing the default button text strings that can be overriden for localized language support.
10790 * Supported properties are: ok, cancel, yes and no.
10791 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10804 * Shorthand for {@link Roo.MessageBox}
10806 Roo.Msg = Roo.MessageBox;/*
10808 * Ext JS Library 1.1.1
10809 * Copyright(c) 2006-2007, Ext JS, LLC.
10811 * Originally Released Under LGPL - original licence link has changed is not relivant.
10814 * <script type="text/javascript">
10817 * @class Roo.QuickTips
10818 * Provides attractive and customizable tooltips for any element.
10821 Roo.QuickTips = function(){
10822 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10823 var ce, bd, xy, dd;
10824 var visible = false, disabled = true, inited = false;
10825 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10827 var onOver = function(e){
10831 var t = e.getTarget();
10832 if(!t || t.nodeType !== 1 || t == document || t == document.body){
10835 if(ce && t == ce.el){
10836 clearTimeout(hideProc);
10839 if(t && tagEls[t.id]){
10840 tagEls[t.id].el = t;
10841 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10844 var ttp, et = Roo.fly(t);
10845 var ns = cfg.namespace;
10846 if(tm.interceptTitles && t.title){
10849 t.removeAttribute("title");
10850 e.preventDefault();
10852 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10855 showProc = show.defer(tm.showDelay, tm, [{
10857 text: ttp.replace(/\\n/g,'<br/>'),
10858 width: et.getAttributeNS(ns, cfg.width),
10859 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10860 title: et.getAttributeNS(ns, cfg.title),
10861 cls: et.getAttributeNS(ns, cfg.cls)
10866 var onOut = function(e){
10867 clearTimeout(showProc);
10868 var t = e.getTarget();
10869 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10870 hideProc = setTimeout(hide, tm.hideDelay);
10874 var onMove = function(e){
10880 if(tm.trackMouse && ce){
10885 var onDown = function(e){
10886 clearTimeout(showProc);
10887 clearTimeout(hideProc);
10889 if(tm.hideOnClick){
10892 tm.enable.defer(100, tm);
10897 var getPad = function(){
10898 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10901 var show = function(o){
10905 clearTimeout(dismissProc);
10907 if(removeCls){ // in case manually hidden
10908 el.removeClass(removeCls);
10912 el.addClass(ce.cls);
10913 removeCls = ce.cls;
10916 tipTitle.update(ce.title);
10919 tipTitle.update('');
10922 el.dom.style.width = tm.maxWidth+'px';
10923 //tipBody.dom.style.width = '';
10924 tipBodyText.update(o.text);
10925 var p = getPad(), w = ce.width;
10927 var td = tipBodyText.dom;
10928 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10929 if(aw > tm.maxWidth){
10931 }else if(aw < tm.minWidth){
10937 //tipBody.setWidth(w);
10938 el.setWidth(parseInt(w, 10) + p);
10939 if(ce.autoHide === false){
10940 close.setDisplayed(true);
10945 close.setDisplayed(false);
10951 el.avoidY = xy[1]-18;
10956 el.setStyle("visibility", "visible");
10957 el.fadeIn({callback: afterShow});
10963 var afterShow = function(){
10967 if(tm.autoDismiss && ce.autoHide !== false){
10968 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10973 var hide = function(noanim){
10974 clearTimeout(dismissProc);
10975 clearTimeout(hideProc);
10977 if(el.isVisible()){
10979 if(noanim !== true && tm.animate){
10980 el.fadeOut({callback: afterHide});
10987 var afterHide = function(){
10990 el.removeClass(removeCls);
10997 * @cfg {Number} minWidth
10998 * The minimum width of the quick tip (defaults to 40)
11002 * @cfg {Number} maxWidth
11003 * The maximum width of the quick tip (defaults to 300)
11007 * @cfg {Boolean} interceptTitles
11008 * True to automatically use the element's DOM title value if available (defaults to false)
11010 interceptTitles : false,
11012 * @cfg {Boolean} trackMouse
11013 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11015 trackMouse : false,
11017 * @cfg {Boolean} hideOnClick
11018 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11020 hideOnClick : true,
11022 * @cfg {Number} showDelay
11023 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11027 * @cfg {Number} hideDelay
11028 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11032 * @cfg {Boolean} autoHide
11033 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11034 * Used in conjunction with hideDelay.
11039 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11040 * (defaults to true). Used in conjunction with autoDismissDelay.
11042 autoDismiss : true,
11045 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11047 autoDismissDelay : 5000,
11049 * @cfg {Boolean} animate
11050 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11055 * @cfg {String} title
11056 * Title text to display (defaults to ''). This can be any valid HTML markup.
11060 * @cfg {String} text
11061 * Body text to display (defaults to ''). This can be any valid HTML markup.
11065 * @cfg {String} cls
11066 * A CSS class to apply to the base quick tip element (defaults to '').
11070 * @cfg {Number} width
11071 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11072 * minWidth or maxWidth.
11077 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11078 * or display QuickTips in a page.
11081 tm = Roo.QuickTips;
11082 cfg = tm.tagConfig;
11084 if(!Roo.isReady){ // allow calling of init() before onReady
11085 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11088 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11089 el.fxDefaults = {stopFx: true};
11090 // maximum custom styling
11091 //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11092 el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');
11093 tipTitle = el.child('h3');
11094 tipTitle.enableDisplayMode("block");
11095 tipBody = el.child('div.x-tip-bd');
11096 tipBodyText = el.child('div.x-tip-bd-inner');
11097 //bdLeft = el.child('div.x-tip-bd-left');
11098 //bdRight = el.child('div.x-tip-bd-right');
11099 close = el.child('div.x-tip-close');
11100 close.enableDisplayMode("block");
11101 close.on("click", hide);
11102 var d = Roo.get(document);
11103 d.on("mousedown", onDown);
11104 d.on("mouseover", onOver);
11105 d.on("mouseout", onOut);
11106 d.on("mousemove", onMove);
11107 esc = d.addKeyListener(27, hide);
11110 dd = el.initDD("default", null, {
11111 onDrag : function(){
11115 dd.setHandleElId(tipTitle.id);
11124 * Configures a new quick tip instance and assigns it to a target element. The following config options
11127 Property Type Description
11128 ---------- --------------------- ------------------------------------------------------------------------
11129 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11131 * @param {Object} config The config object
11133 register : function(config){
11134 var cs = config instanceof Array ? config : arguments;
11135 for(var i = 0, len = cs.length; i < len; i++) {
11137 var target = c.target;
11139 if(target instanceof Array){
11140 for(var j = 0, jlen = target.length; j < jlen; j++){
11141 tagEls[target[j]] = c;
11144 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11151 * Removes this quick tip from its element and destroys it.
11152 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11154 unregister : function(el){
11155 delete tagEls[Roo.id(el)];
11159 * Enable this quick tip.
11161 enable : function(){
11162 if(inited && disabled){
11164 if(locks.length < 1){
11171 * Disable this quick tip.
11173 disable : function(){
11175 clearTimeout(showProc);
11176 clearTimeout(hideProc);
11177 clearTimeout(dismissProc);
11185 * Returns true if the quick tip is enabled, else false.
11187 isEnabled : function(){
11193 namespace : "roo", // was ext?? this may break..
11194 alt_namespace : "ext",
11195 attribute : "qtip",
11205 // backwards compat
11206 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11208 * Ext JS Library 1.1.1
11209 * Copyright(c) 2006-2007, Ext JS, LLC.
11211 * Originally Released Under LGPL - original licence link has changed is not relivant.
11214 * <script type="text/javascript">
11219 * @class Roo.tree.TreePanel
11220 * @extends Roo.data.Tree
11221 * @cfg {Roo.tree.TreeNode} root The root node
11222 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11223 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11224 * @cfg {Boolean} enableDD true to enable drag and drop
11225 * @cfg {Boolean} enableDrag true to enable just drag
11226 * @cfg {Boolean} enableDrop true to enable just drop
11227 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11228 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11229 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11230 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11231 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11232 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11233 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11234 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11235 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11236 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11237 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11238 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11239 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11240 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11241 * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11242 * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11245 * @param {String/HTMLElement/Element} el The container element
11246 * @param {Object} config
11248 Roo.tree.TreePanel = function(el, config){
11250 var loader = false;
11252 root = config.root;
11253 delete config.root;
11255 if (config.loader) {
11256 loader = config.loader;
11257 delete config.loader;
11260 Roo.apply(this, config);
11261 Roo.tree.TreePanel.superclass.constructor.call(this);
11262 this.el = Roo.get(el);
11263 this.el.addClass('x-tree');
11264 //console.log(root);
11266 this.setRootNode( Roo.factory(root, Roo.tree));
11269 this.loader = Roo.factory(loader, Roo.tree);
11272 * Read-only. The id of the container element becomes this TreePanel's id.
11274 this.id = this.el.id;
11277 * @event beforeload
11278 * Fires before a node is loaded, return false to cancel
11279 * @param {Node} node The node being loaded
11281 "beforeload" : true,
11284 * Fires when a node is loaded
11285 * @param {Node} node The node that was loaded
11289 * @event textchange
11290 * Fires when the text for a node is changed
11291 * @param {Node} node The node
11292 * @param {String} text The new text
11293 * @param {String} oldText The old text
11295 "textchange" : true,
11297 * @event beforeexpand
11298 * Fires before a node is expanded, return false to cancel.
11299 * @param {Node} node The node
11300 * @param {Boolean} deep
11301 * @param {Boolean} anim
11303 "beforeexpand" : true,
11305 * @event beforecollapse
11306 * Fires before a node is collapsed, return false to cancel.
11307 * @param {Node} node The node
11308 * @param {Boolean} deep
11309 * @param {Boolean} anim
11311 "beforecollapse" : true,
11314 * Fires when a node is expanded
11315 * @param {Node} node The node
11319 * @event disabledchange
11320 * Fires when the disabled status of a node changes
11321 * @param {Node} node The node
11322 * @param {Boolean} disabled
11324 "disabledchange" : true,
11327 * Fires when a node is collapsed
11328 * @param {Node} node The node
11332 * @event beforeclick
11333 * Fires before click processing on a node. Return false to cancel the default action.
11334 * @param {Node} node The node
11335 * @param {Roo.EventObject} e The event object
11337 "beforeclick":true,
11339 * @event checkchange
11340 * Fires when a node with a checkbox's checked property changes
11341 * @param {Node} this This node
11342 * @param {Boolean} checked
11344 "checkchange":true,
11347 * Fires when a node is clicked
11348 * @param {Node} node The node
11349 * @param {Roo.EventObject} e The event object
11354 * Fires when a node is double clicked
11355 * @param {Node} node The node
11356 * @param {Roo.EventObject} e The event object
11360 * @event contextmenu
11361 * Fires when a node is right clicked
11362 * @param {Node} node The node
11363 * @param {Roo.EventObject} e The event object
11365 "contextmenu":true,
11367 * @event beforechildrenrendered
11368 * Fires right before the child nodes for a node are rendered
11369 * @param {Node} node The node
11371 "beforechildrenrendered":true,
11374 * Fires when a node starts being dragged
11375 * @param {Roo.tree.TreePanel} this
11376 * @param {Roo.tree.TreeNode} node
11377 * @param {event} e The raw browser event
11379 "startdrag" : true,
11382 * Fires when a drag operation is complete
11383 * @param {Roo.tree.TreePanel} this
11384 * @param {Roo.tree.TreeNode} node
11385 * @param {event} e The raw browser event
11390 * Fires when a dragged node is dropped on a valid DD target
11391 * @param {Roo.tree.TreePanel} this
11392 * @param {Roo.tree.TreeNode} node
11393 * @param {DD} dd The dd it was dropped on
11394 * @param {event} e The raw browser event
11398 * @event beforenodedrop
11399 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11400 * passed to handlers has the following properties:<br />
11401 * <ul style="padding:5px;padding-left:16px;">
11402 * <li>tree - The TreePanel</li>
11403 * <li>target - The node being targeted for the drop</li>
11404 * <li>data - The drag data from the drag source</li>
11405 * <li>point - The point of the drop - append, above or below</li>
11406 * <li>source - The drag source</li>
11407 * <li>rawEvent - Raw mouse event</li>
11408 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11409 * to be inserted by setting them on this object.</li>
11410 * <li>cancel - Set this to true to cancel the drop.</li>
11412 * @param {Object} dropEvent
11414 "beforenodedrop" : true,
11417 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11418 * passed to handlers has the following properties:<br />
11419 * <ul style="padding:5px;padding-left:16px;">
11420 * <li>tree - The TreePanel</li>
11421 * <li>target - The node being targeted for the drop</li>
11422 * <li>data - The drag data from the drag source</li>
11423 * <li>point - The point of the drop - append, above or below</li>
11424 * <li>source - The drag source</li>
11425 * <li>rawEvent - Raw mouse event</li>
11426 * <li>dropNode - Dropped node(s).</li>
11428 * @param {Object} dropEvent
11432 * @event nodedragover
11433 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11434 * passed to handlers has the following properties:<br />
11435 * <ul style="padding:5px;padding-left:16px;">
11436 * <li>tree - The TreePanel</li>
11437 * <li>target - The node being targeted for the drop</li>
11438 * <li>data - The drag data from the drag source</li>
11439 * <li>point - The point of the drop - append, above or below</li>
11440 * <li>source - The drag source</li>
11441 * <li>rawEvent - Raw mouse event</li>
11442 * <li>dropNode - Drop node(s) provided by the source.</li>
11443 * <li>cancel - Set this to true to signal drop not allowed.</li>
11445 * @param {Object} dragOverEvent
11447 "nodedragover" : true,
11449 * @event appendnode
11450 * Fires when append node to the tree
11451 * @param {Roo.tree.TreePanel} this
11452 * @param {Roo.tree.TreeNode} node
11453 * @param {Number} index The index of the newly appended node
11455 "appendnode" : true
11458 if(this.singleExpand){
11459 this.on("beforeexpand", this.restrictExpand, this);
11462 this.editor.tree = this;
11463 this.editor = Roo.factory(this.editor, Roo.tree);
11466 if (this.selModel) {
11467 this.selModel = Roo.factory(this.selModel, Roo.tree);
11471 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11472 rootVisible : true,
11473 animate: Roo.enableFx,
11476 hlDrop : Roo.enableFx,
11480 rendererTip: false,
11482 restrictExpand : function(node){
11483 var p = node.parentNode;
11485 if(p.expandedChild && p.expandedChild.parentNode == p){
11486 p.expandedChild.collapse();
11488 p.expandedChild = node;
11492 // private override
11493 setRootNode : function(node){
11494 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11495 if(!this.rootVisible){
11496 node.ui = new Roo.tree.RootTreeNodeUI(node);
11502 * Returns the container element for this TreePanel
11504 getEl : function(){
11509 * Returns the default TreeLoader for this TreePanel
11511 getLoader : function(){
11512 return this.loader;
11518 expandAll : function(){
11519 this.root.expand(true);
11523 * Collapse all nodes
11525 collapseAll : function(){
11526 this.root.collapse(true);
11530 * Returns the selection model used by this TreePanel
11532 getSelectionModel : function(){
11533 if(!this.selModel){
11534 this.selModel = new Roo.tree.DefaultSelectionModel();
11536 return this.selModel;
11540 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11541 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11542 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11545 getChecked : function(a, startNode){
11546 startNode = startNode || this.root;
11548 var f = function(){
11549 if(this.attributes.checked){
11550 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11553 startNode.cascade(f);
11558 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11559 * @param {String} path
11560 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11561 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11562 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11564 expandPath : function(path, attr, callback){
11565 attr = attr || "id";
11566 var keys = path.split(this.pathSeparator);
11567 var curNode = this.root;
11568 if(curNode.attributes[attr] != keys[1]){ // invalid root
11570 callback(false, null);
11575 var f = function(){
11576 if(++index == keys.length){
11578 callback(true, curNode);
11582 var c = curNode.findChild(attr, keys[index]);
11585 callback(false, curNode);
11590 c.expand(false, false, f);
11592 curNode.expand(false, false, f);
11596 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11597 * @param {String} path
11598 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11599 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11600 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11602 selectPath : function(path, attr, callback){
11603 attr = attr || "id";
11604 var keys = path.split(this.pathSeparator);
11605 var v = keys.pop();
11606 if(keys.length > 0){
11607 var f = function(success, node){
11608 if(success && node){
11609 var n = node.findChild(attr, v);
11615 }else if(callback){
11616 callback(false, n);
11620 callback(false, n);
11624 this.expandPath(keys.join(this.pathSeparator), attr, f);
11626 this.root.select();
11628 callback(true, this.root);
11633 getTreeEl : function(){
11638 * Trigger rendering of this TreePanel
11640 render : function(){
11641 if (this.innerCt) {
11642 return this; // stop it rendering more than once!!
11645 this.innerCt = this.el.createChild({tag:"ul",
11646 cls:"x-tree-root-ct " +
11647 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11649 if(this.containerScroll){
11650 Roo.dd.ScrollManager.register(this.el);
11652 if((this.enableDD || this.enableDrop) && !this.dropZone){
11654 * The dropZone used by this tree if drop is enabled
11655 * @type Roo.tree.TreeDropZone
11657 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11658 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11661 if((this.enableDD || this.enableDrag) && !this.dragZone){
11663 * The dragZone used by this tree if drag is enabled
11664 * @type Roo.tree.TreeDragZone
11666 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11667 ddGroup: this.ddGroup || "TreeDD",
11668 scroll: this.ddScroll
11671 this.getSelectionModel().init(this);
11673 Roo.log("ROOT not set in tree");
11676 this.root.render();
11677 if(!this.rootVisible){
11678 this.root.renderChildren();
11684 * Ext JS Library 1.1.1
11685 * Copyright(c) 2006-2007, Ext JS, LLC.
11687 * Originally Released Under LGPL - original licence link has changed is not relivant.
11690 * <script type="text/javascript">
11695 * @class Roo.tree.DefaultSelectionModel
11696 * @extends Roo.util.Observable
11697 * The default single selection for a TreePanel.
11698 * @param {Object} cfg Configuration
11700 Roo.tree.DefaultSelectionModel = function(cfg){
11701 this.selNode = null;
11707 * @event selectionchange
11708 * Fires when the selected node changes
11709 * @param {DefaultSelectionModel} this
11710 * @param {TreeNode} node the new selection
11712 "selectionchange" : true,
11715 * @event beforeselect
11716 * Fires before the selected node changes, return false to cancel the change
11717 * @param {DefaultSelectionModel} this
11718 * @param {TreeNode} node the new selection
11719 * @param {TreeNode} node the old selection
11721 "beforeselect" : true
11724 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11727 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11728 init : function(tree){
11730 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11731 tree.on("click", this.onNodeClick, this);
11734 onNodeClick : function(node, e){
11735 if (e.ctrlKey && this.selNode == node) {
11736 this.unselect(node);
11744 * @param {TreeNode} node The node to select
11745 * @return {TreeNode} The selected node
11747 select : function(node){
11748 var last = this.selNode;
11749 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11751 last.ui.onSelectedChange(false);
11753 this.selNode = node;
11754 node.ui.onSelectedChange(true);
11755 this.fireEvent("selectionchange", this, node, last);
11762 * @param {TreeNode} node The node to unselect
11764 unselect : function(node){
11765 if(this.selNode == node){
11766 this.clearSelections();
11771 * Clear all selections
11773 clearSelections : function(){
11774 var n = this.selNode;
11776 n.ui.onSelectedChange(false);
11777 this.selNode = null;
11778 this.fireEvent("selectionchange", this, null);
11784 * Get the selected node
11785 * @return {TreeNode} The selected node
11787 getSelectedNode : function(){
11788 return this.selNode;
11792 * Returns true if the node is selected
11793 * @param {TreeNode} node The node to check
11794 * @return {Boolean}
11796 isSelected : function(node){
11797 return this.selNode == node;
11801 * Selects the node above the selected node in the tree, intelligently walking the nodes
11802 * @return TreeNode The new selection
11804 selectPrevious : function(){
11805 var s = this.selNode || this.lastSelNode;
11809 var ps = s.previousSibling;
11811 if(!ps.isExpanded() || ps.childNodes.length < 1){
11812 return this.select(ps);
11814 var lc = ps.lastChild;
11815 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11818 return this.select(lc);
11820 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11821 return this.select(s.parentNode);
11827 * Selects the node above the selected node in the tree, intelligently walking the nodes
11828 * @return TreeNode The new selection
11830 selectNext : function(){
11831 var s = this.selNode || this.lastSelNode;
11835 if(s.firstChild && s.isExpanded()){
11836 return this.select(s.firstChild);
11837 }else if(s.nextSibling){
11838 return this.select(s.nextSibling);
11839 }else if(s.parentNode){
11841 s.parentNode.bubble(function(){
11842 if(this.nextSibling){
11843 newS = this.getOwnerTree().selModel.select(this.nextSibling);
11852 onKeyDown : function(e){
11853 var s = this.selNode || this.lastSelNode;
11854 // undesirable, but required
11859 var k = e.getKey();
11867 this.selectPrevious();
11870 e.preventDefault();
11871 if(s.hasChildNodes()){
11872 if(!s.isExpanded()){
11874 }else if(s.firstChild){
11875 this.select(s.firstChild, e);
11880 e.preventDefault();
11881 if(s.hasChildNodes() && s.isExpanded()){
11883 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11884 this.select(s.parentNode, e);
11892 * @class Roo.tree.MultiSelectionModel
11893 * @extends Roo.util.Observable
11894 * Multi selection for a TreePanel.
11895 * @param {Object} cfg Configuration
11897 Roo.tree.MultiSelectionModel = function(){
11898 this.selNodes = [];
11902 * @event selectionchange
11903 * Fires when the selected nodes change
11904 * @param {MultiSelectionModel} this
11905 * @param {Array} nodes Array of the selected nodes
11907 "selectionchange" : true
11909 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11913 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11914 init : function(tree){
11916 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11917 tree.on("click", this.onNodeClick, this);
11920 onNodeClick : function(node, e){
11921 this.select(node, e, e.ctrlKey);
11926 * @param {TreeNode} node The node to select
11927 * @param {EventObject} e (optional) An event associated with the selection
11928 * @param {Boolean} keepExisting True to retain existing selections
11929 * @return {TreeNode} The selected node
11931 select : function(node, e, keepExisting){
11932 if(keepExisting !== true){
11933 this.clearSelections(true);
11935 if(this.isSelected(node)){
11936 this.lastSelNode = node;
11939 this.selNodes.push(node);
11940 this.selMap[node.id] = node;
11941 this.lastSelNode = node;
11942 node.ui.onSelectedChange(true);
11943 this.fireEvent("selectionchange", this, this.selNodes);
11949 * @param {TreeNode} node The node to unselect
11951 unselect : function(node){
11952 if(this.selMap[node.id]){
11953 node.ui.onSelectedChange(false);
11954 var sn = this.selNodes;
11957 index = sn.indexOf(node);
11959 for(var i = 0, len = sn.length; i < len; i++){
11967 this.selNodes.splice(index, 1);
11969 delete this.selMap[node.id];
11970 this.fireEvent("selectionchange", this, this.selNodes);
11975 * Clear all selections
11977 clearSelections : function(suppressEvent){
11978 var sn = this.selNodes;
11980 for(var i = 0, len = sn.length; i < len; i++){
11981 sn[i].ui.onSelectedChange(false);
11983 this.selNodes = [];
11985 if(suppressEvent !== true){
11986 this.fireEvent("selectionchange", this, this.selNodes);
11992 * Returns true if the node is selected
11993 * @param {TreeNode} node The node to check
11994 * @return {Boolean}
11996 isSelected : function(node){
11997 return this.selMap[node.id] ? true : false;
12001 * Returns an array of the selected nodes
12004 getSelectedNodes : function(){
12005 return this.selNodes;
12008 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12010 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12012 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12015 * Ext JS Library 1.1.1
12016 * Copyright(c) 2006-2007, Ext JS, LLC.
12018 * Originally Released Under LGPL - original licence link has changed is not relivant.
12021 * <script type="text/javascript">
12025 * @class Roo.tree.TreeNode
12026 * @extends Roo.data.Node
12027 * @cfg {String} text The text for this node
12028 * @cfg {Boolean} expanded true to start the node expanded
12029 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12030 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12031 * @cfg {Boolean} disabled true to start the node disabled
12032 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12033 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12034 * @cfg {String} cls A css class to be added to the node
12035 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12036 * @cfg {String} href URL of the link used for the node (defaults to #)
12037 * @cfg {String} hrefTarget target frame for the link
12038 * @cfg {String} qtip An Ext QuickTip for the node
12039 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12040 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12041 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12042 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12043 * (defaults to undefined with no checkbox rendered)
12045 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12047 Roo.tree.TreeNode = function(attributes){
12048 attributes = attributes || {};
12049 if(typeof attributes == "string"){
12050 attributes = {text: attributes};
12052 this.childrenRendered = false;
12053 this.rendered = false;
12054 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12055 this.expanded = attributes.expanded === true;
12056 this.isTarget = attributes.isTarget !== false;
12057 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12058 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12061 * Read-only. The text for this node. To change it use setText().
12064 this.text = attributes.text;
12066 * True if this node is disabled.
12069 this.disabled = attributes.disabled === true;
12073 * @event textchange
12074 * Fires when the text for this node is changed
12075 * @param {Node} this This node
12076 * @param {String} text The new text
12077 * @param {String} oldText The old text
12079 "textchange" : true,
12081 * @event beforeexpand
12082 * Fires before this node is expanded, return false to cancel.
12083 * @param {Node} this This node
12084 * @param {Boolean} deep
12085 * @param {Boolean} anim
12087 "beforeexpand" : true,
12089 * @event beforecollapse
12090 * Fires before this node is collapsed, return false to cancel.
12091 * @param {Node} this This node
12092 * @param {Boolean} deep
12093 * @param {Boolean} anim
12095 "beforecollapse" : true,
12098 * Fires when this node is expanded
12099 * @param {Node} this This node
12103 * @event disabledchange
12104 * Fires when the disabled status of this node changes
12105 * @param {Node} this This node
12106 * @param {Boolean} disabled
12108 "disabledchange" : true,
12111 * Fires when this node is collapsed
12112 * @param {Node} this This node
12116 * @event beforeclick
12117 * Fires before click processing. Return false to cancel the default action.
12118 * @param {Node} this This node
12119 * @param {Roo.EventObject} e The event object
12121 "beforeclick":true,
12123 * @event checkchange
12124 * Fires when a node with a checkbox's checked property changes
12125 * @param {Node} this This node
12126 * @param {Boolean} checked
12128 "checkchange":true,
12131 * Fires when this node is clicked
12132 * @param {Node} this This node
12133 * @param {Roo.EventObject} e The event object
12138 * Fires when this node is double clicked
12139 * @param {Node} this This node
12140 * @param {Roo.EventObject} e The event object
12144 * @event contextmenu
12145 * Fires when this node is right clicked
12146 * @param {Node} this This node
12147 * @param {Roo.EventObject} e The event object
12149 "contextmenu":true,
12151 * @event beforechildrenrendered
12152 * Fires right before the child nodes for this node are rendered
12153 * @param {Node} this This node
12155 "beforechildrenrendered":true
12158 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12161 * Read-only. The UI for this node
12164 this.ui = new uiClass(this);
12166 // finally support items[]
12167 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12172 Roo.each(this.attributes.items, function(c) {
12173 this.appendChild(Roo.factory(c,Roo.Tree));
12175 delete this.attributes.items;
12180 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12181 preventHScroll: true,
12183 * Returns true if this node is expanded
12184 * @return {Boolean}
12186 isExpanded : function(){
12187 return this.expanded;
12191 * Returns the UI object for this node
12192 * @return {TreeNodeUI}
12194 getUI : function(){
12198 // private override
12199 setFirstChild : function(node){
12200 var of = this.firstChild;
12201 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12202 if(this.childrenRendered && of && node != of){
12203 of.renderIndent(true, true);
12206 this.renderIndent(true, true);
12210 // private override
12211 setLastChild : function(node){
12212 var ol = this.lastChild;
12213 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12214 if(this.childrenRendered && ol && node != ol){
12215 ol.renderIndent(true, true);
12218 this.renderIndent(true, true);
12222 // these methods are overridden to provide lazy rendering support
12223 // private override
12224 appendChild : function()
12226 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12227 if(node && this.childrenRendered){
12230 this.ui.updateExpandIcon();
12234 // private override
12235 removeChild : function(node){
12236 this.ownerTree.getSelectionModel().unselect(node);
12237 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12238 // if it's been rendered remove dom node
12239 if(this.childrenRendered){
12242 if(this.childNodes.length < 1){
12243 this.collapse(false, false);
12245 this.ui.updateExpandIcon();
12247 if(!this.firstChild) {
12248 this.childrenRendered = false;
12253 // private override
12254 insertBefore : function(node, refNode){
12255 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12256 if(newNode && refNode && this.childrenRendered){
12259 this.ui.updateExpandIcon();
12264 * Sets the text for this node
12265 * @param {String} text
12267 setText : function(text){
12268 var oldText = this.text;
12270 this.attributes.text = text;
12271 if(this.rendered){ // event without subscribing
12272 this.ui.onTextChange(this, text, oldText);
12274 this.fireEvent("textchange", this, text, oldText);
12278 * Triggers selection of this node
12280 select : function(){
12281 this.getOwnerTree().getSelectionModel().select(this);
12285 * Triggers deselection of this node
12287 unselect : function(){
12288 this.getOwnerTree().getSelectionModel().unselect(this);
12292 * Returns true if this node is selected
12293 * @return {Boolean}
12295 isSelected : function(){
12296 return this.getOwnerTree().getSelectionModel().isSelected(this);
12300 * Expand this node.
12301 * @param {Boolean} deep (optional) True to expand all children as well
12302 * @param {Boolean} anim (optional) false to cancel the default animation
12303 * @param {Function} callback (optional) A callback to be called when
12304 * expanding this node completes (does not wait for deep expand to complete).
12305 * Called with 1 parameter, this node.
12307 expand : function(deep, anim, callback){
12308 if(!this.expanded){
12309 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12312 if(!this.childrenRendered){
12313 this.renderChildren();
12315 this.expanded = true;
12317 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12318 this.ui.animExpand(function(){
12319 this.fireEvent("expand", this);
12320 if(typeof callback == "function"){
12324 this.expandChildNodes(true);
12326 }.createDelegate(this));
12330 this.fireEvent("expand", this);
12331 if(typeof callback == "function"){
12336 if(typeof callback == "function"){
12341 this.expandChildNodes(true);
12345 isHiddenRoot : function(){
12346 return this.isRoot && !this.getOwnerTree().rootVisible;
12350 * Collapse this node.
12351 * @param {Boolean} deep (optional) True to collapse all children as well
12352 * @param {Boolean} anim (optional) false to cancel the default animation
12354 collapse : function(deep, anim){
12355 if(this.expanded && !this.isHiddenRoot()){
12356 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12359 this.expanded = false;
12360 if((this.getOwnerTree().animate && anim !== false) || anim){
12361 this.ui.animCollapse(function(){
12362 this.fireEvent("collapse", this);
12364 this.collapseChildNodes(true);
12366 }.createDelegate(this));
12369 this.ui.collapse();
12370 this.fireEvent("collapse", this);
12374 var cs = this.childNodes;
12375 for(var i = 0, len = cs.length; i < len; i++) {
12376 cs[i].collapse(true, false);
12382 delayedExpand : function(delay){
12383 if(!this.expandProcId){
12384 this.expandProcId = this.expand.defer(delay, this);
12389 cancelExpand : function(){
12390 if(this.expandProcId){
12391 clearTimeout(this.expandProcId);
12393 this.expandProcId = false;
12397 * Toggles expanded/collapsed state of the node
12399 toggle : function(){
12408 * Ensures all parent nodes are expanded
12410 ensureVisible : function(callback){
12411 var tree = this.getOwnerTree();
12412 tree.expandPath(this.parentNode.getPath(), false, function(){
12413 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12414 Roo.callback(callback);
12415 }.createDelegate(this));
12419 * Expand all child nodes
12420 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12422 expandChildNodes : function(deep){
12423 var cs = this.childNodes;
12424 for(var i = 0, len = cs.length; i < len; i++) {
12425 cs[i].expand(deep);
12430 * Collapse all child nodes
12431 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12433 collapseChildNodes : function(deep){
12434 var cs = this.childNodes;
12435 for(var i = 0, len = cs.length; i < len; i++) {
12436 cs[i].collapse(deep);
12441 * Disables this node
12443 disable : function(){
12444 this.disabled = true;
12446 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12447 this.ui.onDisableChange(this, true);
12449 this.fireEvent("disabledchange", this, true);
12453 * Enables this node
12455 enable : function(){
12456 this.disabled = false;
12457 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12458 this.ui.onDisableChange(this, false);
12460 this.fireEvent("disabledchange", this, false);
12464 renderChildren : function(suppressEvent){
12465 if(suppressEvent !== false){
12466 this.fireEvent("beforechildrenrendered", this);
12468 var cs = this.childNodes;
12469 for(var i = 0, len = cs.length; i < len; i++){
12470 cs[i].render(true);
12472 this.childrenRendered = true;
12476 sort : function(fn, scope){
12477 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12478 if(this.childrenRendered){
12479 var cs = this.childNodes;
12480 for(var i = 0, len = cs.length; i < len; i++){
12481 cs[i].render(true);
12487 render : function(bulkRender){
12488 this.ui.render(bulkRender);
12489 if(!this.rendered){
12490 this.rendered = true;
12492 this.expanded = false;
12493 this.expand(false, false);
12499 renderIndent : function(deep, refresh){
12501 this.ui.childIndent = null;
12503 this.ui.renderIndent();
12504 if(deep === true && this.childrenRendered){
12505 var cs = this.childNodes;
12506 for(var i = 0, len = cs.length; i < len; i++){
12507 cs[i].renderIndent(true, refresh);
12513 * Ext JS Library 1.1.1
12514 * Copyright(c) 2006-2007, Ext JS, LLC.
12516 * Originally Released Under LGPL - original licence link has changed is not relivant.
12519 * <script type="text/javascript">
12523 * @class Roo.tree.AsyncTreeNode
12524 * @extends Roo.tree.TreeNode
12525 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12527 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12529 Roo.tree.AsyncTreeNode = function(config){
12530 this.loaded = false;
12531 this.loading = false;
12532 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12534 * @event beforeload
12535 * Fires before this node is loaded, return false to cancel
12536 * @param {Node} this This node
12538 this.addEvents({'beforeload':true, 'load': true});
12541 * Fires when this node is loaded
12542 * @param {Node} this This node
12545 * The loader used by this node (defaults to using the tree's defined loader)
12550 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12551 expand : function(deep, anim, callback){
12552 if(this.loading){ // if an async load is already running, waiting til it's done
12554 var f = function(){
12555 if(!this.loading){ // done loading
12556 clearInterval(timer);
12557 this.expand(deep, anim, callback);
12559 }.createDelegate(this);
12560 timer = setInterval(f, 200);
12564 if(this.fireEvent("beforeload", this) === false){
12567 this.loading = true;
12568 this.ui.beforeLoad(this);
12569 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12571 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12575 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12579 * Returns true if this node is currently loading
12580 * @return {Boolean}
12582 isLoading : function(){
12583 return this.loading;
12586 loadComplete : function(deep, anim, callback){
12587 this.loading = false;
12588 this.loaded = true;
12589 this.ui.afterLoad(this);
12590 this.fireEvent("load", this);
12591 this.expand(deep, anim, callback);
12595 * Returns true if this node has been loaded
12596 * @return {Boolean}
12598 isLoaded : function(){
12599 return this.loaded;
12602 hasChildNodes : function(){
12603 if(!this.isLeaf() && !this.loaded){
12606 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12611 * Trigger a reload for this node
12612 * @param {Function} callback
12614 reload : function(callback){
12615 this.collapse(false, false);
12616 while(this.firstChild){
12617 this.removeChild(this.firstChild);
12619 this.childrenRendered = false;
12620 this.loaded = false;
12621 if(this.isHiddenRoot()){
12622 this.expanded = false;
12624 this.expand(false, false, callback);
12628 * Ext JS Library 1.1.1
12629 * Copyright(c) 2006-2007, Ext JS, LLC.
12631 * Originally Released Under LGPL - original licence link has changed is not relivant.
12634 * <script type="text/javascript">
12638 * @class Roo.tree.TreeNodeUI
12640 * @param {Object} node The node to render
12641 * The TreeNode UI implementation is separate from the
12642 * tree implementation. Unless you are customizing the tree UI,
12643 * you should never have to use this directly.
12645 Roo.tree.TreeNodeUI = function(node){
12647 this.rendered = false;
12648 this.animating = false;
12649 this.emptyIcon = Roo.BLANK_IMAGE_URL;
12652 Roo.tree.TreeNodeUI.prototype = {
12653 removeChild : function(node){
12655 this.ctNode.removeChild(node.ui.getEl());
12659 beforeLoad : function(){
12660 this.addClass("x-tree-node-loading");
12663 afterLoad : function(){
12664 this.removeClass("x-tree-node-loading");
12667 onTextChange : function(node, text, oldText){
12669 this.textNode.innerHTML = text;
12673 onDisableChange : function(node, state){
12674 this.disabled = state;
12676 this.addClass("x-tree-node-disabled");
12678 this.removeClass("x-tree-node-disabled");
12682 onSelectedChange : function(state){
12685 this.addClass("x-tree-selected");
12688 this.removeClass("x-tree-selected");
12692 onMove : function(tree, node, oldParent, newParent, index, refNode){
12693 this.childIndent = null;
12695 var targetNode = newParent.ui.getContainer();
12696 if(!targetNode){//target not rendered
12697 this.holder = document.createElement("div");
12698 this.holder.appendChild(this.wrap);
12701 var insertBefore = refNode ? refNode.ui.getEl() : null;
12703 targetNode.insertBefore(this.wrap, insertBefore);
12705 targetNode.appendChild(this.wrap);
12707 this.node.renderIndent(true);
12711 addClass : function(cls){
12713 Roo.fly(this.elNode).addClass(cls);
12717 removeClass : function(cls){
12719 Roo.fly(this.elNode).removeClass(cls);
12723 remove : function(){
12725 this.holder = document.createElement("div");
12726 this.holder.appendChild(this.wrap);
12730 fireEvent : function(){
12731 return this.node.fireEvent.apply(this.node, arguments);
12734 initEvents : function(){
12735 this.node.on("move", this.onMove, this);
12736 var E = Roo.EventManager;
12737 var a = this.anchor;
12739 var el = Roo.fly(a, '_treeui');
12741 if(Roo.isOpera){ // opera render bug ignores the CSS
12742 el.setStyle("text-decoration", "none");
12745 el.on("click", this.onClick, this);
12746 el.on("dblclick", this.onDblClick, this);
12749 Roo.EventManager.on(this.checkbox,
12750 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12753 el.on("contextmenu", this.onContextMenu, this);
12755 var icon = Roo.fly(this.iconNode);
12756 icon.on("click", this.onClick, this);
12757 icon.on("dblclick", this.onDblClick, this);
12758 icon.on("contextmenu", this.onContextMenu, this);
12759 E.on(this.ecNode, "click", this.ecClick, this, true);
12761 if(this.node.disabled){
12762 this.addClass("x-tree-node-disabled");
12764 if(this.node.hidden){
12765 this.addClass("x-tree-node-disabled");
12767 var ot = this.node.getOwnerTree();
12768 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12769 if(dd && (!this.node.isRoot || ot.rootVisible)){
12770 Roo.dd.Registry.register(this.elNode, {
12772 handles: this.getDDHandles(),
12778 getDDHandles : function(){
12779 return [this.iconNode, this.textNode];
12784 this.wrap.style.display = "none";
12790 this.wrap.style.display = "";
12794 onContextMenu : function(e){
12795 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12796 e.preventDefault();
12798 this.fireEvent("contextmenu", this.node, e);
12802 onClick : function(e){
12807 if(this.fireEvent("beforeclick", this.node, e) !== false){
12808 if(!this.disabled && this.node.attributes.href){
12809 this.fireEvent("click", this.node, e);
12812 e.preventDefault();
12817 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12818 this.node.toggle();
12821 this.fireEvent("click", this.node, e);
12827 onDblClick : function(e){
12828 e.preventDefault();
12833 this.toggleCheck();
12835 if(!this.animating && this.node.hasChildNodes()){
12836 this.node.toggle();
12838 this.fireEvent("dblclick", this.node, e);
12841 onCheckChange : function(){
12842 var checked = this.checkbox.checked;
12843 this.node.attributes.checked = checked;
12844 this.fireEvent('checkchange', this.node, checked);
12847 ecClick : function(e){
12848 if(!this.animating && this.node.hasChildNodes()){
12849 this.node.toggle();
12853 startDrop : function(){
12854 this.dropping = true;
12857 // delayed drop so the click event doesn't get fired on a drop
12858 endDrop : function(){
12859 setTimeout(function(){
12860 this.dropping = false;
12861 }.createDelegate(this), 50);
12864 expand : function(){
12865 this.updateExpandIcon();
12866 this.ctNode.style.display = "";
12869 focus : function(){
12870 if(!this.node.preventHScroll){
12871 try{this.anchor.focus();
12873 }else if(!Roo.isIE){
12875 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12876 var l = noscroll.scrollLeft;
12877 this.anchor.focus();
12878 noscroll.scrollLeft = l;
12883 toggleCheck : function(value){
12884 var cb = this.checkbox;
12886 cb.checked = (value === undefined ? !cb.checked : value);
12892 this.anchor.blur();
12896 animExpand : function(callback){
12897 var ct = Roo.get(this.ctNode);
12899 if(!this.node.hasChildNodes()){
12900 this.updateExpandIcon();
12901 this.ctNode.style.display = "";
12902 Roo.callback(callback);
12905 this.animating = true;
12906 this.updateExpandIcon();
12909 callback : function(){
12910 this.animating = false;
12911 Roo.callback(callback);
12914 duration: this.node.ownerTree.duration || .25
12918 highlight : function(){
12919 var tree = this.node.getOwnerTree();
12920 Roo.fly(this.wrap).highlight(
12921 tree.hlColor || "C3DAF9",
12922 {endColor: tree.hlBaseColor}
12926 collapse : function(){
12927 this.updateExpandIcon();
12928 this.ctNode.style.display = "none";
12931 animCollapse : function(callback){
12932 var ct = Roo.get(this.ctNode);
12933 ct.enableDisplayMode('block');
12936 this.animating = true;
12937 this.updateExpandIcon();
12940 callback : function(){
12941 this.animating = false;
12942 Roo.callback(callback);
12945 duration: this.node.ownerTree.duration || .25
12949 getContainer : function(){
12950 return this.ctNode;
12953 getEl : function(){
12957 appendDDGhost : function(ghostNode){
12958 ghostNode.appendChild(this.elNode.cloneNode(true));
12961 getDDRepairXY : function(){
12962 return Roo.lib.Dom.getXY(this.iconNode);
12965 onRender : function(){
12969 render : function(bulkRender){
12970 var n = this.node, a = n.attributes;
12971 var targetNode = n.parentNode ?
12972 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12974 if(!this.rendered){
12975 this.rendered = true;
12977 this.renderElements(n, a, targetNode, bulkRender);
12980 if(this.textNode.setAttributeNS){
12981 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12983 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12986 this.textNode.setAttribute("ext:qtip", a.qtip);
12988 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12991 }else if(a.qtipCfg){
12992 a.qtipCfg.target = Roo.id(this.textNode);
12993 Roo.QuickTips.register(a.qtipCfg);
12996 if(!this.node.expanded){
12997 this.updateExpandIcon();
13000 if(bulkRender === true) {
13001 targetNode.appendChild(this.wrap);
13006 renderElements : function(n, a, targetNode, bulkRender)
13008 // add some indent caching, this helps performance when rendering a large tree
13009 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13010 var t = n.getOwnerTree();
13011 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13012 if (typeof(n.attributes.html) != 'undefined') {
13013 txt = n.attributes.html;
13015 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13016 var cb = typeof a.checked == 'boolean';
13017 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13018 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13019 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13020 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13021 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13022 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13023 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13024 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13025 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13026 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13029 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13030 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13031 n.nextSibling.ui.getEl(), buf.join(""));
13033 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13036 this.elNode = this.wrap.childNodes[0];
13037 this.ctNode = this.wrap.childNodes[1];
13038 var cs = this.elNode.childNodes;
13039 this.indentNode = cs[0];
13040 this.ecNode = cs[1];
13041 this.iconNode = cs[2];
13044 this.checkbox = cs[3];
13047 this.anchor = cs[index];
13048 this.textNode = cs[index].firstChild;
13051 getAnchor : function(){
13052 return this.anchor;
13055 getTextEl : function(){
13056 return this.textNode;
13059 getIconEl : function(){
13060 return this.iconNode;
13063 isChecked : function(){
13064 return this.checkbox ? this.checkbox.checked : false;
13067 updateExpandIcon : function(){
13069 var n = this.node, c1, c2;
13070 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13071 var hasChild = n.hasChildNodes();
13075 c1 = "x-tree-node-collapsed";
13076 c2 = "x-tree-node-expanded";
13079 c1 = "x-tree-node-expanded";
13080 c2 = "x-tree-node-collapsed";
13083 this.removeClass("x-tree-node-leaf");
13084 this.wasLeaf = false;
13086 if(this.c1 != c1 || this.c2 != c2){
13087 Roo.fly(this.elNode).replaceClass(c1, c2);
13088 this.c1 = c1; this.c2 = c2;
13091 // this changes non-leafs into leafs if they have no children.
13092 // it's not very rational behaviour..
13094 if(!this.wasLeaf && this.node.leaf){
13095 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13098 this.wasLeaf = true;
13101 var ecc = "x-tree-ec-icon "+cls;
13102 if(this.ecc != ecc){
13103 this.ecNode.className = ecc;
13109 getChildIndent : function(){
13110 if(!this.childIndent){
13114 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13116 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13118 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13123 this.childIndent = buf.join("");
13125 return this.childIndent;
13128 renderIndent : function(){
13131 var p = this.node.parentNode;
13133 indent = p.ui.getChildIndent();
13135 if(this.indentMarkup != indent){ // don't rerender if not required
13136 this.indentNode.innerHTML = indent;
13137 this.indentMarkup = indent;
13139 this.updateExpandIcon();
13144 Roo.tree.RootTreeNodeUI = function(){
13145 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13147 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13148 render : function(){
13149 if(!this.rendered){
13150 var targetNode = this.node.ownerTree.innerCt.dom;
13151 this.node.expanded = true;
13152 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13153 this.wrap = this.ctNode = targetNode.firstChild;
13156 collapse : function(){
13158 expand : function(){
13162 * Ext JS Library 1.1.1
13163 * Copyright(c) 2006-2007, Ext JS, LLC.
13165 * Originally Released Under LGPL - original licence link has changed is not relivant.
13168 * <script type="text/javascript">
13171 * @class Roo.tree.TreeLoader
13172 * @extends Roo.util.Observable
13173 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13174 * nodes from a specified URL. The response must be a javascript Array definition
13175 * who's elements are node definition objects. eg:
13180 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13181 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13188 * The old style respose with just an array is still supported, but not recommended.
13191 * A server request is sent, and child nodes are loaded only when a node is expanded.
13192 * The loading node's id is passed to the server under the parameter name "node" to
13193 * enable the server to produce the correct child nodes.
13195 * To pass extra parameters, an event handler may be attached to the "beforeload"
13196 * event, and the parameters specified in the TreeLoader's baseParams property:
13198 myTreeLoader.on("beforeload", function(treeLoader, node) {
13199 this.baseParams.category = node.attributes.category;
13204 * This would pass an HTTP parameter called "category" to the server containing
13205 * the value of the Node's "category" attribute.
13207 * Creates a new Treeloader.
13208 * @param {Object} config A config object containing config properties.
13210 Roo.tree.TreeLoader = function(config){
13211 this.baseParams = {};
13212 this.requestMethod = "POST";
13213 Roo.apply(this, config);
13218 * @event beforeload
13219 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13220 * @param {Object} This TreeLoader object.
13221 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13222 * @param {Object} callback The callback function specified in the {@link #load} call.
13227 * Fires when the node has been successfuly loaded.
13228 * @param {Object} This TreeLoader object.
13229 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13230 * @param {Object} response The response object containing the data from the server.
13234 * @event loadexception
13235 * Fires if the network request failed.
13236 * @param {Object} This TreeLoader object.
13237 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13238 * @param {Object} response The response object containing the data from the server.
13240 loadexception : true,
13243 * Fires before a node is created, enabling you to return custom Node types
13244 * @param {Object} This TreeLoader object.
13245 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13250 Roo.tree.TreeLoader.superclass.constructor.call(this);
13253 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13255 * @cfg {String} dataUrl The URL from which to request a Json string which
13256 * specifies an array of node definition object representing the child nodes
13260 * @cfg {String} requestMethod either GET or POST
13261 * defaults to POST (due to BC)
13265 * @cfg {Object} baseParams (optional) An object containing properties which
13266 * specify HTTP parameters to be passed to each request for child nodes.
13269 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13270 * created by this loader. If the attributes sent by the server have an attribute in this object,
13271 * they take priority.
13274 * @cfg {Object} uiProviders (optional) An object containing properties which
13276 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13277 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13278 * <i>uiProvider</i> attribute of a returned child node is a string rather
13279 * than a reference to a TreeNodeUI implementation, this that string value
13280 * is used as a property name in the uiProviders object. You can define the provider named
13281 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13286 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13287 * child nodes before loading.
13289 clearOnLoad : true,
13292 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13293 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13294 * Grid query { data : [ .....] }
13299 * @cfg {String} queryParam (optional)
13300 * Name of the query as it will be passed on the querystring (defaults to 'node')
13301 * eg. the request will be ?node=[id]
13308 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13309 * This is called automatically when a node is expanded, but may be used to reload
13310 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13311 * @param {Roo.tree.TreeNode} node
13312 * @param {Function} callback
13314 load : function(node, callback){
13315 if(this.clearOnLoad){
13316 while(node.firstChild){
13317 node.removeChild(node.firstChild);
13320 if(node.attributes.children){ // preloaded json children
13321 var cs = node.attributes.children;
13322 for(var i = 0, len = cs.length; i < len; i++){
13323 node.appendChild(this.createNode(cs[i]));
13325 if(typeof callback == "function"){
13328 }else if(this.dataUrl){
13329 this.requestData(node, callback);
13333 getParams: function(node){
13334 var buf = [], bp = this.baseParams;
13335 for(var key in bp){
13336 if(typeof bp[key] != "function"){
13337 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13340 var n = this.queryParam === false ? 'node' : this.queryParam;
13341 buf.push(n + "=", encodeURIComponent(node.id));
13342 return buf.join("");
13345 requestData : function(node, callback){
13346 if(this.fireEvent("beforeload", this, node, callback) !== false){
13347 this.transId = Roo.Ajax.request({
13348 method:this.requestMethod,
13349 url: this.dataUrl||this.url,
13350 success: this.handleResponse,
13351 failure: this.handleFailure,
13353 argument: {callback: callback, node: node},
13354 params: this.getParams(node)
13357 // if the load is cancelled, make sure we notify
13358 // the node that we are done
13359 if(typeof callback == "function"){
13365 isLoading : function(){
13366 return this.transId ? true : false;
13369 abort : function(){
13370 if(this.isLoading()){
13371 Roo.Ajax.abort(this.transId);
13376 createNode : function(attr)
13378 // apply baseAttrs, nice idea Corey!
13379 if(this.baseAttrs){
13380 Roo.applyIf(attr, this.baseAttrs);
13382 if(this.applyLoader !== false){
13383 attr.loader = this;
13385 // uiProvider = depreciated..
13387 if(typeof(attr.uiProvider) == 'string'){
13388 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13389 /** eval:var:attr */ eval(attr.uiProvider);
13391 if(typeof(this.uiProviders['default']) != 'undefined') {
13392 attr.uiProvider = this.uiProviders['default'];
13395 this.fireEvent('create', this, attr);
13397 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13399 new Roo.tree.TreeNode(attr) :
13400 new Roo.tree.AsyncTreeNode(attr));
13403 processResponse : function(response, node, callback)
13405 var json = response.responseText;
13408 var o = Roo.decode(json);
13410 if (this.root === false && typeof(o.success) != undefined) {
13411 this.root = 'data'; // the default behaviour for list like data..
13414 if (this.root !== false && !o.success) {
13415 // it's a failure condition.
13416 var a = response.argument;
13417 this.fireEvent("loadexception", this, a.node, response);
13418 Roo.log("Load failed - should have a handler really");
13424 if (this.root !== false) {
13428 for(var i = 0, len = o.length; i < len; i++){
13429 var n = this.createNode(o[i]);
13431 node.appendChild(n);
13434 if(typeof callback == "function"){
13435 callback(this, node);
13438 this.handleFailure(response);
13442 handleResponse : function(response){
13443 this.transId = false;
13444 var a = response.argument;
13445 this.processResponse(response, a.node, a.callback);
13446 this.fireEvent("load", this, a.node, response);
13449 handleFailure : function(response)
13451 // should handle failure better..
13452 this.transId = false;
13453 var a = response.argument;
13454 this.fireEvent("loadexception", this, a.node, response);
13455 if(typeof a.callback == "function"){
13456 a.callback(this, a.node);
13461 * Ext JS Library 1.1.1
13462 * Copyright(c) 2006-2007, Ext JS, LLC.
13464 * Originally Released Under LGPL - original licence link has changed is not relivant.
13467 * <script type="text/javascript">
13471 * @class Roo.tree.TreeFilter
13472 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13473 * @param {TreePanel} tree
13474 * @param {Object} config (optional)
13476 Roo.tree.TreeFilter = function(tree, config){
13478 this.filtered = {};
13479 Roo.apply(this, config);
13482 Roo.tree.TreeFilter.prototype = {
13489 * Filter the data by a specific attribute.
13490 * @param {String/RegExp} value Either string that the attribute value
13491 * should start with or a RegExp to test against the attribute
13492 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13493 * @param {TreeNode} startNode (optional) The node to start the filter at.
13495 filter : function(value, attr, startNode){
13496 attr = attr || "text";
13498 if(typeof value == "string"){
13499 var vlen = value.length;
13500 // auto clear empty filter
13501 if(vlen == 0 && this.clearBlank){
13505 value = value.toLowerCase();
13507 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13509 }else if(value.exec){ // regex?
13511 return value.test(n.attributes[attr]);
13514 throw 'Illegal filter type, must be string or regex';
13516 this.filterBy(f, null, startNode);
13520 * Filter by a function. The passed function will be called with each
13521 * node in the tree (or from the startNode). If the function returns true, the node is kept
13522 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13523 * @param {Function} fn The filter function
13524 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13526 filterBy : function(fn, scope, startNode){
13527 startNode = startNode || this.tree.root;
13528 if(this.autoClear){
13531 var af = this.filtered, rv = this.reverse;
13532 var f = function(n){
13533 if(n == startNode){
13539 var m = fn.call(scope || n, n);
13547 startNode.cascade(f);
13550 if(typeof id != "function"){
13552 if(n && n.parentNode){
13553 n.parentNode.removeChild(n);
13561 * Clears the current filter. Note: with the "remove" option
13562 * set a filter cannot be cleared.
13564 clear : function(){
13566 var af = this.filtered;
13568 if(typeof id != "function"){
13575 this.filtered = {};
13580 * Ext JS Library 1.1.1
13581 * Copyright(c) 2006-2007, Ext JS, LLC.
13583 * Originally Released Under LGPL - original licence link has changed is not relivant.
13586 * <script type="text/javascript">
13591 * @class Roo.tree.TreeSorter
13592 * Provides sorting of nodes in a TreePanel
13594 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13595 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13596 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13597 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13598 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13599 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13601 * @param {TreePanel} tree
13602 * @param {Object} config
13604 Roo.tree.TreeSorter = function(tree, config){
13605 Roo.apply(this, config);
13606 tree.on("beforechildrenrendered", this.doSort, this);
13607 tree.on("append", this.updateSort, this);
13608 tree.on("insert", this.updateSort, this);
13610 var dsc = this.dir && this.dir.toLowerCase() == "desc";
13611 var p = this.property || "text";
13612 var sortType = this.sortType;
13613 var fs = this.folderSort;
13614 var cs = this.caseSensitive === true;
13615 var leafAttr = this.leafAttr || 'leaf';
13617 this.sortFn = function(n1, n2){
13619 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13622 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13626 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13627 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13629 return dsc ? +1 : -1;
13631 return dsc ? -1 : +1;
13638 Roo.tree.TreeSorter.prototype = {
13639 doSort : function(node){
13640 node.sort(this.sortFn);
13643 compareNodes : function(n1, n2){
13644 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13647 updateSort : function(tree, node){
13648 if(node.childrenRendered){
13649 this.doSort.defer(1, this, [node]);
13654 * Ext JS Library 1.1.1
13655 * Copyright(c) 2006-2007, Ext JS, LLC.
13657 * Originally Released Under LGPL - original licence link has changed is not relivant.
13660 * <script type="text/javascript">
13663 if(Roo.dd.DropZone){
13665 Roo.tree.TreeDropZone = function(tree, config){
13666 this.allowParentInsert = false;
13667 this.allowContainerDrop = false;
13668 this.appendOnly = false;
13669 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13671 this.lastInsertClass = "x-tree-no-status";
13672 this.dragOverData = {};
13675 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13676 ddGroup : "TreeDD",
13679 expandDelay : 1000,
13681 expandNode : function(node){
13682 if(node.hasChildNodes() && !node.isExpanded()){
13683 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13687 queueExpand : function(node){
13688 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13691 cancelExpand : function(){
13692 if(this.expandProcId){
13693 clearTimeout(this.expandProcId);
13694 this.expandProcId = false;
13698 isValidDropPoint : function(n, pt, dd, e, data){
13699 if(!n || !data){ return false; }
13700 var targetNode = n.node;
13701 var dropNode = data.node;
13702 // default drop rules
13703 if(!(targetNode && targetNode.isTarget && pt)){
13706 if(pt == "append" && targetNode.allowChildren === false){
13709 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13712 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13715 // reuse the object
13716 var overEvent = this.dragOverData;
13717 overEvent.tree = this.tree;
13718 overEvent.target = targetNode;
13719 overEvent.data = data;
13720 overEvent.point = pt;
13721 overEvent.source = dd;
13722 overEvent.rawEvent = e;
13723 overEvent.dropNode = dropNode;
13724 overEvent.cancel = false;
13725 var result = this.tree.fireEvent("nodedragover", overEvent);
13726 return overEvent.cancel === false && result !== false;
13729 getDropPoint : function(e, n, dd)
13733 return tn.allowChildren !== false ? "append" : false; // always append for root
13735 var dragEl = n.ddel;
13736 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13737 var y = Roo.lib.Event.getPageY(e);
13738 //var noAppend = tn.allowChildren === false || tn.isLeaf();
13740 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13741 var noAppend = tn.allowChildren === false;
13742 if(this.appendOnly || tn.parentNode.allowChildren === false){
13743 return noAppend ? false : "append";
13745 var noBelow = false;
13746 if(!this.allowParentInsert){
13747 noBelow = tn.hasChildNodes() && tn.isExpanded();
13749 var q = (b - t) / (noAppend ? 2 : 3);
13750 if(y >= t && y < (t + q)){
13752 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13759 onNodeEnter : function(n, dd, e, data)
13761 this.cancelExpand();
13764 onNodeOver : function(n, dd, e, data)
13767 var pt = this.getDropPoint(e, n, dd);
13770 // auto node expand check
13771 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13772 this.queueExpand(node);
13773 }else if(pt != "append"){
13774 this.cancelExpand();
13777 // set the insert point style on the target node
13778 var returnCls = this.dropNotAllowed;
13779 if(this.isValidDropPoint(n, pt, dd, e, data)){
13784 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13785 cls = "x-tree-drag-insert-above";
13786 }else if(pt == "below"){
13787 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13788 cls = "x-tree-drag-insert-below";
13790 returnCls = "x-tree-drop-ok-append";
13791 cls = "x-tree-drag-append";
13793 if(this.lastInsertClass != cls){
13794 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13795 this.lastInsertClass = cls;
13802 onNodeOut : function(n, dd, e, data){
13804 this.cancelExpand();
13805 this.removeDropIndicators(n);
13808 onNodeDrop : function(n, dd, e, data){
13809 var point = this.getDropPoint(e, n, dd);
13810 var targetNode = n.node;
13811 targetNode.ui.startDrop();
13812 if(!this.isValidDropPoint(n, point, dd, e, data)){
13813 targetNode.ui.endDrop();
13816 // first try to find the drop node
13817 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13820 target: targetNode,
13825 dropNode: dropNode,
13828 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13829 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13830 targetNode.ui.endDrop();
13833 // allow target changing
13834 targetNode = dropEvent.target;
13835 if(point == "append" && !targetNode.isExpanded()){
13836 targetNode.expand(false, null, function(){
13837 this.completeDrop(dropEvent);
13838 }.createDelegate(this));
13840 this.completeDrop(dropEvent);
13845 completeDrop : function(de){
13846 var ns = de.dropNode, p = de.point, t = de.target;
13847 if(!(ns instanceof Array)){
13851 for(var i = 0, len = ns.length; i < len; i++){
13854 t.parentNode.insertBefore(n, t);
13855 }else if(p == "below"){
13856 t.parentNode.insertBefore(n, t.nextSibling);
13862 if(this.tree.hlDrop){
13866 this.tree.fireEvent("nodedrop", de);
13869 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13870 if(this.tree.hlDrop){
13871 dropNode.ui.focus();
13872 dropNode.ui.highlight();
13874 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13877 getTree : function(){
13881 removeDropIndicators : function(n){
13884 Roo.fly(el).removeClass([
13885 "x-tree-drag-insert-above",
13886 "x-tree-drag-insert-below",
13887 "x-tree-drag-append"]);
13888 this.lastInsertClass = "_noclass";
13892 beforeDragDrop : function(target, e, id){
13893 this.cancelExpand();
13897 afterRepair : function(data){
13898 if(data && Roo.enableFx){
13899 data.node.ui.highlight();
13909 * Ext JS Library 1.1.1
13910 * Copyright(c) 2006-2007, Ext JS, LLC.
13912 * Originally Released Under LGPL - original licence link has changed is not relivant.
13915 * <script type="text/javascript">
13919 if(Roo.dd.DragZone){
13920 Roo.tree.TreeDragZone = function(tree, config){
13921 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13925 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13926 ddGroup : "TreeDD",
13928 onBeforeDrag : function(data, e){
13930 return n && n.draggable && !n.disabled;
13934 onInitDrag : function(e){
13935 var data = this.dragData;
13936 this.tree.getSelectionModel().select(data.node);
13937 this.proxy.update("");
13938 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13939 this.tree.fireEvent("startdrag", this.tree, data.node, e);
13942 getRepairXY : function(e, data){
13943 return data.node.ui.getDDRepairXY();
13946 onEndDrag : function(data, e){
13947 this.tree.fireEvent("enddrag", this.tree, data.node, e);
13952 onValidDrop : function(dd, e, id){
13953 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13957 beforeInvalidDrop : function(e, id){
13958 // this scrolls the original position back into view
13959 var sm = this.tree.getSelectionModel();
13960 sm.clearSelections();
13961 sm.select(this.dragData.node);
13966 * Ext JS Library 1.1.1
13967 * Copyright(c) 2006-2007, Ext JS, LLC.
13969 * Originally Released Under LGPL - original licence link has changed is not relivant.
13972 * <script type="text/javascript">
13975 * @class Roo.tree.TreeEditor
13976 * @extends Roo.Editor
13977 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
13978 * as the editor field.
13980 * @param {Object} config (used to be the tree panel.)
13981 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13983 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13984 * @cfg {Roo.form.TextField} field [required] The field configuration
13988 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13991 if (oldconfig) { // old style..
13992 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13995 tree = config.tree;
13996 config.field = config.field || {};
13997 config.field.xtype = 'TextField';
13998 field = Roo.factory(config.field, Roo.form);
14000 config = config || {};
14005 * @event beforenodeedit
14006 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14007 * false from the handler of this event.
14008 * @param {Editor} this
14009 * @param {Roo.tree.Node} node
14011 "beforenodeedit" : true
14015 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14019 tree.on('beforeclick', this.beforeNodeClick, this);
14020 tree.getTreeEl().on('mousedown', this.hide, this);
14021 this.on('complete', this.updateNode, this);
14022 this.on('beforestartedit', this.fitToTree, this);
14023 this.on('startedit', this.bindScroll, this, {delay:10});
14024 this.on('specialkey', this.onSpecialKey, this);
14027 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14029 * @cfg {String} alignment
14030 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14036 * @cfg {Boolean} hideEl
14037 * True to hide the bound element while the editor is displayed (defaults to false)
14041 * @cfg {String} cls
14042 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14044 cls: "x-small-editor x-tree-editor",
14046 * @cfg {Boolean} shim
14047 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14053 * @cfg {Number} maxWidth
14054 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14055 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14056 * scroll and client offsets into account prior to each edit.
14063 fitToTree : function(ed, el){
14064 var td = this.tree.getTreeEl().dom, nd = el.dom;
14065 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14066 td.scrollLeft = nd.offsetLeft;
14070 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14071 this.setSize(w, '');
14073 return this.fireEvent('beforenodeedit', this, this.editNode);
14078 triggerEdit : function(node){
14079 this.completeEdit();
14080 this.editNode = node;
14081 this.startEdit(node.ui.textNode, node.text);
14085 bindScroll : function(){
14086 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14090 beforeNodeClick : function(node, e){
14091 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14092 this.lastClick = new Date();
14093 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14095 this.triggerEdit(node);
14102 updateNode : function(ed, value){
14103 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14104 this.editNode.setText(value);
14108 onHide : function(){
14109 Roo.tree.TreeEditor.superclass.onHide.call(this);
14111 this.editNode.ui.focus();
14116 onSpecialKey : function(field, e){
14117 var k = e.getKey();
14121 }else if(k == e.ENTER && !e.hasModifier()){
14123 this.completeEdit();
14126 });//<Script type="text/javascript">
14129 * Ext JS Library 1.1.1
14130 * Copyright(c) 2006-2007, Ext JS, LLC.
14132 * Originally Released Under LGPL - original licence link has changed is not relivant.
14135 * <script type="text/javascript">
14139 * Not documented??? - probably should be...
14142 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14143 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14145 renderElements : function(n, a, targetNode, bulkRender){
14146 //consel.log("renderElements?");
14147 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14149 var t = n.getOwnerTree();
14150 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14152 var cols = t.columns;
14153 var bw = t.borderWidth;
14155 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14156 var cb = typeof a.checked == "boolean";
14157 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14158 var colcls = 'x-t-' + tid + '-c0';
14160 '<li class="x-tree-node">',
14163 '<div class="x-tree-node-el ', a.cls,'">',
14165 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14168 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14169 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14170 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14171 (a.icon ? ' x-tree-node-inline-icon' : ''),
14172 (a.iconCls ? ' '+a.iconCls : ''),
14173 '" unselectable="on" />',
14174 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14175 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14177 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14178 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14179 '<span unselectable="on" qtip="' + tx + '">',
14183 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14184 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14186 for(var i = 1, len = cols.length; i < len; i++){
14188 colcls = 'x-t-' + tid + '-c' +i;
14189 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14190 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14191 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14197 '<div class="x-clear"></div></div>',
14198 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14201 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14202 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14203 n.nextSibling.ui.getEl(), buf.join(""));
14205 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14207 var el = this.wrap.firstChild;
14209 this.elNode = el.firstChild;
14210 this.ranchor = el.childNodes[1];
14211 this.ctNode = this.wrap.childNodes[1];
14212 var cs = el.firstChild.childNodes;
14213 this.indentNode = cs[0];
14214 this.ecNode = cs[1];
14215 this.iconNode = cs[2];
14218 this.checkbox = cs[3];
14221 this.anchor = cs[index];
14223 this.textNode = cs[index].firstChild;
14225 //el.on("click", this.onClick, this);
14226 //el.on("dblclick", this.onDblClick, this);
14229 // console.log(this);
14231 initEvents : function(){
14232 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14235 var a = this.ranchor;
14237 var el = Roo.get(a);
14239 if(Roo.isOpera){ // opera render bug ignores the CSS
14240 el.setStyle("text-decoration", "none");
14243 el.on("click", this.onClick, this);
14244 el.on("dblclick", this.onDblClick, this);
14245 el.on("contextmenu", this.onContextMenu, this);
14249 /*onSelectedChange : function(state){
14252 this.addClass("x-tree-selected");
14255 this.removeClass("x-tree-selected");
14258 addClass : function(cls){
14260 Roo.fly(this.elRow).addClass(cls);
14266 removeClass : function(cls){
14268 Roo.fly(this.elRow).removeClass(cls);
14274 });//<Script type="text/javascript">
14278 * Ext JS Library 1.1.1
14279 * Copyright(c) 2006-2007, Ext JS, LLC.
14281 * Originally Released Under LGPL - original licence link has changed is not relivant.
14284 * <script type="text/javascript">
14289 * @class Roo.tree.ColumnTree
14290 * @extends Roo.data.TreePanel
14291 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14292 * @cfg {int} borderWidth compined right/left border allowance
14294 * @param {String/HTMLElement/Element} el The container element
14295 * @param {Object} config
14297 Roo.tree.ColumnTree = function(el, config)
14299 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14303 * Fire this event on a container when it resizes
14304 * @param {int} w Width
14305 * @param {int} h Height
14309 this.on('resize', this.onResize, this);
14312 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14316 borderWidth: Roo.isBorderBox ? 0 : 2,
14319 render : function(){
14320 // add the header.....
14322 Roo.tree.ColumnTree.superclass.render.apply(this);
14324 this.el.addClass('x-column-tree');
14326 this.headers = this.el.createChild(
14327 {cls:'x-tree-headers'},this.innerCt.dom);
14329 var cols = this.columns, c;
14330 var totalWidth = 0;
14332 var len = cols.length;
14333 for(var i = 0; i < len; i++){
14335 totalWidth += c.width;
14336 this.headEls.push(this.headers.createChild({
14337 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14339 cls:'x-tree-hd-text',
14342 style:'width:'+(c.width-this.borderWidth)+'px;'
14345 this.headers.createChild({cls:'x-clear'});
14346 // prevent floats from wrapping when clipped
14347 this.headers.setWidth(totalWidth);
14348 //this.innerCt.setWidth(totalWidth);
14349 this.innerCt.setStyle({ overflow: 'auto' });
14350 this.onResize(this.width, this.height);
14354 onResize : function(w,h)
14359 this.innerCt.setWidth(this.width);
14360 this.innerCt.setHeight(this.height-20);
14363 var cols = this.columns, c;
14364 var totalWidth = 0;
14366 var len = cols.length;
14367 for(var i = 0; i < len; i++){
14369 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14370 // it's the expander..
14371 expEl = this.headEls[i];
14374 totalWidth += c.width;
14378 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14380 this.headers.setWidth(w-20);
14389 * Ext JS Library 1.1.1
14390 * Copyright(c) 2006-2007, Ext JS, LLC.
14392 * Originally Released Under LGPL - original licence link has changed is not relivant.
14395 * <script type="text/javascript">
14399 * @class Roo.menu.Menu
14400 * @extends Roo.util.Observable
14401 * @children Roo.menu.BaseItem
14402 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14403 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14405 * Creates a new Menu
14406 * @param {Object} config Configuration options
14408 Roo.menu.Menu = function(config){
14410 Roo.menu.Menu.superclass.constructor.call(this, config);
14412 this.id = this.id || Roo.id();
14415 * @event beforeshow
14416 * Fires before this menu is displayed
14417 * @param {Roo.menu.Menu} this
14421 * @event beforehide
14422 * Fires before this menu is hidden
14423 * @param {Roo.menu.Menu} this
14428 * Fires after this menu is displayed
14429 * @param {Roo.menu.Menu} this
14434 * Fires after this menu is hidden
14435 * @param {Roo.menu.Menu} this
14440 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14441 * @param {Roo.menu.Menu} this
14442 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14443 * @param {Roo.EventObject} e
14448 * Fires when the mouse is hovering over this menu
14449 * @param {Roo.menu.Menu} this
14450 * @param {Roo.EventObject} e
14451 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14456 * Fires when the mouse exits this menu
14457 * @param {Roo.menu.Menu} this
14458 * @param {Roo.EventObject} e
14459 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14464 * Fires when a menu item contained in this menu is clicked
14465 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14466 * @param {Roo.EventObject} e
14470 if (this.registerMenu) {
14471 Roo.menu.MenuMgr.register(this);
14474 var mis = this.items;
14475 this.items = new Roo.util.MixedCollection();
14477 this.add.apply(this, mis);
14481 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14483 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14487 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14488 * for bottom-right shadow (defaults to "sides")
14492 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14493 * this menu (defaults to "tl-tr?")
14495 subMenuAlign : "tl-tr?",
14497 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14498 * relative to its element of origin (defaults to "tl-bl?")
14500 defaultAlign : "tl-bl?",
14502 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14504 allowOtherMenus : false,
14506 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14508 registerMenu : true,
14513 render : function(){
14517 var el = this.el = new Roo.Layer({
14519 shadow:this.shadow,
14521 parentEl: this.parentEl || document.body,
14525 this.keyNav = new Roo.menu.MenuNav(this);
14528 el.addClass("x-menu-plain");
14531 el.addClass(this.cls);
14533 // generic focus element
14534 this.focusEl = el.createChild({
14535 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14537 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14538 //disabling touch- as it's causing issues ..
14539 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14540 ul.on('click' , this.onClick, this);
14543 ul.on("mouseover", this.onMouseOver, this);
14544 ul.on("mouseout", this.onMouseOut, this);
14545 this.items.each(function(item){
14550 var li = document.createElement("li");
14551 li.className = "x-menu-list-item";
14552 ul.dom.appendChild(li);
14553 item.render(li, this);
14560 autoWidth : function(){
14561 var el = this.el, ul = this.ul;
14565 var w = this.width;
14568 }else if(Roo.isIE){
14569 el.setWidth(this.minWidth);
14570 var t = el.dom.offsetWidth; // force recalc
14571 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14576 delayAutoWidth : function(){
14579 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14581 this.awTask.delay(20);
14586 findTargetItem : function(e){
14587 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14588 if(t && t.menuItemId){
14589 return this.items.get(t.menuItemId);
14594 onClick : function(e){
14595 Roo.log("menu.onClick");
14596 var t = this.findTargetItem(e);
14601 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14602 if(t == this.activeItem && t.shouldDeactivate(e)){
14603 this.activeItem.deactivate();
14604 delete this.activeItem;
14608 this.setActiveItem(t, true);
14616 this.fireEvent("click", this, t, e);
14620 setActiveItem : function(item, autoExpand){
14621 if(item != this.activeItem){
14622 if(this.activeItem){
14623 this.activeItem.deactivate();
14625 this.activeItem = item;
14626 item.activate(autoExpand);
14627 }else if(autoExpand){
14633 tryActivate : function(start, step){
14634 var items = this.items;
14635 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14636 var item = items.get(i);
14637 if(!item.disabled && item.canActivate){
14638 this.setActiveItem(item, false);
14646 onMouseOver : function(e){
14648 if(t = this.findTargetItem(e)){
14649 if(t.canActivate && !t.disabled){
14650 this.setActiveItem(t, true);
14653 this.fireEvent("mouseover", this, e, t);
14657 onMouseOut : function(e){
14659 if(t = this.findTargetItem(e)){
14660 if(t == this.activeItem && t.shouldDeactivate(e)){
14661 this.activeItem.deactivate();
14662 delete this.activeItem;
14665 this.fireEvent("mouseout", this, e, t);
14669 * Read-only. Returns true if the menu is currently displayed, else false.
14672 isVisible : function(){
14673 return this.el && !this.hidden;
14677 * Displays this menu relative to another element
14678 * @param {String/HTMLElement/Roo.Element} element The element to align to
14679 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14680 * the element (defaults to this.defaultAlign)
14681 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14683 show : function(el, pos, parentMenu){
14684 this.parentMenu = parentMenu;
14688 this.fireEvent("beforeshow", this);
14689 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14693 * Displays this menu at a specific xy position
14694 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14695 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14697 showAt : function(xy, parentMenu, /* private: */_e){
14698 this.parentMenu = parentMenu;
14703 this.fireEvent("beforeshow", this);
14704 xy = this.el.adjustForConstraints(xy);
14708 this.hidden = false;
14710 this.fireEvent("show", this);
14713 focus : function(){
14715 this.doFocus.defer(50, this);
14719 doFocus : function(){
14721 this.focusEl.focus();
14726 * Hides this menu and optionally all parent menus
14727 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14729 hide : function(deep){
14730 if(this.el && this.isVisible()){
14731 this.fireEvent("beforehide", this);
14732 if(this.activeItem){
14733 this.activeItem.deactivate();
14734 this.activeItem = null;
14737 this.hidden = true;
14738 this.fireEvent("hide", this);
14740 if(deep === true && this.parentMenu){
14741 this.parentMenu.hide(true);
14746 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14747 * Any of the following are valid:
14749 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14750 * <li>An HTMLElement object which will be converted to a menu item</li>
14751 * <li>A menu item config object that will be created as a new menu item</li>
14752 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14753 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14758 var menu = new Roo.menu.Menu();
14760 // Create a menu item to add by reference
14761 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14763 // Add a bunch of items at once using different methods.
14764 // Only the last item added will be returned.
14765 var item = menu.add(
14766 menuItem, // add existing item by ref
14767 'Dynamic Item', // new TextItem
14768 '-', // new separator
14769 { text: 'Config Item' } // new item by config
14772 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14773 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14776 var a = arguments, l = a.length, item;
14777 for(var i = 0; i < l; i++){
14779 if ((typeof(el) == "object") && el.xtype && el.xns) {
14780 el = Roo.factory(el, Roo.menu);
14783 if(el.render){ // some kind of Item
14784 item = this.addItem(el);
14785 }else if(typeof el == "string"){ // string
14786 if(el == "separator" || el == "-"){
14787 item = this.addSeparator();
14789 item = this.addText(el);
14791 }else if(el.tagName || el.el){ // element
14792 item = this.addElement(el);
14793 }else if(typeof el == "object"){ // must be menu item config?
14794 item = this.addMenuItem(el);
14801 * Returns this menu's underlying {@link Roo.Element} object
14802 * @return {Roo.Element} The element
14804 getEl : function(){
14812 * Adds a separator bar to the menu
14813 * @return {Roo.menu.Item} The menu item that was added
14815 addSeparator : function(){
14816 return this.addItem(new Roo.menu.Separator());
14820 * Adds an {@link Roo.Element} object to the menu
14821 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14822 * @return {Roo.menu.Item} The menu item that was added
14824 addElement : function(el){
14825 return this.addItem(new Roo.menu.BaseItem(el));
14829 * Adds an existing object based on {@link Roo.menu.Item} to the menu
14830 * @param {Roo.menu.Item} item The menu item to add
14831 * @return {Roo.menu.Item} The menu item that was added
14833 addItem : function(item){
14834 this.items.add(item);
14836 var li = document.createElement("li");
14837 li.className = "x-menu-list-item";
14838 this.ul.dom.appendChild(li);
14839 item.render(li, this);
14840 this.delayAutoWidth();
14846 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14847 * @param {Object} config A MenuItem config object
14848 * @return {Roo.menu.Item} The menu item that was added
14850 addMenuItem : function(config){
14851 if(!(config instanceof Roo.menu.Item)){
14852 if(typeof config.checked == "boolean"){ // must be check menu item config?
14853 config = new Roo.menu.CheckItem(config);
14855 config = new Roo.menu.Item(config);
14858 return this.addItem(config);
14862 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14863 * @param {String} text The text to display in the menu item
14864 * @return {Roo.menu.Item} The menu item that was added
14866 addText : function(text){
14867 return this.addItem(new Roo.menu.TextItem({ text : text }));
14871 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14872 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14873 * @param {Roo.menu.Item} item The menu item to add
14874 * @return {Roo.menu.Item} The menu item that was added
14876 insert : function(index, item){
14877 this.items.insert(index, item);
14879 var li = document.createElement("li");
14880 li.className = "x-menu-list-item";
14881 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14882 item.render(li, this);
14883 this.delayAutoWidth();
14889 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14890 * @param {Roo.menu.Item} item The menu item to remove
14892 remove : function(item){
14893 this.items.removeKey(item.id);
14898 * Removes and destroys all items in the menu
14900 removeAll : function(){
14902 while(f = this.items.first()){
14908 // MenuNav is a private utility class used internally by the Menu
14909 Roo.menu.MenuNav = function(menu){
14910 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14911 this.scope = this.menu = menu;
14914 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14915 doRelay : function(e, h){
14916 var k = e.getKey();
14917 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14918 this.menu.tryActivate(0, 1);
14921 return h.call(this.scope || this, e, this.menu);
14924 up : function(e, m){
14925 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14926 m.tryActivate(m.items.length-1, -1);
14930 down : function(e, m){
14931 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14932 m.tryActivate(0, 1);
14936 right : function(e, m){
14938 m.activeItem.expandMenu(true);
14942 left : function(e, m){
14944 if(m.parentMenu && m.parentMenu.activeItem){
14945 m.parentMenu.activeItem.activate();
14949 enter : function(e, m){
14951 e.stopPropagation();
14952 m.activeItem.onClick(e);
14953 m.fireEvent("click", this, m.activeItem);
14959 * Ext JS Library 1.1.1
14960 * Copyright(c) 2006-2007, Ext JS, LLC.
14962 * Originally Released Under LGPL - original licence link has changed is not relivant.
14965 * <script type="text/javascript">
14969 * @class Roo.menu.MenuMgr
14970 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14973 Roo.menu.MenuMgr = function(){
14974 var menus, active, groups = {}, attached = false, lastShow = new Date();
14976 // private - called when first menu is created
14979 active = new Roo.util.MixedCollection();
14980 Roo.get(document).addKeyListener(27, function(){
14981 if(active.length > 0){
14988 function hideAll(){
14989 if(active && active.length > 0){
14990 var c = active.clone();
14991 c.each(function(m){
14998 function onHide(m){
15000 if(active.length < 1){
15001 Roo.get(document).un("mousedown", onMouseDown);
15007 function onShow(m){
15008 var last = active.last();
15009 lastShow = new Date();
15012 Roo.get(document).on("mousedown", onMouseDown);
15016 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15017 m.parentMenu.activeChild = m;
15018 }else if(last && last.isVisible()){
15019 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15024 function onBeforeHide(m){
15026 m.activeChild.hide();
15028 if(m.autoHideTimer){
15029 clearTimeout(m.autoHideTimer);
15030 delete m.autoHideTimer;
15035 function onBeforeShow(m){
15036 var pm = m.parentMenu;
15037 if(!pm && !m.allowOtherMenus){
15039 }else if(pm && pm.activeChild && active != m){
15040 pm.activeChild.hide();
15045 function onMouseDown(e){
15046 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15052 function onBeforeCheck(mi, state){
15054 var g = groups[mi.group];
15055 for(var i = 0, l = g.length; i < l; i++){
15057 g[i].setChecked(false);
15066 * Hides all menus that are currently visible
15068 hideAll : function(){
15073 register : function(menu){
15077 menus[menu.id] = menu;
15078 menu.on("beforehide", onBeforeHide);
15079 menu.on("hide", onHide);
15080 menu.on("beforeshow", onBeforeShow);
15081 menu.on("show", onShow);
15082 var g = menu.group;
15083 if(g && menu.events["checkchange"]){
15087 groups[g].push(menu);
15088 menu.on("checkchange", onCheck);
15093 * Returns a {@link Roo.menu.Menu} object
15094 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15095 * be used to generate and return a new Menu instance.
15097 get : function(menu){
15098 if(typeof menu == "string"){ // menu id
15099 return menus[menu];
15100 }else if(menu.events){ // menu instance
15102 }else if(typeof menu.length == 'number'){ // array of menu items?
15103 return new Roo.menu.Menu({items:menu});
15104 }else{ // otherwise, must be a config
15105 return new Roo.menu.Menu(menu);
15110 unregister : function(menu){
15111 delete menus[menu.id];
15112 menu.un("beforehide", onBeforeHide);
15113 menu.un("hide", onHide);
15114 menu.un("beforeshow", onBeforeShow);
15115 menu.un("show", onShow);
15116 var g = menu.group;
15117 if(g && menu.events["checkchange"]){
15118 groups[g].remove(menu);
15119 menu.un("checkchange", onCheck);
15124 registerCheckable : function(menuItem){
15125 var g = menuItem.group;
15130 groups[g].push(menuItem);
15131 menuItem.on("beforecheckchange", onBeforeCheck);
15136 unregisterCheckable : function(menuItem){
15137 var g = menuItem.group;
15139 groups[g].remove(menuItem);
15140 menuItem.un("beforecheckchange", onBeforeCheck);
15146 * Ext JS Library 1.1.1
15147 * Copyright(c) 2006-2007, Ext JS, LLC.
15149 * Originally Released Under LGPL - original licence link has changed is not relivant.
15152 * <script type="text/javascript">
15157 * @class Roo.menu.BaseItem
15158 * @extends Roo.Component
15160 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15161 * management and base configuration options shared by all menu components.
15163 * Creates a new BaseItem
15164 * @param {Object} config Configuration options
15166 Roo.menu.BaseItem = function(config){
15167 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15172 * Fires when this item is clicked
15173 * @param {Roo.menu.BaseItem} this
15174 * @param {Roo.EventObject} e
15179 * Fires when this item is activated
15180 * @param {Roo.menu.BaseItem} this
15184 * @event deactivate
15185 * Fires when this item is deactivated
15186 * @param {Roo.menu.BaseItem} this
15192 this.on("click", this.handler, this.scope, true);
15196 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15198 * @cfg {Function} handler
15199 * A function that will handle the click event of this menu item (defaults to undefined)
15202 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15204 canActivate : false,
15207 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15212 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15214 activeClass : "x-menu-item-active",
15216 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15218 hideOnClick : true,
15220 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15225 ctype: "Roo.menu.BaseItem",
15228 actionMode : "container",
15231 render : function(container, parentMenu){
15232 this.parentMenu = parentMenu;
15233 Roo.menu.BaseItem.superclass.render.call(this, container);
15234 this.container.menuItemId = this.id;
15238 onRender : function(container, position){
15239 this.el = Roo.get(this.el);
15240 container.dom.appendChild(this.el.dom);
15244 onClick : function(e){
15245 if(!this.disabled && this.fireEvent("click", this, e) !== false
15246 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15247 this.handleClick(e);
15254 activate : function(){
15258 var li = this.container;
15259 li.addClass(this.activeClass);
15260 this.region = li.getRegion().adjust(2, 2, -2, -2);
15261 this.fireEvent("activate", this);
15266 deactivate : function(){
15267 this.container.removeClass(this.activeClass);
15268 this.fireEvent("deactivate", this);
15272 shouldDeactivate : function(e){
15273 return !this.region || !this.region.contains(e.getPoint());
15277 handleClick : function(e){
15278 if(this.hideOnClick){
15279 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15284 expandMenu : function(autoActivate){
15289 hideMenu : function(){
15294 * Ext JS Library 1.1.1
15295 * Copyright(c) 2006-2007, Ext JS, LLC.
15297 * Originally Released Under LGPL - original licence link has changed is not relivant.
15300 * <script type="text/javascript">
15304 * @class Roo.menu.Adapter
15305 * @extends Roo.menu.BaseItem
15307 * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15308 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15310 * Creates a new Adapter
15311 * @param {Object} config Configuration options
15313 Roo.menu.Adapter = function(component, config){
15314 Roo.menu.Adapter.superclass.constructor.call(this, config);
15315 this.component = component;
15317 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15319 canActivate : true,
15322 onRender : function(container, position){
15323 this.component.render(container);
15324 this.el = this.component.getEl();
15328 activate : function(){
15332 this.component.focus();
15333 this.fireEvent("activate", this);
15338 deactivate : function(){
15339 this.fireEvent("deactivate", this);
15343 disable : function(){
15344 this.component.disable();
15345 Roo.menu.Adapter.superclass.disable.call(this);
15349 enable : function(){
15350 this.component.enable();
15351 Roo.menu.Adapter.superclass.enable.call(this);
15355 * Ext JS Library 1.1.1
15356 * Copyright(c) 2006-2007, Ext JS, LLC.
15358 * Originally Released Under LGPL - original licence link has changed is not relivant.
15361 * <script type="text/javascript">
15365 * @class Roo.menu.TextItem
15366 * @extends Roo.menu.BaseItem
15367 * Adds a static text string to a menu, usually used as either a heading or group separator.
15368 * Note: old style constructor with text is still supported.
15371 * Creates a new TextItem
15372 * @param {Object} cfg Configuration
15374 Roo.menu.TextItem = function(cfg){
15375 if (typeof(cfg) == 'string') {
15378 Roo.apply(this,cfg);
15381 Roo.menu.TextItem.superclass.constructor.call(this);
15384 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15386 * @cfg {String} text Text to show on item.
15391 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15393 hideOnClick : false,
15395 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15397 itemCls : "x-menu-text",
15400 onRender : function(){
15401 var s = document.createElement("span");
15402 s.className = this.itemCls;
15403 s.innerHTML = this.text;
15405 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15409 * Ext JS Library 1.1.1
15410 * Copyright(c) 2006-2007, Ext JS, LLC.
15412 * Originally Released Under LGPL - original licence link has changed is not relivant.
15415 * <script type="text/javascript">
15419 * @class Roo.menu.Separator
15420 * @extends Roo.menu.BaseItem
15421 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15422 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15424 * @param {Object} config Configuration options
15426 Roo.menu.Separator = function(config){
15427 Roo.menu.Separator.superclass.constructor.call(this, config);
15430 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15432 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15434 itemCls : "x-menu-sep",
15436 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15438 hideOnClick : false,
15441 onRender : function(li){
15442 var s = document.createElement("span");
15443 s.className = this.itemCls;
15444 s.innerHTML = " ";
15446 li.addClass("x-menu-sep-li");
15447 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15451 * Ext JS Library 1.1.1
15452 * Copyright(c) 2006-2007, Ext JS, LLC.
15454 * Originally Released Under LGPL - original licence link has changed is not relivant.
15457 * <script type="text/javascript">
15460 * @class Roo.menu.Item
15461 * @extends Roo.menu.BaseItem
15462 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15463 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15464 * activation and click handling.
15466 * Creates a new Item
15467 * @param {Object} config Configuration options
15469 Roo.menu.Item = function(config){
15470 Roo.menu.Item.superclass.constructor.call(this, config);
15472 this.menu = Roo.menu.MenuMgr.get(this.menu);
15475 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15477 * @cfg {Roo.menu.Menu} menu
15481 * @cfg {String} text
15482 * The text to show on the menu item.
15486 * @cfg {String} HTML to render in menu
15487 * The text to show on the menu item (HTML version).
15491 * @cfg {String} icon
15492 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15496 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15498 itemCls : "x-menu-item",
15500 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15502 canActivate : true,
15504 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15507 // doc'd in BaseItem
15511 ctype: "Roo.menu.Item",
15514 onRender : function(container, position){
15515 var el = document.createElement("a");
15516 el.hideFocus = true;
15517 el.unselectable = "on";
15518 el.href = this.href || "#";
15519 if(this.hrefTarget){
15520 el.target = this.hrefTarget;
15522 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15524 var html = this.html.length ? this.html : String.format('{0}',this.text);
15526 el.innerHTML = String.format(
15527 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15528 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15530 Roo.menu.Item.superclass.onRender.call(this, container, position);
15534 * Sets the text to display in this menu item
15535 * @param {String} text The text to display
15536 * @param {Boolean} isHTML true to indicate text is pure html.
15538 setText : function(text, isHTML){
15546 var html = this.html.length ? this.html : String.format('{0}',this.text);
15548 this.el.update(String.format(
15549 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15550 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15551 this.parentMenu.autoWidth();
15556 handleClick : function(e){
15557 if(!this.href){ // if no link defined, stop the event automatically
15560 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15564 activate : function(autoExpand){
15565 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15575 shouldDeactivate : function(e){
15576 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15577 if(this.menu && this.menu.isVisible()){
15578 return !this.menu.getEl().getRegion().contains(e.getPoint());
15586 deactivate : function(){
15587 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15592 expandMenu : function(autoActivate){
15593 if(!this.disabled && this.menu){
15594 clearTimeout(this.hideTimer);
15595 delete this.hideTimer;
15596 if(!this.menu.isVisible() && !this.showTimer){
15597 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15598 }else if (this.menu.isVisible() && autoActivate){
15599 this.menu.tryActivate(0, 1);
15605 deferExpand : function(autoActivate){
15606 delete this.showTimer;
15607 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15609 this.menu.tryActivate(0, 1);
15614 hideMenu : function(){
15615 clearTimeout(this.showTimer);
15616 delete this.showTimer;
15617 if(!this.hideTimer && this.menu && this.menu.isVisible()){
15618 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15623 deferHide : function(){
15624 delete this.hideTimer;
15629 * Ext JS Library 1.1.1
15630 * Copyright(c) 2006-2007, Ext JS, LLC.
15632 * Originally Released Under LGPL - original licence link has changed is not relivant.
15635 * <script type="text/javascript">
15639 * @class Roo.menu.CheckItem
15640 * @extends Roo.menu.Item
15641 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15643 * Creates a new CheckItem
15644 * @param {Object} config Configuration options
15646 Roo.menu.CheckItem = function(config){
15647 Roo.menu.CheckItem.superclass.constructor.call(this, config);
15650 * @event beforecheckchange
15651 * Fires before the checked value is set, providing an opportunity to cancel if needed
15652 * @param {Roo.menu.CheckItem} this
15653 * @param {Boolean} checked The new checked value that will be set
15655 "beforecheckchange" : true,
15657 * @event checkchange
15658 * Fires after the checked value has been set
15659 * @param {Roo.menu.CheckItem} this
15660 * @param {Boolean} checked The checked value that was set
15662 "checkchange" : true
15664 if(this.checkHandler){
15665 this.on('checkchange', this.checkHandler, this.scope);
15668 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15670 * @cfg {String} group
15671 * All check items with the same group name will automatically be grouped into a single-select
15672 * radio button group (defaults to '')
15675 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15677 itemCls : "x-menu-item x-menu-check-item",
15679 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15681 groupClass : "x-menu-group-item",
15684 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
15685 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15686 * initialized with checked = true will be rendered as checked.
15691 ctype: "Roo.menu.CheckItem",
15694 onRender : function(c){
15695 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15697 this.el.addClass(this.groupClass);
15699 Roo.menu.MenuMgr.registerCheckable(this);
15701 this.checked = false;
15702 this.setChecked(true, true);
15707 destroy : function(){
15709 Roo.menu.MenuMgr.unregisterCheckable(this);
15711 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15715 * Set the checked state of this item
15716 * @param {Boolean} checked The new checked value
15717 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15719 setChecked : function(state, suppressEvent){
15720 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15721 if(this.container){
15722 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15724 this.checked = state;
15725 if(suppressEvent !== true){
15726 this.fireEvent("checkchange", this, state);
15732 handleClick : function(e){
15733 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15734 this.setChecked(!this.checked);
15736 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15740 * Ext JS Library 1.1.1
15741 * Copyright(c) 2006-2007, Ext JS, LLC.
15743 * Originally Released Under LGPL - original licence link has changed is not relivant.
15746 * <script type="text/javascript">
15750 * @class Roo.menu.DateItem
15751 * @extends Roo.menu.Adapter
15752 * A menu item that wraps the {@link Roo.DatPicker} component.
15754 * Creates a new DateItem
15755 * @param {Object} config Configuration options
15757 Roo.menu.DateItem = function(config){
15758 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15759 /** The Roo.DatePicker object @type Roo.DatePicker */
15760 this.picker = this.component;
15761 this.addEvents({select: true});
15763 this.picker.on("render", function(picker){
15764 picker.getEl().swallowEvent("click");
15765 picker.container.addClass("x-menu-date-item");
15768 this.picker.on("select", this.onSelect, this);
15771 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15773 onSelect : function(picker, date){
15774 this.fireEvent("select", this, date, picker);
15775 Roo.menu.DateItem.superclass.handleClick.call(this);
15779 * Ext JS Library 1.1.1
15780 * Copyright(c) 2006-2007, Ext JS, LLC.
15782 * Originally Released Under LGPL - original licence link has changed is not relivant.
15785 * <script type="text/javascript">
15789 * @class Roo.menu.ColorItem
15790 * @extends Roo.menu.Adapter
15791 * A menu item that wraps the {@link Roo.ColorPalette} component.
15793 * Creates a new ColorItem
15794 * @param {Object} config Configuration options
15796 Roo.menu.ColorItem = function(config){
15797 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15798 /** The Roo.ColorPalette object @type Roo.ColorPalette */
15799 this.palette = this.component;
15800 this.relayEvents(this.palette, ["select"]);
15801 if(this.selectHandler){
15802 this.on('select', this.selectHandler, this.scope);
15805 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15807 * Ext JS Library 1.1.1
15808 * Copyright(c) 2006-2007, Ext JS, LLC.
15810 * Originally Released Under LGPL - original licence link has changed is not relivant.
15813 * <script type="text/javascript">
15818 * @class Roo.menu.DateMenu
15819 * @extends Roo.menu.Menu
15820 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15822 * Creates a new DateMenu
15823 * @param {Object} config Configuration options
15825 Roo.menu.DateMenu = function(config){
15826 Roo.menu.DateMenu.superclass.constructor.call(this, config);
15828 var di = new Roo.menu.DateItem(config);
15831 * The {@link Roo.DatePicker} instance for this DateMenu
15834 this.picker = di.picker;
15837 * @param {DatePicker} picker
15838 * @param {Date} date
15840 this.relayEvents(di, ["select"]);
15841 this.on('beforeshow', function(){
15843 this.picker.hideMonthPicker(false);
15847 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15851 * Ext JS Library 1.1.1
15852 * Copyright(c) 2006-2007, Ext JS, LLC.
15854 * Originally Released Under LGPL - original licence link has changed is not relivant.
15857 * <script type="text/javascript">
15862 * @class Roo.menu.ColorMenu
15863 * @extends Roo.menu.Menu
15864 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15866 * Creates a new ColorMenu
15867 * @param {Object} config Configuration options
15869 Roo.menu.ColorMenu = function(config){
15870 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15872 var ci = new Roo.menu.ColorItem(config);
15875 * The {@link Roo.ColorPalette} instance for this ColorMenu
15876 * @type ColorPalette
15878 this.palette = ci.palette;
15881 * @param {ColorPalette} palette
15882 * @param {String} color
15884 this.relayEvents(ci, ["select"]);
15886 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15888 * Ext JS Library 1.1.1
15889 * Copyright(c) 2006-2007, Ext JS, LLC.
15891 * Originally Released Under LGPL - original licence link has changed is not relivant.
15894 * <script type="text/javascript">
15898 * @class Roo.form.TextItem
15899 * @extends Roo.BoxComponent
15900 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15902 * Creates a new TextItem
15903 * @param {Object} config Configuration options
15905 Roo.form.TextItem = function(config){
15906 Roo.form.TextItem.superclass.constructor.call(this, config);
15909 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
15912 * @cfg {String} tag the tag for this item (default div)
15916 * @cfg {String} html the content for this item
15920 getAutoCreate : function()
15933 onRender : function(ct, position)
15935 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15938 var cfg = this.getAutoCreate();
15940 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15942 if (!cfg.name.length) {
15945 this.el = ct.createChild(cfg, position);
15950 * @param {String} html update the Contents of the element.
15952 setHTML : function(html)
15954 this.fieldEl.dom.innerHTML = html;
15959 * Ext JS Library 1.1.1
15960 * Copyright(c) 2006-2007, Ext JS, LLC.
15962 * Originally Released Under LGPL - original licence link has changed is not relivant.
15965 * <script type="text/javascript">
15969 * @class Roo.form.Field
15970 * @extends Roo.BoxComponent
15971 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15973 * Creates a new Field
15974 * @param {Object} config Configuration options
15976 Roo.form.Field = function(config){
15977 Roo.form.Field.superclass.constructor.call(this, config);
15980 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
15982 * @cfg {String} fieldLabel Label to use when rendering a form.
15985 * @cfg {String} qtip Mouse over tip
15989 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15991 invalidClass : "x-form-invalid",
15993 * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
15995 invalidText : "The value in this field is invalid",
15997 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15999 focusClass : "x-form-focus",
16001 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16002 automatic validation (defaults to "keyup").
16004 validationEvent : "keyup",
16006 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16008 validateOnBlur : true,
16010 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16012 validationDelay : 250,
16014 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16015 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16017 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16019 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16021 fieldClass : "x-form-field",
16023 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16026 ----------- ----------------------------------------------------------------------
16027 qtip Display a quick tip when the user hovers over the field
16028 title Display a default browser title attribute popup
16029 under Add a block div beneath the field containing the error text
16030 side Add an error icon to the right of the field with a popup on hover
16031 [element id] Add the error text directly to the innerHTML of the specified element
16034 msgTarget : 'qtip',
16036 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16041 * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16046 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16051 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16053 inputType : undefined,
16056 * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16058 tabIndex : undefined,
16061 isFormField : true,
16066 * @property {Roo.Element} fieldEl
16067 * Element Containing the rendered Field (with label etc.)
16070 * @cfg {Mixed} value A value to initialize this field with.
16075 * @cfg {String} name The field's HTML name attribute.
16078 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16081 loadedValue : false,
16085 initComponent : function(){
16086 Roo.form.Field.superclass.initComponent.call(this);
16090 * Fires when this field receives input focus.
16091 * @param {Roo.form.Field} this
16096 * Fires when this field loses input focus.
16097 * @param {Roo.form.Field} this
16101 * @event specialkey
16102 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16103 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16104 * @param {Roo.form.Field} this
16105 * @param {Roo.EventObject} e The event object
16110 * Fires just before the field blurs if the field value has changed.
16111 * @param {Roo.form.Field} this
16112 * @param {Mixed} newValue The new value
16113 * @param {Mixed} oldValue The original value
16118 * Fires after the field has been marked as invalid.
16119 * @param {Roo.form.Field} this
16120 * @param {String} msg The validation message
16125 * Fires after the field has been validated with no errors.
16126 * @param {Roo.form.Field} this
16131 * Fires after the key up
16132 * @param {Roo.form.Field} this
16133 * @param {Roo.EventObject} e The event Object
16140 * Returns the name attribute of the field if available
16141 * @return {String} name The field name
16143 getName: function(){
16144 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16148 onRender : function(ct, position){
16149 Roo.form.Field.superclass.onRender.call(this, ct, position);
16151 var cfg = this.getAutoCreate();
16153 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16155 if (!cfg.name.length) {
16158 if(this.inputType){
16159 cfg.type = this.inputType;
16161 this.el = ct.createChild(cfg, position);
16163 var type = this.el.dom.type;
16165 if(type == 'password'){
16168 this.el.addClass('x-form-'+type);
16171 this.el.dom.readOnly = true;
16173 if(this.tabIndex !== undefined){
16174 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16177 this.el.addClass([this.fieldClass, this.cls]);
16182 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16183 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16184 * @return {Roo.form.Field} this
16186 applyTo : function(target){
16187 this.allowDomMove = false;
16188 this.el = Roo.get(target);
16189 this.render(this.el.dom.parentNode);
16194 initValue : function(){
16195 if(this.value !== undefined){
16196 this.setValue(this.value);
16197 }else if(this.el.dom.value.length > 0){
16198 this.setValue(this.el.dom.value);
16203 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16204 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16206 isDirty : function() {
16207 if(this.disabled) {
16210 return String(this.getValue()) !== String(this.originalValue);
16214 * stores the current value in loadedValue
16216 resetHasChanged : function()
16218 this.loadedValue = String(this.getValue());
16221 * checks the current value against the 'loaded' value.
16222 * Note - will return false if 'resetHasChanged' has not been called first.
16224 hasChanged : function()
16226 if(this.disabled || this.readOnly) {
16229 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16235 afterRender : function(){
16236 Roo.form.Field.superclass.afterRender.call(this);
16241 fireKey : function(e){
16242 //Roo.log('field ' + e.getKey());
16243 if(e.isNavKeyPress()){
16244 this.fireEvent("specialkey", this, e);
16249 * Resets the current field value to the originally loaded value and clears any validation messages
16251 reset : function(){
16252 this.setValue(this.resetValue);
16253 this.originalValue = this.getValue();
16254 this.clearInvalid();
16258 initEvents : function(){
16259 // safari killled keypress - so keydown is now used..
16260 this.el.on("keydown" , this.fireKey, this);
16261 this.el.on("focus", this.onFocus, this);
16262 this.el.on("blur", this.onBlur, this);
16263 this.el.relayEvent('keyup', this);
16265 // reference to original value for reset
16266 this.originalValue = this.getValue();
16267 this.resetValue = this.getValue();
16271 onFocus : function(){
16272 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16273 this.el.addClass(this.focusClass);
16275 if(!this.hasFocus){
16276 this.hasFocus = true;
16277 this.startValue = this.getValue();
16278 this.fireEvent("focus", this);
16282 beforeBlur : Roo.emptyFn,
16285 onBlur : function(){
16287 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16288 this.el.removeClass(this.focusClass);
16290 this.hasFocus = false;
16291 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16294 var v = this.getValue();
16295 if(String(v) !== String(this.startValue)){
16296 this.fireEvent('change', this, v, this.startValue);
16298 this.fireEvent("blur", this);
16302 * Returns whether or not the field value is currently valid
16303 * @param {Boolean} preventMark True to disable marking the field invalid
16304 * @return {Boolean} True if the value is valid, else false
16306 isValid : function(preventMark){
16310 var restore = this.preventMark;
16311 this.preventMark = preventMark === true;
16312 var v = this.validateValue(this.processValue(this.getRawValue()));
16313 this.preventMark = restore;
16318 * Validates the field value
16319 * @return {Boolean} True if the value is valid, else false
16321 validate : function(){
16322 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16323 this.clearInvalid();
16329 processValue : function(value){
16334 // Subclasses should provide the validation implementation by overriding this
16335 validateValue : function(value){
16340 * Mark this field as invalid
16341 * @param {String} msg The validation message
16343 markInvalid : function(msg){
16344 if(!this.rendered || this.preventMark){ // not rendered
16348 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16350 obj.el.addClass(this.invalidClass);
16351 msg = msg || this.invalidText;
16352 switch(this.msgTarget){
16354 obj.el.dom.qtip = msg;
16355 obj.el.dom.qclass = 'x-form-invalid-tip';
16356 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16357 Roo.QuickTips.enable();
16361 this.el.dom.title = msg;
16365 var elp = this.el.findParent('.x-form-element', 5, true);
16366 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16367 this.errorEl.setWidth(elp.getWidth(true)-20);
16369 this.errorEl.update(msg);
16370 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16373 if(!this.errorIcon){
16374 var elp = this.el.findParent('.x-form-element', 5, true);
16375 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16377 this.alignErrorIcon();
16378 this.errorIcon.dom.qtip = msg;
16379 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16380 this.errorIcon.show();
16381 this.on('resize', this.alignErrorIcon, this);
16384 var t = Roo.getDom(this.msgTarget);
16386 t.style.display = this.msgDisplay;
16389 this.fireEvent('invalid', this, msg);
16393 alignErrorIcon : function(){
16394 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16398 * Clear any invalid styles/messages for this field
16400 clearInvalid : function(){
16401 if(!this.rendered || this.preventMark){ // not rendered
16404 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16406 obj.el.removeClass(this.invalidClass);
16407 switch(this.msgTarget){
16409 obj.el.dom.qtip = '';
16412 this.el.dom.title = '';
16416 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16420 if(this.errorIcon){
16421 this.errorIcon.dom.qtip = '';
16422 this.errorIcon.hide();
16423 this.un('resize', this.alignErrorIcon, this);
16427 var t = Roo.getDom(this.msgTarget);
16429 t.style.display = 'none';
16432 this.fireEvent('valid', this);
16436 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16437 * @return {Mixed} value The field value
16439 getRawValue : function(){
16440 var v = this.el.getValue();
16446 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16447 * @return {Mixed} value The field value
16449 getValue : function(){
16450 var v = this.el.getValue();
16456 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16457 * @param {Mixed} value The value to set
16459 setRawValue : function(v){
16460 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16464 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16465 * @param {Mixed} value The value to set
16467 setValue : function(v){
16470 this.el.dom.value = (v === null || v === undefined ? '' : v);
16475 adjustSize : function(w, h){
16476 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16477 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16481 adjustWidth : function(tag, w){
16482 tag = tag.toLowerCase();
16483 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16484 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16485 if(tag == 'input'){
16488 if(tag == 'textarea'){
16491 }else if(Roo.isOpera){
16492 if(tag == 'input'){
16495 if(tag == 'textarea'){
16505 // anything other than normal should be considered experimental
16506 Roo.form.Field.msgFx = {
16508 show: function(msgEl, f){
16509 msgEl.setDisplayed('block');
16512 hide : function(msgEl, f){
16513 msgEl.setDisplayed(false).update('');
16518 show: function(msgEl, f){
16519 msgEl.slideIn('t', {stopFx:true});
16522 hide : function(msgEl, f){
16523 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16528 show: function(msgEl, f){
16529 msgEl.fixDisplay();
16530 msgEl.alignTo(f.el, 'tl-tr');
16531 msgEl.slideIn('l', {stopFx:true});
16534 hide : function(msgEl, f){
16535 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16540 * Ext JS Library 1.1.1
16541 * Copyright(c) 2006-2007, Ext JS, LLC.
16543 * Originally Released Under LGPL - original licence link has changed is not relivant.
16546 * <script type="text/javascript">
16551 * @class Roo.form.TextField
16552 * @extends Roo.form.Field
16553 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16554 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16556 * Creates a new TextField
16557 * @param {Object} config Configuration options
16559 Roo.form.TextField = function(config){
16560 Roo.form.TextField.superclass.constructor.call(this, config);
16564 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16565 * according to the default logic, but this event provides a hook for the developer to apply additional
16566 * logic at runtime to resize the field if needed.
16567 * @param {Roo.form.Field} this This text field
16568 * @param {Number} width The new field width
16574 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16576 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16580 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16584 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16588 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16592 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16596 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16598 disableKeyFilter : false,
16600 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16604 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16608 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16610 maxLength : Number.MAX_VALUE,
16612 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16614 minLengthText : "The minimum length for this field is {0}",
16616 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16618 maxLengthText : "The maximum length for this field is {0}",
16620 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16622 selectOnFocus : false,
16624 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
16626 allowLeadingSpace : false,
16628 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16630 blankText : "This field is required",
16632 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16633 * If available, this function will be called only after the basic validators all return true, and will be passed the
16634 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16638 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16639 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16640 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
16644 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16648 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16654 initEvents : function()
16656 if (this.emptyText) {
16657 this.el.attr('placeholder', this.emptyText);
16660 Roo.form.TextField.superclass.initEvents.call(this);
16661 if(this.validationEvent == 'keyup'){
16662 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16663 this.el.on('keyup', this.filterValidation, this);
16665 else if(this.validationEvent !== false){
16666 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16669 if(this.selectOnFocus){
16670 this.on("focus", this.preFocus, this);
16672 if (!this.allowLeadingSpace) {
16673 this.on('blur', this.cleanLeadingSpace, this);
16676 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16677 this.el.on("keypress", this.filterKeys, this);
16680 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
16681 this.el.on("click", this.autoSize, this);
16683 if(this.el.is('input[type=password]') && Roo.isSafari){
16684 this.el.on('keydown', this.SafariOnKeyDown, this);
16688 processValue : function(value){
16689 if(this.stripCharsRe){
16690 var newValue = value.replace(this.stripCharsRe, '');
16691 if(newValue !== value){
16692 this.setRawValue(newValue);
16699 filterValidation : function(e){
16700 if(!e.isNavKeyPress()){
16701 this.validationTask.delay(this.validationDelay);
16706 onKeyUp : function(e){
16707 if(!e.isNavKeyPress()){
16711 // private - clean the leading white space
16712 cleanLeadingSpace : function(e)
16714 if ( this.inputType == 'file') {
16718 this.setValue((this.getValue() + '').replace(/^\s+/,''));
16721 * Resets the current field value to the originally-loaded value and clears any validation messages.
16724 reset : function(){
16725 Roo.form.TextField.superclass.reset.call(this);
16729 preFocus : function(){
16731 if(this.selectOnFocus){
16732 this.el.dom.select();
16738 filterKeys : function(e){
16739 var k = e.getKey();
16740 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16743 var c = e.getCharCode(), cc = String.fromCharCode(c);
16744 if(Roo.isIE && (e.isSpecialKey() || !cc)){
16747 if(!this.maskRe.test(cc)){
16752 setValue : function(v){
16754 Roo.form.TextField.superclass.setValue.apply(this, arguments);
16760 * Validates a value according to the field's validation rules and marks the field as invalid
16761 * if the validation fails
16762 * @param {Mixed} value The value to validate
16763 * @return {Boolean} True if the value is valid, else false
16765 validateValue : function(value){
16766 if(value.length < 1) { // if it's blank
16767 if(this.allowBlank){
16768 this.clearInvalid();
16771 this.markInvalid(this.blankText);
16775 if(value.length < this.minLength){
16776 this.markInvalid(String.format(this.minLengthText, this.minLength));
16779 if(value.length > this.maxLength){
16780 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16784 var vt = Roo.form.VTypes;
16785 if(!vt[this.vtype](value, this)){
16786 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16790 if(typeof this.validator == "function"){
16791 var msg = this.validator(value);
16793 this.markInvalid(msg);
16797 if(this.regex && !this.regex.test(value)){
16798 this.markInvalid(this.regexText);
16805 * Selects text in this field
16806 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16807 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16809 selectText : function(start, end){
16810 var v = this.getRawValue();
16812 start = start === undefined ? 0 : start;
16813 end = end === undefined ? v.length : end;
16814 var d = this.el.dom;
16815 if(d.setSelectionRange){
16816 d.setSelectionRange(start, end);
16817 }else if(d.createTextRange){
16818 var range = d.createTextRange();
16819 range.moveStart("character", start);
16820 range.moveEnd("character", v.length-end);
16827 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16828 * This only takes effect if grow = true, and fires the autosize event.
16830 autoSize : function(){
16831 if(!this.grow || !this.rendered){
16835 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16838 var v = el.dom.value;
16839 var d = document.createElement('div');
16840 d.appendChild(document.createTextNode(v));
16844 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16845 this.el.setWidth(w);
16846 this.fireEvent("autosize", this, w);
16850 SafariOnKeyDown : function(event)
16852 // this is a workaround for a password hang bug on chrome/ webkit.
16854 var isSelectAll = false;
16856 if(this.el.dom.selectionEnd > 0){
16857 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16859 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16860 event.preventDefault();
16865 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16867 event.preventDefault();
16868 // this is very hacky as keydown always get's upper case.
16870 var cc = String.fromCharCode(event.getCharCode());
16873 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
16881 * Ext JS Library 1.1.1
16882 * Copyright(c) 2006-2007, Ext JS, LLC.
16884 * Originally Released Under LGPL - original licence link has changed is not relivant.
16887 * <script type="text/javascript">
16891 * @class Roo.form.Hidden
16892 * @extends Roo.form.TextField
16893 * Simple Hidden element used on forms
16895 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16898 * Creates a new Hidden form element.
16899 * @param {Object} config Configuration options
16904 // easy hidden field...
16905 Roo.form.Hidden = function(config){
16906 Roo.form.Hidden.superclass.constructor.call(this, config);
16909 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16911 inputType: 'hidden',
16914 labelSeparator: '',
16916 itemCls : 'x-form-item-display-none'
16924 * Ext JS Library 1.1.1
16925 * Copyright(c) 2006-2007, Ext JS, LLC.
16927 * Originally Released Under LGPL - original licence link has changed is not relivant.
16930 * <script type="text/javascript">
16934 * @class Roo.form.TriggerField
16935 * @extends Roo.form.TextField
16936 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16937 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16938 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16939 * for which you can provide a custom implementation. For example:
16941 var trigger = new Roo.form.TriggerField();
16942 trigger.onTriggerClick = myTriggerFn;
16943 trigger.applyTo('my-field');
16946 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16947 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16948 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
16949 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16951 * Create a new TriggerField.
16952 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16953 * to the base TextField)
16955 Roo.form.TriggerField = function(config){
16956 this.mimicing = false;
16957 Roo.form.TriggerField.superclass.constructor.call(this, config);
16960 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
16962 * @cfg {String} triggerClass A CSS class to apply to the trigger
16965 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16966 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16968 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16970 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16974 /** @cfg {Boolean} grow @hide */
16975 /** @cfg {Number} growMin @hide */
16976 /** @cfg {Number} growMax @hide */
16982 autoSize: Roo.emptyFn,
16986 deferHeight : true,
16989 actionMode : 'wrap',
16991 onResize : function(w, h){
16992 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16993 if(typeof w == 'number'){
16994 var x = w - this.trigger.getWidth();
16995 this.el.setWidth(this.adjustWidth('input', x));
16996 this.trigger.setStyle('left', x+'px');
17001 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17004 getResizeEl : function(){
17009 getPositionEl : function(){
17014 alignErrorIcon : function(){
17015 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17019 onRender : function(ct, position){
17020 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17021 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17022 this.trigger = this.wrap.createChild(this.triggerConfig ||
17023 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17024 if(this.hideTrigger){
17025 this.trigger.setDisplayed(false);
17027 this.initTrigger();
17029 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17034 initTrigger : function(){
17035 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17036 this.trigger.addClassOnOver('x-form-trigger-over');
17037 this.trigger.addClassOnClick('x-form-trigger-click');
17041 onDestroy : function(){
17043 this.trigger.removeAllListeners();
17044 this.trigger.remove();
17047 this.wrap.remove();
17049 Roo.form.TriggerField.superclass.onDestroy.call(this);
17053 onFocus : function(){
17054 Roo.form.TriggerField.superclass.onFocus.call(this);
17055 if(!this.mimicing){
17056 this.wrap.addClass('x-trigger-wrap-focus');
17057 this.mimicing = true;
17058 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17059 if(this.monitorTab){
17060 this.el.on("keydown", this.checkTab, this);
17066 checkTab : function(e){
17067 if(e.getKey() == e.TAB){
17068 this.triggerBlur();
17073 onBlur : function(){
17078 mimicBlur : function(e, t){
17079 if(!this.wrap.contains(t) && this.validateBlur()){
17080 this.triggerBlur();
17085 triggerBlur : function(){
17086 this.mimicing = false;
17087 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17088 if(this.monitorTab){
17089 this.el.un("keydown", this.checkTab, this);
17091 this.wrap.removeClass('x-trigger-wrap-focus');
17092 Roo.form.TriggerField.superclass.onBlur.call(this);
17096 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17097 validateBlur : function(e, t){
17102 onDisable : function(){
17103 Roo.form.TriggerField.superclass.onDisable.call(this);
17105 this.wrap.addClass('x-item-disabled');
17110 onEnable : function(){
17111 Roo.form.TriggerField.superclass.onEnable.call(this);
17113 this.wrap.removeClass('x-item-disabled');
17118 onShow : function(){
17119 var ae = this.getActionEl();
17122 ae.dom.style.display = '';
17123 ae.dom.style.visibility = 'visible';
17129 onHide : function(){
17130 var ae = this.getActionEl();
17131 ae.dom.style.display = 'none';
17135 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17136 * by an implementing function.
17138 * @param {EventObject} e
17140 onTriggerClick : Roo.emptyFn
17143 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17144 // to be extended by an implementing class. For an example of implementing this class, see the custom
17145 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17146 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17147 initComponent : function(){
17148 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17150 this.triggerConfig = {
17151 tag:'span', cls:'x-form-twin-triggers', cn:[
17152 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17153 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17157 getTrigger : function(index){
17158 return this.triggers[index];
17161 initTrigger : function(){
17162 var ts = this.trigger.select('.x-form-trigger', true);
17163 this.wrap.setStyle('overflow', 'hidden');
17164 var triggerField = this;
17165 ts.each(function(t, all, index){
17166 t.hide = function(){
17167 var w = triggerField.wrap.getWidth();
17168 this.dom.style.display = 'none';
17169 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17171 t.show = function(){
17172 var w = triggerField.wrap.getWidth();
17173 this.dom.style.display = '';
17174 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17176 var triggerIndex = 'Trigger'+(index+1);
17178 if(this['hide'+triggerIndex]){
17179 t.dom.style.display = 'none';
17181 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17182 t.addClassOnOver('x-form-trigger-over');
17183 t.addClassOnClick('x-form-trigger-click');
17185 this.triggers = ts.elements;
17188 onTrigger1Click : Roo.emptyFn,
17189 onTrigger2Click : Roo.emptyFn
17192 * Ext JS Library 1.1.1
17193 * Copyright(c) 2006-2007, Ext JS, LLC.
17195 * Originally Released Under LGPL - original licence link has changed is not relivant.
17198 * <script type="text/javascript">
17202 * @class Roo.form.TextArea
17203 * @extends Roo.form.TextField
17204 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17205 * support for auto-sizing.
17207 * Creates a new TextArea
17208 * @param {Object} config Configuration options
17210 Roo.form.TextArea = function(config){
17211 Roo.form.TextArea.superclass.constructor.call(this, config);
17212 // these are provided exchanges for backwards compat
17213 // minHeight/maxHeight were replaced by growMin/growMax to be
17214 // compatible with TextField growing config values
17215 if(this.minHeight !== undefined){
17216 this.growMin = this.minHeight;
17218 if(this.maxHeight !== undefined){
17219 this.growMax = this.maxHeight;
17223 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17225 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17229 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17233 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17234 * in the field (equivalent to setting overflow: hidden, defaults to false)
17236 preventScrollbars: false,
17238 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17239 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17243 onRender : function(ct, position){
17245 this.defaultAutoCreate = {
17247 style:"width:300px;height:60px;",
17248 autocomplete: "new-password"
17251 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17253 this.textSizeEl = Roo.DomHelper.append(document.body, {
17254 tag: "pre", cls: "x-form-grow-sizer"
17256 if(this.preventScrollbars){
17257 this.el.setStyle("overflow", "hidden");
17259 this.el.setHeight(this.growMin);
17263 onDestroy : function(){
17264 if(this.textSizeEl){
17265 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17267 Roo.form.TextArea.superclass.onDestroy.call(this);
17271 onKeyUp : function(e){
17272 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17278 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17279 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17281 autoSize : function(){
17282 if(!this.grow || !this.textSizeEl){
17286 var v = el.dom.value;
17287 var ts = this.textSizeEl;
17290 ts.appendChild(document.createTextNode(v));
17293 Roo.fly(ts).setWidth(this.el.getWidth());
17295 v = "  ";
17298 v = v.replace(/\n/g, '<p> </p>');
17300 v += " \n ";
17303 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17304 if(h != this.lastHeight){
17305 this.lastHeight = h;
17306 this.el.setHeight(h);
17307 this.fireEvent("autosize", this, h);
17312 * Ext JS Library 1.1.1
17313 * Copyright(c) 2006-2007, Ext JS, LLC.
17315 * Originally Released Under LGPL - original licence link has changed is not relivant.
17318 * <script type="text/javascript">
17323 * @class Roo.form.NumberField
17324 * @extends Roo.form.TextField
17325 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17327 * Creates a new NumberField
17328 * @param {Object} config Configuration options
17330 Roo.form.NumberField = function(config){
17331 Roo.form.NumberField.superclass.constructor.call(this, config);
17334 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17336 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17338 fieldClass: "x-form-field x-form-num-field",
17340 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17342 allowDecimals : true,
17344 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17346 decimalSeparator : ".",
17348 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17350 decimalPrecision : 2,
17352 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17354 allowNegative : true,
17356 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17358 minValue : Number.NEGATIVE_INFINITY,
17360 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17362 maxValue : Number.MAX_VALUE,
17364 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17366 minText : "The minimum value for this field is {0}",
17368 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17370 maxText : "The maximum value for this field is {0}",
17372 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17373 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17375 nanText : "{0} is not a valid number",
17378 initEvents : function(){
17379 Roo.form.NumberField.superclass.initEvents.call(this);
17380 var allowed = "0123456789";
17381 if(this.allowDecimals){
17382 allowed += this.decimalSeparator;
17384 if(this.allowNegative){
17387 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17388 var keyPress = function(e){
17389 var k = e.getKey();
17390 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17393 var c = e.getCharCode();
17394 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17398 this.el.on("keypress", keyPress, this);
17402 validateValue : function(value){
17403 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17406 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17409 var num = this.parseValue(value);
17411 this.markInvalid(String.format(this.nanText, value));
17414 if(num < this.minValue){
17415 this.markInvalid(String.format(this.minText, this.minValue));
17418 if(num > this.maxValue){
17419 this.markInvalid(String.format(this.maxText, this.maxValue));
17425 getValue : function(){
17426 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17430 parseValue : function(value){
17431 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17432 return isNaN(value) ? '' : value;
17436 fixPrecision : function(value){
17437 var nan = isNaN(value);
17438 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17439 return nan ? '' : value;
17441 return parseFloat(value).toFixed(this.decimalPrecision);
17444 setValue : function(v){
17445 v = this.fixPrecision(v);
17446 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17450 decimalPrecisionFcn : function(v){
17451 return Math.floor(v);
17454 beforeBlur : function(){
17455 var v = this.parseValue(this.getRawValue());
17462 * Ext JS Library 1.1.1
17463 * Copyright(c) 2006-2007, Ext JS, LLC.
17465 * Originally Released Under LGPL - original licence link has changed is not relivant.
17468 * <script type="text/javascript">
17472 * @class Roo.form.DateField
17473 * @extends Roo.form.TriggerField
17474 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17476 * Create a new DateField
17477 * @param {Object} config
17479 Roo.form.DateField = function(config)
17481 Roo.form.DateField.superclass.constructor.call(this, config);
17487 * Fires when a date is selected
17488 * @param {Roo.form.DateField} combo This combo box
17489 * @param {Date} date The date selected
17496 if(typeof this.minValue == "string") {
17497 this.minValue = this.parseDate(this.minValue);
17499 if(typeof this.maxValue == "string") {
17500 this.maxValue = this.parseDate(this.maxValue);
17502 this.ddMatch = null;
17503 if(this.disabledDates){
17504 var dd = this.disabledDates;
17506 for(var i = 0; i < dd.length; i++){
17508 if(i != dd.length-1) {
17512 this.ddMatch = new RegExp(re + ")");
17516 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17518 * @cfg {String} format
17519 * The default date format string which can be overriden for localization support. The format must be
17520 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17524 * @cfg {String} altFormats
17525 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17526 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17528 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17530 * @cfg {Array} disabledDays
17531 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17533 disabledDays : null,
17535 * @cfg {String} disabledDaysText
17536 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17538 disabledDaysText : "Disabled",
17540 * @cfg {Array} disabledDates
17541 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17542 * expression so they are very powerful. Some examples:
17544 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17545 * <li>["03/08", "09/16"] would disable those days for every year</li>
17546 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17547 * <li>["03/../2006"] would disable every day in March 2006</li>
17548 * <li>["^03"] would disable every day in every March</li>
17550 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17551 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17553 disabledDates : null,
17555 * @cfg {String} disabledDatesText
17556 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17558 disabledDatesText : "Disabled",
17560 * @cfg {Date/String} minValue
17561 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17562 * valid format (defaults to null).
17566 * @cfg {Date/String} maxValue
17567 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17568 * valid format (defaults to null).
17572 * @cfg {String} minText
17573 * The error text to display when the date in the cell is before minValue (defaults to
17574 * 'The date in this field must be after {minValue}').
17576 minText : "The date in this field must be equal to or after {0}",
17578 * @cfg {String} maxText
17579 * The error text to display when the date in the cell is after maxValue (defaults to
17580 * 'The date in this field must be before {maxValue}').
17582 maxText : "The date in this field must be equal to or before {0}",
17584 * @cfg {String} invalidText
17585 * The error text to display when the date in the field is invalid (defaults to
17586 * '{value} is not a valid date - it must be in the format {format}').
17588 invalidText : "{0} is not a valid date - it must be in the format {1}",
17590 * @cfg {String} triggerClass
17591 * An additional CSS class used to style the trigger button. The trigger will always get the
17592 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17593 * which displays a calendar icon).
17595 triggerClass : 'x-form-date-trigger',
17599 * @cfg {Boolean} useIso
17600 * if enabled, then the date field will use a hidden field to store the
17601 * real value as iso formated date. default (false)
17605 * @cfg {String/Object} autoCreate
17606 * A DomHelper element spec, or true for a default element spec (defaults to
17607 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17610 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17613 hiddenField: false,
17615 onRender : function(ct, position)
17617 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17619 //this.el.dom.removeAttribute('name');
17620 Roo.log("Changing name?");
17621 this.el.dom.setAttribute('name', this.name + '____hidden___' );
17622 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17624 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17625 // prevent input submission
17626 this.hiddenName = this.name;
17633 validateValue : function(value)
17635 value = this.formatDate(value);
17636 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17637 Roo.log('super failed');
17640 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17643 var svalue = value;
17644 value = this.parseDate(value);
17646 Roo.log('parse date failed' + svalue);
17647 this.markInvalid(String.format(this.invalidText, svalue, this.format));
17650 var time = value.getTime();
17651 if(this.minValue && time < this.minValue.getTime()){
17652 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17655 if(this.maxValue && time > this.maxValue.getTime()){
17656 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17659 if(this.disabledDays){
17660 var day = value.getDay();
17661 for(var i = 0; i < this.disabledDays.length; i++) {
17662 if(day === this.disabledDays[i]){
17663 this.markInvalid(this.disabledDaysText);
17668 var fvalue = this.formatDate(value);
17669 if(this.ddMatch && this.ddMatch.test(fvalue)){
17670 this.markInvalid(String.format(this.disabledDatesText, fvalue));
17677 // Provides logic to override the default TriggerField.validateBlur which just returns true
17678 validateBlur : function(){
17679 return !this.menu || !this.menu.isVisible();
17682 getName: function()
17684 // returns hidden if it's set..
17685 if (!this.rendered) {return ''};
17686 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
17691 * Returns the current date value of the date field.
17692 * @return {Date} The date value
17694 getValue : function(){
17696 return this.hiddenField ?
17697 this.hiddenField.value :
17698 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17702 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
17703 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17704 * (the default format used is "m/d/y").
17707 //All of these calls set the same date value (May 4, 2006)
17709 //Pass a date object:
17710 var dt = new Date('5/4/06');
17711 dateField.setValue(dt);
17713 //Pass a date string (default format):
17714 dateField.setValue('5/4/06');
17716 //Pass a date string (custom format):
17717 dateField.format = 'Y-m-d';
17718 dateField.setValue('2006-5-4');
17720 * @param {String/Date} date The date or valid date string
17722 setValue : function(date){
17723 if (this.hiddenField) {
17724 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17726 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17727 // make sure the value field is always stored as a date..
17728 this.value = this.parseDate(date);
17734 parseDate : function(value){
17735 if(!value || value instanceof Date){
17738 var v = Date.parseDate(value, this.format);
17739 if (!v && this.useIso) {
17740 v = Date.parseDate(value, 'Y-m-d');
17742 if(!v && this.altFormats){
17743 if(!this.altFormatsArray){
17744 this.altFormatsArray = this.altFormats.split("|");
17746 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17747 v = Date.parseDate(value, this.altFormatsArray[i]);
17754 formatDate : function(date, fmt){
17755 return (!date || !(date instanceof Date)) ?
17756 date : date.dateFormat(fmt || this.format);
17761 select: function(m, d){
17764 this.fireEvent('select', this, d);
17766 show : function(){ // retain focus styling
17770 this.focus.defer(10, this);
17771 var ml = this.menuListeners;
17772 this.menu.un("select", ml.select, this);
17773 this.menu.un("show", ml.show, this);
17774 this.menu.un("hide", ml.hide, this);
17779 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17780 onTriggerClick : function(){
17784 if(this.menu == null){
17785 this.menu = new Roo.menu.DateMenu();
17787 Roo.apply(this.menu.picker, {
17788 showClear: this.allowBlank,
17789 minDate : this.minValue,
17790 maxDate : this.maxValue,
17791 disabledDatesRE : this.ddMatch,
17792 disabledDatesText : this.disabledDatesText,
17793 disabledDays : this.disabledDays,
17794 disabledDaysText : this.disabledDaysText,
17795 format : this.useIso ? 'Y-m-d' : this.format,
17796 minText : String.format(this.minText, this.formatDate(this.minValue)),
17797 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17799 this.menu.on(Roo.apply({}, this.menuListeners, {
17802 this.menu.picker.setValue(this.getValue() || new Date());
17803 this.menu.show(this.el, "tl-bl?");
17806 beforeBlur : function(){
17807 var v = this.parseDate(this.getRawValue());
17817 isDirty : function() {
17818 if(this.disabled) {
17822 if(typeof(this.startValue) === 'undefined'){
17826 return String(this.getValue()) !== String(this.startValue);
17830 cleanLeadingSpace : function(e)
17837 * Ext JS Library 1.1.1
17838 * Copyright(c) 2006-2007, Ext JS, LLC.
17840 * Originally Released Under LGPL - original licence link has changed is not relivant.
17843 * <script type="text/javascript">
17847 * @class Roo.form.MonthField
17848 * @extends Roo.form.TriggerField
17849 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17851 * Create a new MonthField
17852 * @param {Object} config
17854 Roo.form.MonthField = function(config){
17856 Roo.form.MonthField.superclass.constructor.call(this, config);
17862 * Fires when a date is selected
17863 * @param {Roo.form.MonthFieeld} combo This combo box
17864 * @param {Date} date The date selected
17871 if(typeof this.minValue == "string") {
17872 this.minValue = this.parseDate(this.minValue);
17874 if(typeof this.maxValue == "string") {
17875 this.maxValue = this.parseDate(this.maxValue);
17877 this.ddMatch = null;
17878 if(this.disabledDates){
17879 var dd = this.disabledDates;
17881 for(var i = 0; i < dd.length; i++){
17883 if(i != dd.length-1) {
17887 this.ddMatch = new RegExp(re + ")");
17891 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
17893 * @cfg {String} format
17894 * The default date format string which can be overriden for localization support. The format must be
17895 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17899 * @cfg {String} altFormats
17900 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17901 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17903 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17905 * @cfg {Array} disabledDays
17906 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17908 disabledDays : [0,1,2,3,4,5,6],
17910 * @cfg {String} disabledDaysText
17911 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17913 disabledDaysText : "Disabled",
17915 * @cfg {Array} disabledDates
17916 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17917 * expression so they are very powerful. Some examples:
17919 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17920 * <li>["03/08", "09/16"] would disable those days for every year</li>
17921 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17922 * <li>["03/../2006"] would disable every day in March 2006</li>
17923 * <li>["^03"] would disable every day in every March</li>
17925 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17926 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17928 disabledDates : null,
17930 * @cfg {String} disabledDatesText
17931 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17933 disabledDatesText : "Disabled",
17935 * @cfg {Date/String} minValue
17936 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17937 * valid format (defaults to null).
17941 * @cfg {Date/String} maxValue
17942 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17943 * valid format (defaults to null).
17947 * @cfg {String} minText
17948 * The error text to display when the date in the cell is before minValue (defaults to
17949 * 'The date in this field must be after {minValue}').
17951 minText : "The date in this field must be equal to or after {0}",
17953 * @cfg {String} maxTextf
17954 * The error text to display when the date in the cell is after maxValue (defaults to
17955 * 'The date in this field must be before {maxValue}').
17957 maxText : "The date in this field must be equal to or before {0}",
17959 * @cfg {String} invalidText
17960 * The error text to display when the date in the field is invalid (defaults to
17961 * '{value} is not a valid date - it must be in the format {format}').
17963 invalidText : "{0} is not a valid date - it must be in the format {1}",
17965 * @cfg {String} triggerClass
17966 * An additional CSS class used to style the trigger button. The trigger will always get the
17967 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17968 * which displays a calendar icon).
17970 triggerClass : 'x-form-date-trigger',
17974 * @cfg {Boolean} useIso
17975 * if enabled, then the date field will use a hidden field to store the
17976 * real value as iso formated date. default (true)
17980 * @cfg {String/Object} autoCreate
17981 * A DomHelper element spec, or true for a default element spec (defaults to
17982 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17985 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17988 hiddenField: false,
17990 hideMonthPicker : false,
17992 onRender : function(ct, position)
17994 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17996 this.el.dom.removeAttribute('name');
17997 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17999 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18000 // prevent input submission
18001 this.hiddenName = this.name;
18008 validateValue : function(value)
18010 value = this.formatDate(value);
18011 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18014 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18017 var svalue = value;
18018 value = this.parseDate(value);
18020 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18023 var time = value.getTime();
18024 if(this.minValue && time < this.minValue.getTime()){
18025 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18028 if(this.maxValue && time > this.maxValue.getTime()){
18029 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18032 /*if(this.disabledDays){
18033 var day = value.getDay();
18034 for(var i = 0; i < this.disabledDays.length; i++) {
18035 if(day === this.disabledDays[i]){
18036 this.markInvalid(this.disabledDaysText);
18042 var fvalue = this.formatDate(value);
18043 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18044 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18052 // Provides logic to override the default TriggerField.validateBlur which just returns true
18053 validateBlur : function(){
18054 return !this.menu || !this.menu.isVisible();
18058 * Returns the current date value of the date field.
18059 * @return {Date} The date value
18061 getValue : function(){
18065 return this.hiddenField ?
18066 this.hiddenField.value :
18067 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18071 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18072 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18073 * (the default format used is "m/d/y").
18076 //All of these calls set the same date value (May 4, 2006)
18078 //Pass a date object:
18079 var dt = new Date('5/4/06');
18080 monthField.setValue(dt);
18082 //Pass a date string (default format):
18083 monthField.setValue('5/4/06');
18085 //Pass a date string (custom format):
18086 monthField.format = 'Y-m-d';
18087 monthField.setValue('2006-5-4');
18089 * @param {String/Date} date The date or valid date string
18091 setValue : function(date){
18092 Roo.log('month setValue' + date);
18093 // can only be first of month..
18095 var val = this.parseDate(date);
18097 if (this.hiddenField) {
18098 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18100 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18101 this.value = this.parseDate(date);
18105 parseDate : function(value){
18106 if(!value || value instanceof Date){
18107 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18110 var v = Date.parseDate(value, this.format);
18111 if (!v && this.useIso) {
18112 v = Date.parseDate(value, 'Y-m-d');
18116 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18120 if(!v && this.altFormats){
18121 if(!this.altFormatsArray){
18122 this.altFormatsArray = this.altFormats.split("|");
18124 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18125 v = Date.parseDate(value, this.altFormatsArray[i]);
18132 formatDate : function(date, fmt){
18133 return (!date || !(date instanceof Date)) ?
18134 date : date.dateFormat(fmt || this.format);
18139 select: function(m, d){
18141 this.fireEvent('select', this, d);
18143 show : function(){ // retain focus styling
18147 this.focus.defer(10, this);
18148 var ml = this.menuListeners;
18149 this.menu.un("select", ml.select, this);
18150 this.menu.un("show", ml.show, this);
18151 this.menu.un("hide", ml.hide, this);
18155 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18156 onTriggerClick : function(){
18160 if(this.menu == null){
18161 this.menu = new Roo.menu.DateMenu();
18165 Roo.apply(this.menu.picker, {
18167 showClear: this.allowBlank,
18168 minDate : this.minValue,
18169 maxDate : this.maxValue,
18170 disabledDatesRE : this.ddMatch,
18171 disabledDatesText : this.disabledDatesText,
18173 format : this.useIso ? 'Y-m-d' : this.format,
18174 minText : String.format(this.minText, this.formatDate(this.minValue)),
18175 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18178 this.menu.on(Roo.apply({}, this.menuListeners, {
18186 // hide month picker get's called when we called by 'before hide';
18188 var ignorehide = true;
18189 p.hideMonthPicker = function(disableAnim){
18193 if(this.monthPicker){
18194 Roo.log("hideMonthPicker called");
18195 if(disableAnim === true){
18196 this.monthPicker.hide();
18198 this.monthPicker.slideOut('t', {duration:.2});
18199 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18200 p.fireEvent("select", this, this.value);
18206 Roo.log('picker set value');
18207 Roo.log(this.getValue());
18208 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18209 m.show(this.el, 'tl-bl?');
18210 ignorehide = false;
18211 // this will trigger hideMonthPicker..
18214 // hidden the day picker
18215 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18221 p.showMonthPicker.defer(100, p);
18227 beforeBlur : function(){
18228 var v = this.parseDate(this.getRawValue());
18234 /** @cfg {Boolean} grow @hide */
18235 /** @cfg {Number} growMin @hide */
18236 /** @cfg {Number} growMax @hide */
18243 * Ext JS Library 1.1.1
18244 * Copyright(c) 2006-2007, Ext JS, LLC.
18246 * Originally Released Under LGPL - original licence link has changed is not relivant.
18249 * <script type="text/javascript">
18254 * @class Roo.form.ComboBox
18255 * @extends Roo.form.TriggerField
18256 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18258 * Create a new ComboBox.
18259 * @param {Object} config Configuration options
18261 Roo.form.ComboBox = function(config){
18262 Roo.form.ComboBox.superclass.constructor.call(this, config);
18266 * Fires when the dropdown list is expanded
18267 * @param {Roo.form.ComboBox} combo This combo box
18272 * Fires when the dropdown list is collapsed
18273 * @param {Roo.form.ComboBox} combo This combo box
18277 * @event beforeselect
18278 * Fires before a list item is selected. Return false to cancel the selection.
18279 * @param {Roo.form.ComboBox} combo This combo box
18280 * @param {Roo.data.Record} record The data record returned from the underlying store
18281 * @param {Number} index The index of the selected item in the dropdown list
18283 'beforeselect' : true,
18286 * Fires when a list item is selected
18287 * @param {Roo.form.ComboBox} combo This combo box
18288 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18289 * @param {Number} index The index of the selected item in the dropdown list
18293 * @event beforequery
18294 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18295 * The event object passed has these properties:
18296 * @param {Roo.form.ComboBox} combo This combo box
18297 * @param {String} query The query
18298 * @param {Boolean} forceAll true to force "all" query
18299 * @param {Boolean} cancel true to cancel the query
18300 * @param {Object} e The query event object
18302 'beforequery': true,
18305 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18306 * @param {Roo.form.ComboBox} combo This combo box
18311 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18312 * @param {Roo.form.ComboBox} combo This combo box
18313 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18319 if(this.transform){
18320 this.allowDomMove = false;
18321 var s = Roo.getDom(this.transform);
18322 if(!this.hiddenName){
18323 this.hiddenName = s.name;
18326 this.mode = 'local';
18327 var d = [], opts = s.options;
18328 for(var i = 0, len = opts.length;i < len; i++){
18330 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18332 this.value = value;
18334 d.push([value, o.text]);
18336 this.store = new Roo.data.SimpleStore({
18338 fields: ['value', 'text'],
18341 this.valueField = 'value';
18342 this.displayField = 'text';
18344 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18345 if(!this.lazyRender){
18346 this.target = true;
18347 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18348 s.parentNode.removeChild(s); // remove it
18349 this.render(this.el.parentNode);
18351 s.parentNode.removeChild(s); // remove it
18356 this.store = Roo.factory(this.store, Roo.data);
18359 this.selectedIndex = -1;
18360 if(this.mode == 'local'){
18361 if(config.queryDelay === undefined){
18362 this.queryDelay = 10;
18364 if(config.minChars === undefined){
18370 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18372 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18375 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18376 * rendering into an Roo.Editor, defaults to false)
18379 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18380 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18383 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18386 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18387 * the dropdown list (defaults to undefined, with no header element)
18391 * @cfg {String/Roo.Template} tpl The template to use to render the output
18395 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18397 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18399 listWidth: undefined,
18401 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18402 * mode = 'remote' or 'text' if mode = 'local')
18404 displayField: undefined,
18406 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18407 * mode = 'remote' or 'value' if mode = 'local').
18408 * Note: use of a valueField requires the user make a selection
18409 * in order for a value to be mapped.
18411 valueField: undefined,
18415 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18416 * field's data value (defaults to the underlying DOM element's name)
18418 hiddenName: undefined,
18420 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18424 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18426 selectedClass: 'x-combo-selected',
18428 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18429 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18430 * which displays a downward arrow icon).
18432 triggerClass : 'x-form-arrow-trigger',
18434 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18438 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18439 * anchor positions (defaults to 'tl-bl')
18441 listAlign: 'tl-bl?',
18443 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18447 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18448 * query specified by the allQuery config option (defaults to 'query')
18450 triggerAction: 'query',
18452 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18453 * (defaults to 4, does not apply if editable = false)
18457 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18458 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18462 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18463 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18467 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18468 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18472 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18473 * when editable = true (defaults to false)
18475 selectOnFocus:false,
18477 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18479 queryParam: 'query',
18481 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18482 * when mode = 'remote' (defaults to 'Loading...')
18484 loadingText: 'Loading...',
18486 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18490 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18494 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18495 * traditional select (defaults to true)
18499 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18503 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18507 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18508 * listWidth has a higher value)
18512 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18513 * allow the user to set arbitrary text into the field (defaults to false)
18515 forceSelection:false,
18517 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18518 * if typeAhead = true (defaults to 250)
18520 typeAheadDelay : 250,
18522 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18523 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18525 valueNotFoundText : undefined,
18527 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18529 blockFocus : false,
18532 * @cfg {Boolean} disableClear Disable showing of clear button.
18534 disableClear : false,
18536 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18538 alwaysQuery : false,
18544 // element that contains real text value.. (when hidden is used..)
18547 onRender : function(ct, position)
18549 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18551 if(this.hiddenName){
18552 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18554 this.hiddenField.value =
18555 this.hiddenValue !== undefined ? this.hiddenValue :
18556 this.value !== undefined ? this.value : '';
18558 // prevent input submission
18559 this.el.dom.removeAttribute('name');
18565 this.el.dom.setAttribute('autocomplete', 'off');
18568 var cls = 'x-combo-list';
18570 this.list = new Roo.Layer({
18571 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18574 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18575 this.list.setWidth(lw);
18576 this.list.swallowEvent('mousewheel');
18577 this.assetHeight = 0;
18580 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18581 this.assetHeight += this.header.getHeight();
18584 this.innerList = this.list.createChild({cls:cls+'-inner'});
18585 this.innerList.on('mouseover', this.onViewOver, this);
18586 this.innerList.on('mousemove', this.onViewMove, this);
18587 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18589 if(this.allowBlank && !this.pageSize && !this.disableClear){
18590 this.footer = this.list.createChild({cls:cls+'-ft'});
18591 this.pageTb = new Roo.Toolbar(this.footer);
18595 this.footer = this.list.createChild({cls:cls+'-ft'});
18596 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18597 {pageSize: this.pageSize});
18601 if (this.pageTb && this.allowBlank && !this.disableClear) {
18603 this.pageTb.add(new Roo.Toolbar.Fill(), {
18604 cls: 'x-btn-icon x-btn-clear',
18606 handler: function()
18609 _this.clearValue();
18610 _this.onSelect(false, -1);
18615 this.assetHeight += this.footer.getHeight();
18620 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18623 this.view = new Roo.View(this.innerList, this.tpl, {
18626 selectedClass: this.selectedClass
18629 this.view.on('click', this.onViewClick, this);
18631 this.store.on('beforeload', this.onBeforeLoad, this);
18632 this.store.on('load', this.onLoad, this);
18633 this.store.on('loadexception', this.onLoadException, this);
18635 if(this.resizable){
18636 this.resizer = new Roo.Resizable(this.list, {
18637 pinned:true, handles:'se'
18639 this.resizer.on('resize', function(r, w, h){
18640 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18641 this.listWidth = w;
18642 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18643 this.restrictHeight();
18645 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18647 if(!this.editable){
18648 this.editable = true;
18649 this.setEditable(false);
18653 if (typeof(this.events.add.listeners) != 'undefined') {
18655 this.addicon = this.wrap.createChild(
18656 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
18658 this.addicon.on('click', function(e) {
18659 this.fireEvent('add', this);
18662 if (typeof(this.events.edit.listeners) != 'undefined') {
18664 this.editicon = this.wrap.createChild(
18665 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
18666 if (this.addicon) {
18667 this.editicon.setStyle('margin-left', '40px');
18669 this.editicon.on('click', function(e) {
18671 // we fire even if inothing is selected..
18672 this.fireEvent('edit', this, this.lastData );
18682 initEvents : function(){
18683 Roo.form.ComboBox.superclass.initEvents.call(this);
18685 this.keyNav = new Roo.KeyNav(this.el, {
18686 "up" : function(e){
18687 this.inKeyMode = true;
18691 "down" : function(e){
18692 if(!this.isExpanded()){
18693 this.onTriggerClick();
18695 this.inKeyMode = true;
18700 "enter" : function(e){
18701 this.onViewClick();
18705 "esc" : function(e){
18709 "tab" : function(e){
18710 this.onViewClick(false);
18711 this.fireEvent("specialkey", this, e);
18717 doRelay : function(foo, bar, hname){
18718 if(hname == 'down' || this.scope.isExpanded()){
18719 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18726 this.queryDelay = Math.max(this.queryDelay || 10,
18727 this.mode == 'local' ? 10 : 250);
18728 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18729 if(this.typeAhead){
18730 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18732 if(this.editable !== false){
18733 this.el.on("keyup", this.onKeyUp, this);
18735 if(this.forceSelection){
18736 this.on('blur', this.doForce, this);
18740 onDestroy : function(){
18742 this.view.setStore(null);
18743 this.view.el.removeAllListeners();
18744 this.view.el.remove();
18745 this.view.purgeListeners();
18748 this.list.destroy();
18751 this.store.un('beforeload', this.onBeforeLoad, this);
18752 this.store.un('load', this.onLoad, this);
18753 this.store.un('loadexception', this.onLoadException, this);
18755 Roo.form.ComboBox.superclass.onDestroy.call(this);
18759 fireKey : function(e){
18760 if(e.isNavKeyPress() && !this.list.isVisible()){
18761 this.fireEvent("specialkey", this, e);
18766 onResize: function(w, h){
18767 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18769 if(typeof w != 'number'){
18770 // we do not handle it!?!?
18773 var tw = this.trigger.getWidth();
18774 tw += this.addicon ? this.addicon.getWidth() : 0;
18775 tw += this.editicon ? this.editicon.getWidth() : 0;
18777 this.el.setWidth( this.adjustWidth('input', x));
18779 this.trigger.setStyle('left', x+'px');
18781 if(this.list && this.listWidth === undefined){
18782 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18783 this.list.setWidth(lw);
18784 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18792 * Allow or prevent the user from directly editing the field text. If false is passed,
18793 * the user will only be able to select from the items defined in the dropdown list. This method
18794 * is the runtime equivalent of setting the 'editable' config option at config time.
18795 * @param {Boolean} value True to allow the user to directly edit the field text
18797 setEditable : function(value){
18798 if(value == this.editable){
18801 this.editable = value;
18803 this.el.dom.setAttribute('readOnly', true);
18804 this.el.on('mousedown', this.onTriggerClick, this);
18805 this.el.addClass('x-combo-noedit');
18807 this.el.dom.setAttribute('readOnly', false);
18808 this.el.un('mousedown', this.onTriggerClick, this);
18809 this.el.removeClass('x-combo-noedit');
18814 onBeforeLoad : function(){
18815 if(!this.hasFocus){
18818 this.innerList.update(this.loadingText ?
18819 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18820 this.restrictHeight();
18821 this.selectedIndex = -1;
18825 onLoad : function(){
18826 if(!this.hasFocus){
18829 if(this.store.getCount() > 0){
18831 this.restrictHeight();
18832 if(this.lastQuery == this.allQuery){
18834 this.el.dom.select();
18836 if(!this.selectByValue(this.value, true)){
18837 this.select(0, true);
18841 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18842 this.taTask.delay(this.typeAheadDelay);
18846 this.onEmptyResults();
18851 onLoadException : function()
18854 Roo.log(this.store.reader.jsonData);
18855 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18856 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18862 onTypeAhead : function(){
18863 if(this.store.getCount() > 0){
18864 var r = this.store.getAt(0);
18865 var newValue = r.data[this.displayField];
18866 var len = newValue.length;
18867 var selStart = this.getRawValue().length;
18868 if(selStart != len){
18869 this.setRawValue(newValue);
18870 this.selectText(selStart, newValue.length);
18876 onSelect : function(record, index){
18877 if(this.fireEvent('beforeselect', this, record, index) !== false){
18878 this.setFromData(index > -1 ? record.data : false);
18880 this.fireEvent('select', this, record, index);
18885 * Returns the currently selected field value or empty string if no value is set.
18886 * @return {String} value The selected value
18888 getValue : function(){
18889 if(this.valueField){
18890 return typeof this.value != 'undefined' ? this.value : '';
18892 return Roo.form.ComboBox.superclass.getValue.call(this);
18896 * Clears any text/value currently set in the field
18898 clearValue : function(){
18899 if(this.hiddenField){
18900 this.hiddenField.value = '';
18903 this.setRawValue('');
18904 this.lastSelectionText = '';
18909 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18910 * will be displayed in the field. If the value does not match the data value of an existing item,
18911 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18912 * Otherwise the field will be blank (although the value will still be set).
18913 * @param {String} value The value to match
18915 setValue : function(v){
18917 if(this.valueField){
18918 var r = this.findRecord(this.valueField, v);
18920 text = r.data[this.displayField];
18921 }else if(this.valueNotFoundText !== undefined){
18922 text = this.valueNotFoundText;
18925 this.lastSelectionText = text;
18926 if(this.hiddenField){
18927 this.hiddenField.value = v;
18929 Roo.form.ComboBox.superclass.setValue.call(this, text);
18933 * @property {Object} the last set data for the element
18938 * Sets the value of the field based on a object which is related to the record format for the store.
18939 * @param {Object} value the value to set as. or false on reset?
18941 setFromData : function(o){
18942 var dv = ''; // display value
18943 var vv = ''; // value value..
18945 if (this.displayField) {
18946 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18948 // this is an error condition!!!
18949 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18952 if(this.valueField){
18953 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18955 if(this.hiddenField){
18956 this.hiddenField.value = vv;
18958 this.lastSelectionText = dv;
18959 Roo.form.ComboBox.superclass.setValue.call(this, dv);
18963 // no hidden field.. - we store the value in 'value', but still display
18964 // display field!!!!
18965 this.lastSelectionText = dv;
18966 Roo.form.ComboBox.superclass.setValue.call(this, dv);
18972 reset : function(){
18973 // overridden so that last data is reset..
18974 this.setValue(this.resetValue);
18975 this.originalValue = this.getValue();
18976 this.clearInvalid();
18977 this.lastData = false;
18979 this.view.clearSelections();
18983 findRecord : function(prop, value){
18985 if(this.store.getCount() > 0){
18986 this.store.each(function(r){
18987 if(r.data[prop] == value){
18997 getName: function()
18999 // returns hidden if it's set..
19000 if (!this.rendered) {return ''};
19001 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19005 onViewMove : function(e, t){
19006 this.inKeyMode = false;
19010 onViewOver : function(e, t){
19011 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19014 var item = this.view.findItemFromChild(t);
19016 var index = this.view.indexOf(item);
19017 this.select(index, false);
19022 onViewClick : function(doFocus)
19024 var index = this.view.getSelectedIndexes()[0];
19025 var r = this.store.getAt(index);
19027 this.onSelect(r, index);
19029 if(doFocus !== false && !this.blockFocus){
19035 restrictHeight : function(){
19036 this.innerList.dom.style.height = '';
19037 var inner = this.innerList.dom;
19038 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19039 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19040 this.list.beginUpdate();
19041 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19042 this.list.alignTo(this.el, this.listAlign);
19043 this.list.endUpdate();
19047 onEmptyResults : function(){
19052 * Returns true if the dropdown list is expanded, else false.
19054 isExpanded : function(){
19055 return this.list.isVisible();
19059 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19060 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19061 * @param {String} value The data value of the item to select
19062 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19063 * selected item if it is not currently in view (defaults to true)
19064 * @return {Boolean} True if the value matched an item in the list, else false
19066 selectByValue : function(v, scrollIntoView){
19067 if(v !== undefined && v !== null){
19068 var r = this.findRecord(this.valueField || this.displayField, v);
19070 this.select(this.store.indexOf(r), scrollIntoView);
19078 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19079 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19080 * @param {Number} index The zero-based index of the list item to select
19081 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19082 * selected item if it is not currently in view (defaults to true)
19084 select : function(index, scrollIntoView){
19085 this.selectedIndex = index;
19086 this.view.select(index);
19087 if(scrollIntoView !== false){
19088 var el = this.view.getNode(index);
19090 this.innerList.scrollChildIntoView(el, false);
19096 selectNext : function(){
19097 var ct = this.store.getCount();
19099 if(this.selectedIndex == -1){
19101 }else if(this.selectedIndex < ct-1){
19102 this.select(this.selectedIndex+1);
19108 selectPrev : function(){
19109 var ct = this.store.getCount();
19111 if(this.selectedIndex == -1){
19113 }else if(this.selectedIndex != 0){
19114 this.select(this.selectedIndex-1);
19120 onKeyUp : function(e){
19121 if(this.editable !== false && !e.isSpecialKey()){
19122 this.lastKey = e.getKey();
19123 this.dqTask.delay(this.queryDelay);
19128 validateBlur : function(){
19129 return !this.list || !this.list.isVisible();
19133 initQuery : function(){
19134 this.doQuery(this.getRawValue());
19138 doForce : function(){
19139 if(this.el.dom.value.length > 0){
19140 this.el.dom.value =
19141 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19147 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19148 * query allowing the query action to be canceled if needed.
19149 * @param {String} query The SQL query to execute
19150 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19151 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19152 * saved in the current store (defaults to false)
19154 doQuery : function(q, forceAll){
19155 if(q === undefined || q === null){
19160 forceAll: forceAll,
19164 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19168 forceAll = qe.forceAll;
19169 if(forceAll === true || (q.length >= this.minChars)){
19170 if(this.lastQuery != q || this.alwaysQuery){
19171 this.lastQuery = q;
19172 if(this.mode == 'local'){
19173 this.selectedIndex = -1;
19175 this.store.clearFilter();
19177 this.store.filter(this.displayField, q);
19181 this.store.baseParams[this.queryParam] = q;
19183 params: this.getParams(q)
19188 this.selectedIndex = -1;
19195 getParams : function(q){
19197 //p[this.queryParam] = q;
19200 p.limit = this.pageSize;
19206 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19208 collapse : function(){
19209 if(!this.isExpanded()){
19213 Roo.get(document).un('mousedown', this.collapseIf, this);
19214 Roo.get(document).un('mousewheel', this.collapseIf, this);
19215 if (!this.editable) {
19216 Roo.get(document).un('keydown', this.listKeyPress, this);
19218 this.fireEvent('collapse', this);
19222 collapseIf : function(e){
19223 if(!e.within(this.wrap) && !e.within(this.list)){
19229 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19231 expand : function(){
19232 if(this.isExpanded() || !this.hasFocus){
19235 this.list.alignTo(this.el, this.listAlign);
19237 Roo.get(document).on('mousedown', this.collapseIf, this);
19238 Roo.get(document).on('mousewheel', this.collapseIf, this);
19239 if (!this.editable) {
19240 Roo.get(document).on('keydown', this.listKeyPress, this);
19243 this.fireEvent('expand', this);
19247 // Implements the default empty TriggerField.onTriggerClick function
19248 onTriggerClick : function(){
19252 if(this.isExpanded()){
19254 if (!this.blockFocus) {
19259 this.hasFocus = true;
19260 if(this.triggerAction == 'all') {
19261 this.doQuery(this.allQuery, true);
19263 this.doQuery(this.getRawValue());
19265 if (!this.blockFocus) {
19270 listKeyPress : function(e)
19272 //Roo.log('listkeypress');
19273 // scroll to first matching element based on key pres..
19274 if (e.isSpecialKey()) {
19277 var k = String.fromCharCode(e.getKey()).toUpperCase();
19280 var csel = this.view.getSelectedNodes();
19281 var cselitem = false;
19283 var ix = this.view.indexOf(csel[0]);
19284 cselitem = this.store.getAt(ix);
19285 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19291 this.store.each(function(v) {
19293 // start at existing selection.
19294 if (cselitem.id == v.id) {
19300 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19301 match = this.store.indexOf(v);
19306 if (match === false) {
19307 return true; // no more action?
19310 this.view.select(match);
19311 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19312 sn.scrollIntoView(sn.dom.parentNode, false);
19316 * @cfg {Boolean} grow
19320 * @cfg {Number} growMin
19324 * @cfg {Number} growMax
19332 * Copyright(c) 2010-2012, Roo J Solutions Limited
19339 * @class Roo.form.ComboBoxArray
19340 * @extends Roo.form.TextField
19341 * A facebook style adder... for lists of email / people / countries etc...
19342 * pick multiple items from a combo box, and shows each one.
19344 * Fred [x] Brian [x] [Pick another |v]
19347 * For this to work: it needs various extra information
19348 * - normal combo problay has
19350 * + displayField, valueField
19352 * For our purpose...
19355 * If we change from 'extends' to wrapping...
19362 * Create a new ComboBoxArray.
19363 * @param {Object} config Configuration options
19367 Roo.form.ComboBoxArray = function(config)
19371 * @event beforeremove
19372 * Fires before remove the value from the list
19373 * @param {Roo.form.ComboBoxArray} _self This combo box array
19374 * @param {Roo.form.ComboBoxArray.Item} item removed item
19376 'beforeremove' : true,
19379 * Fires when remove the value from the list
19380 * @param {Roo.form.ComboBoxArray} _self This combo box array
19381 * @param {Roo.form.ComboBoxArray.Item} item removed item
19388 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19390 this.items = new Roo.util.MixedCollection(false);
19392 // construct the child combo...
19402 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19405 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19410 // behavies liek a hiddne field
19411 inputType: 'hidden',
19413 * @cfg {Number} width The width of the box that displays the selected element
19420 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19424 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19426 hiddenName : false,
19428 * @cfg {String} seperator The value seperator normally ','
19432 // private the array of items that are displayed..
19434 // private - the hidden field el.
19436 // private - the filed el..
19439 //validateValue : function() { return true; }, // all values are ok!
19440 //onAddClick: function() { },
19442 onRender : function(ct, position)
19445 // create the standard hidden element
19446 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19449 // give fake names to child combo;
19450 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19451 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19453 this.combo = Roo.factory(this.combo, Roo.form);
19454 this.combo.onRender(ct, position);
19455 if (typeof(this.combo.width) != 'undefined') {
19456 this.combo.onResize(this.combo.width,0);
19459 this.combo.initEvents();
19461 // assigned so form know we need to do this..
19462 this.store = this.combo.store;
19463 this.valueField = this.combo.valueField;
19464 this.displayField = this.combo.displayField ;
19467 this.combo.wrap.addClass('x-cbarray-grp');
19469 var cbwrap = this.combo.wrap.createChild(
19470 {tag: 'div', cls: 'x-cbarray-cb'},
19475 this.hiddenEl = this.combo.wrap.createChild({
19476 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19478 this.el = this.combo.wrap.createChild({
19479 tag: 'input', type:'hidden' , name: this.name, value : ''
19481 // this.el.dom.removeAttribute("name");
19484 this.outerWrap = this.combo.wrap;
19485 this.wrap = cbwrap;
19487 this.outerWrap.setWidth(this.width);
19488 this.outerWrap.dom.removeChild(this.el.dom);
19490 this.wrap.dom.appendChild(this.el.dom);
19491 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19492 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19494 this.combo.trigger.setStyle('position','relative');
19495 this.combo.trigger.setStyle('left', '0px');
19496 this.combo.trigger.setStyle('top', '2px');
19498 this.combo.el.setStyle('vertical-align', 'text-bottom');
19500 //this.trigger.setStyle('vertical-align', 'top');
19502 // this should use the code from combo really... on('add' ....)
19506 this.adder = this.outerWrap.createChild(
19507 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19509 this.adder.on('click', function(e) {
19510 _t.fireEvent('adderclick', this, e);
19514 //this.adder.on('click', this.onAddClick, _t);
19517 this.combo.on('select', function(cb, rec, ix) {
19518 this.addItem(rec.data);
19521 cb.el.dom.value = '';
19522 //cb.lastData = rec.data;
19531 getName: function()
19533 // returns hidden if it's set..
19534 if (!this.rendered) {return ''};
19535 return this.hiddenName ? this.hiddenName : this.name;
19540 onResize: function(w, h){
19543 // not sure if this is needed..
19544 //this.combo.onResize(w,h);
19546 if(typeof w != 'number'){
19547 // we do not handle it!?!?
19550 var tw = this.combo.trigger.getWidth();
19551 tw += this.addicon ? this.addicon.getWidth() : 0;
19552 tw += this.editicon ? this.editicon.getWidth() : 0;
19554 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19556 this.combo.trigger.setStyle('left', '0px');
19558 if(this.list && this.listWidth === undefined){
19559 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19560 this.list.setWidth(lw);
19561 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19568 addItem: function(rec)
19570 var valueField = this.combo.valueField;
19571 var displayField = this.combo.displayField;
19573 if (this.items.indexOfKey(rec[valueField]) > -1) {
19574 //console.log("GOT " + rec.data.id);
19578 var x = new Roo.form.ComboBoxArray.Item({
19579 //id : rec[this.idField],
19581 displayField : displayField ,
19582 tipField : displayField ,
19586 this.items.add(rec[valueField],x);
19587 // add it before the element..
19588 this.updateHiddenEl();
19589 x.render(this.outerWrap, this.wrap.dom);
19590 // add the image handler..
19593 updateHiddenEl : function()
19596 if (!this.hiddenEl) {
19600 var idField = this.combo.valueField;
19602 this.items.each(function(f) {
19603 ar.push(f.data[idField]);
19605 this.hiddenEl.dom.value = ar.join(this.seperator);
19611 this.items.clear();
19613 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19617 this.el.dom.value = '';
19618 if (this.hiddenEl) {
19619 this.hiddenEl.dom.value = '';
19623 getValue: function()
19625 return this.hiddenEl ? this.hiddenEl.dom.value : '';
19627 setValue: function(v) // not a valid action - must use addItems..
19632 if (this.store.isLocal && (typeof(v) == 'string')) {
19633 // then we can use the store to find the values..
19634 // comma seperated at present.. this needs to allow JSON based encoding..
19635 this.hiddenEl.value = v;
19637 Roo.each(v.split(this.seperator), function(k) {
19638 Roo.log("CHECK " + this.valueField + ',' + k);
19639 var li = this.store.query(this.valueField, k);
19644 add[this.valueField] = k;
19645 add[this.displayField] = li.item(0).data[this.displayField];
19651 if (typeof(v) == 'object' ) {
19652 // then let's assume it's an array of objects..
19653 Roo.each(v, function(l) {
19655 if (typeof(l) == 'string') {
19657 add[this.valueField] = l;
19658 add[this.displayField] = l
19667 setFromData: function(v)
19669 // this recieves an object, if setValues is called.
19671 this.el.dom.value = v[this.displayField];
19672 this.hiddenEl.dom.value = v[this.valueField];
19673 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19676 var kv = v[this.valueField];
19677 var dv = v[this.displayField];
19678 kv = typeof(kv) != 'string' ? '' : kv;
19679 dv = typeof(dv) != 'string' ? '' : dv;
19682 var keys = kv.split(this.seperator);
19683 var display = dv.split(this.seperator);
19684 for (var i = 0 ; i < keys.length; i++) {
19686 add[this.valueField] = keys[i];
19687 add[this.displayField] = display[i];
19695 * Validates the combox array value
19696 * @return {Boolean} True if the value is valid, else false
19698 validate : function(){
19699 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19700 this.clearInvalid();
19706 validateValue : function(value){
19707 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19715 isDirty : function() {
19716 if(this.disabled) {
19721 var d = Roo.decode(String(this.originalValue));
19723 return String(this.getValue()) !== String(this.originalValue);
19726 var originalValue = [];
19728 for (var i = 0; i < d.length; i++){
19729 originalValue.push(d[i][this.valueField]);
19732 return String(this.getValue()) !== String(originalValue.join(this.seperator));
19741 * @class Roo.form.ComboBoxArray.Item
19742 * @extends Roo.BoxComponent
19743 * A selected item in the list
19744 * Fred [x] Brian [x] [Pick another |v]
19747 * Create a new item.
19748 * @param {Object} config Configuration options
19751 Roo.form.ComboBoxArray.Item = function(config) {
19752 config.id = Roo.id();
19753 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19756 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19759 displayField : false,
19763 defaultAutoCreate : {
19765 cls: 'x-cbarray-item',
19772 src : Roo.BLANK_IMAGE_URL ,
19780 onRender : function(ct, position)
19782 Roo.form.Field.superclass.onRender.call(this, ct, position);
19785 var cfg = this.getAutoCreate();
19786 this.el = ct.createChild(cfg, position);
19789 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19791 this.el.child('div').dom.innerHTML = this.cb.renderer ?
19792 this.cb.renderer(this.data) :
19793 String.format('{0}',this.data[this.displayField]);
19796 this.el.child('div').dom.setAttribute('qtip',
19797 String.format('{0}',this.data[this.tipField])
19800 this.el.child('img').on('click', this.remove, this);
19804 remove : function()
19806 if(this.cb.disabled){
19810 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19811 this.cb.items.remove(this);
19812 this.el.child('img').un('click', this.remove, this);
19814 this.cb.updateHiddenEl();
19816 this.cb.fireEvent('remove', this.cb, this);
19821 * RooJS Library 1.1.1
19822 * Copyright(c) 2008-2011 Alan Knowles
19829 * @class Roo.form.ComboNested
19830 * @extends Roo.form.ComboBox
19831 * A combobox for that allows selection of nested items in a list,
19846 * Create a new ComboNested
19847 * @param {Object} config Configuration options
19849 Roo.form.ComboNested = function(config){
19850 Roo.form.ComboCheck.superclass.constructor.call(this, config);
19851 // should verify some data...
19853 // hiddenName = required..
19854 // displayField = required
19855 // valudField == required
19856 var req= [ 'hiddenName', 'displayField', 'valueField' ];
19858 Roo.each(req, function(e) {
19859 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19860 throw "Roo.form.ComboNested : missing value for: " + e;
19867 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19870 * @config {Number} max Number of columns to show
19875 list : null, // the outermost div..
19876 innerLists : null, // the
19880 loadingChildren : false,
19882 onRender : function(ct, position)
19884 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19886 if(this.hiddenName){
19887 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
19889 this.hiddenField.value =
19890 this.hiddenValue !== undefined ? this.hiddenValue :
19891 this.value !== undefined ? this.value : '';
19893 // prevent input submission
19894 this.el.dom.removeAttribute('name');
19900 this.el.dom.setAttribute('autocomplete', 'off');
19903 var cls = 'x-combo-list';
19905 this.list = new Roo.Layer({
19906 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19909 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19910 this.list.setWidth(lw);
19911 this.list.swallowEvent('mousewheel');
19912 this.assetHeight = 0;
19915 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19916 this.assetHeight += this.header.getHeight();
19918 this.innerLists = [];
19921 for (var i =0 ; i < this.maxColumns; i++) {
19922 this.onRenderList( cls, i);
19925 // always needs footer, as we are going to have an 'OK' button.
19926 this.footer = this.list.createChild({cls:cls+'-ft'});
19927 this.pageTb = new Roo.Toolbar(this.footer);
19932 handler: function()
19938 if ( this.allowBlank && !this.disableClear) {
19940 this.pageTb.add(new Roo.Toolbar.Fill(), {
19941 cls: 'x-btn-icon x-btn-clear',
19943 handler: function()
19946 _this.clearValue();
19947 _this.onSelect(false, -1);
19952 this.assetHeight += this.footer.getHeight();
19956 onRenderList : function ( cls, i)
19959 var lw = Math.floor(
19960 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19963 this.list.setWidth(lw); // default to '1'
19965 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19966 //il.on('mouseover', this.onViewOver, this, { list: i });
19967 //il.on('mousemove', this.onViewMove, this, { list: i });
19969 il.setStyle({ 'overflow-x' : 'hidden'});
19972 this.tpl = new Roo.Template({
19973 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19974 isEmpty: function (value, allValues) {
19976 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19977 return dl ? 'has-children' : 'no-children'
19982 var store = this.store;
19984 store = new Roo.data.SimpleStore({
19985 //fields : this.store.reader.meta.fields,
19986 reader : this.store.reader,
19990 this.stores[i] = store;
19992 var view = this.views[i] = new Roo.View(
19998 selectedClass: this.selectedClass
20001 view.getEl().setWidth(lw);
20002 view.getEl().setStyle({
20003 position: i < 1 ? 'relative' : 'absolute',
20005 left: (i * lw ) + 'px',
20006 display : i > 0 ? 'none' : 'block'
20008 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20009 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20010 //view.on('click', this.onViewClick, this, { list : i });
20012 store.on('beforeload', this.onBeforeLoad, this);
20013 store.on('load', this.onLoad, this, { list : i});
20014 store.on('loadexception', this.onLoadException, this);
20016 // hide the other vies..
20022 restrictHeight : function()
20025 Roo.each(this.innerLists, function(il,i) {
20026 var el = this.views[i].getEl();
20027 el.dom.style.height = '';
20028 var inner = el.dom;
20029 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20030 // only adjust heights on other ones..
20031 mh = Math.max(h, mh);
20034 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20035 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20042 this.list.beginUpdate();
20043 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20044 this.list.alignTo(this.el, this.listAlign);
20045 this.list.endUpdate();
20050 // -- store handlers..
20052 onBeforeLoad : function()
20054 if(!this.hasFocus){
20057 this.innerLists[0].update(this.loadingText ?
20058 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20059 this.restrictHeight();
20060 this.selectedIndex = -1;
20063 onLoad : function(a,b,c,d)
20065 if (!this.loadingChildren) {
20066 // then we are loading the top level. - hide the children
20067 for (var i = 1;i < this.views.length; i++) {
20068 this.views[i].getEl().setStyle({ display : 'none' });
20070 var lw = Math.floor(
20071 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20074 this.list.setWidth(lw); // default to '1'
20078 if(!this.hasFocus){
20082 if(this.store.getCount() > 0) {
20084 this.restrictHeight();
20086 this.onEmptyResults();
20089 if (!this.loadingChildren) {
20090 this.selectActive();
20093 this.stores[1].loadData([]);
20094 this.stores[2].loadData([]);
20103 onLoadException : function()
20106 Roo.log(this.store.reader.jsonData);
20107 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20108 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20113 // no cleaning of leading spaces on blur here.
20114 cleanLeadingSpace : function(e) { },
20117 onSelectChange : function (view, sels, opts )
20119 var ix = view.getSelectedIndexes();
20121 if (opts.list > this.maxColumns - 2) {
20122 if (view.store.getCount()< 1) {
20123 this.views[opts.list ].getEl().setStyle({ display : 'none' });
20127 // used to clear ?? but if we are loading unselected
20128 this.setFromData(view.store.getAt(ix[0]).data);
20137 // this get's fired when trigger opens..
20138 // this.setFromData({});
20139 var str = this.stores[opts.list+1];
20140 str.data.clear(); // removeall wihtout the fire events..
20144 var rec = view.store.getAt(ix[0]);
20146 this.setFromData(rec.data);
20147 this.fireEvent('select', this, rec, ix[0]);
20149 var lw = Math.floor(
20151 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20152 ) / this.maxColumns
20154 this.loadingChildren = true;
20155 this.stores[opts.list+1].loadDataFromChildren( rec );
20156 this.loadingChildren = false;
20157 var dl = this.stores[opts.list+1]. getTotalCount();
20159 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20161 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20162 for (var i = opts.list+2; i < this.views.length;i++) {
20163 this.views[i].getEl().setStyle({ display : 'none' });
20166 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20167 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20169 if (this.isLoading) {
20170 // this.selectActive(opts.list);
20178 onDoubleClick : function()
20180 this.collapse(); //??
20188 recordToStack : function(store, prop, value, stack)
20190 var cstore = new Roo.data.SimpleStore({
20191 //fields : this.store.reader.meta.fields, // we need array reader.. for
20192 reader : this.store.reader,
20196 var record = false;
20198 if(store.getCount() < 1){
20201 store.each(function(r){
20202 if(r.data[prop] == value){
20207 if (r.data.cn && r.data.cn.length) {
20208 cstore.loadDataFromChildren( r);
20209 var cret = _this.recordToStack(cstore, prop, value, stack);
20210 if (cret !== false) {
20219 if (record == false) {
20222 stack.unshift(srec);
20227 * find the stack of stores that match our value.
20232 selectActive : function ()
20234 // if store is not loaded, then we will need to wait for that to happen first.
20236 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20237 for (var i = 0; i < stack.length; i++ ) {
20238 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20250 * Ext JS Library 1.1.1
20251 * Copyright(c) 2006-2007, Ext JS, LLC.
20253 * Originally Released Under LGPL - original licence link has changed is not relivant.
20256 * <script type="text/javascript">
20259 * @class Roo.form.Checkbox
20260 * @extends Roo.form.Field
20261 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20263 * Creates a new Checkbox
20264 * @param {Object} config Configuration options
20266 Roo.form.Checkbox = function(config){
20267 Roo.form.Checkbox.superclass.constructor.call(this, config);
20271 * Fires when the checkbox is checked or unchecked.
20272 * @param {Roo.form.Checkbox} this This checkbox
20273 * @param {Boolean} checked The new checked value
20279 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20281 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20283 focusClass : undefined,
20285 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20287 fieldClass: "x-form-field",
20289 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20293 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20294 * {tag: "input", type: "checkbox", autocomplete: "off"})
20296 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20298 * @cfg {String} boxLabel The text that appears beside the checkbox
20302 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20306 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20308 valueOff: '0', // value when not checked..
20310 actionMode : 'viewEl',
20313 itemCls : 'x-menu-check-item x-form-item',
20314 groupClass : 'x-menu-group-item',
20315 inputType : 'hidden',
20318 inSetChecked: false, // check that we are not calling self...
20320 inputElement: false, // real input element?
20321 basedOn: false, // ????
20323 isFormField: true, // not sure where this is needed!!!!
20325 onResize : function(){
20326 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20327 if(!this.boxLabel){
20328 this.el.alignTo(this.wrap, 'c-c');
20332 initEvents : function(){
20333 Roo.form.Checkbox.superclass.initEvents.call(this);
20334 this.el.on("click", this.onClick, this);
20335 this.el.on("change", this.onClick, this);
20339 getResizeEl : function(){
20343 getPositionEl : function(){
20348 onRender : function(ct, position){
20349 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20351 if(this.inputValue !== undefined){
20352 this.el.dom.value = this.inputValue;
20355 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20356 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20357 var viewEl = this.wrap.createChild({
20358 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20359 this.viewEl = viewEl;
20360 this.wrap.on('click', this.onClick, this);
20362 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20363 this.el.on('propertychange', this.setFromHidden, this); //ie
20368 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20369 // viewEl.on('click', this.onClick, this);
20371 //if(this.checked){
20372 this.setChecked(this.checked);
20374 //this.checked = this.el.dom;
20380 initValue : Roo.emptyFn,
20383 * Returns the checked state of the checkbox.
20384 * @return {Boolean} True if checked, else false
20386 getValue : function(){
20388 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20390 return this.valueOff;
20395 onClick : function(){
20396 if (this.disabled) {
20399 this.setChecked(!this.checked);
20401 //if(this.el.dom.checked != this.checked){
20402 // this.setValue(this.el.dom.checked);
20407 * Sets the checked state of the checkbox.
20408 * On is always based on a string comparison between inputValue and the param.
20409 * @param {Boolean/String} value - the value to set
20410 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20412 setValue : function(v,suppressEvent){
20415 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20416 //if(this.el && this.el.dom){
20417 // this.el.dom.checked = this.checked;
20418 // this.el.dom.defaultChecked = this.checked;
20420 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20421 //this.fireEvent("check", this, this.checked);
20424 setChecked : function(state,suppressEvent)
20426 if (this.inSetChecked) {
20427 this.checked = state;
20433 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20435 this.checked = state;
20436 if(suppressEvent !== true){
20437 this.fireEvent('check', this, state);
20439 this.inSetChecked = true;
20440 this.el.dom.value = state ? this.inputValue : this.valueOff;
20441 this.inSetChecked = false;
20444 // handle setting of hidden value by some other method!!?!?
20445 setFromHidden: function()
20450 //console.log("SET FROM HIDDEN");
20451 //alert('setFrom hidden');
20452 this.setValue(this.el.dom.value);
20455 onDestroy : function()
20458 Roo.get(this.viewEl).remove();
20461 Roo.form.Checkbox.superclass.onDestroy.call(this);
20464 setBoxLabel : function(str)
20466 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20471 * Ext JS Library 1.1.1
20472 * Copyright(c) 2006-2007, Ext JS, LLC.
20474 * Originally Released Under LGPL - original licence link has changed is not relivant.
20477 * <script type="text/javascript">
20481 * @class Roo.form.Radio
20482 * @extends Roo.form.Checkbox
20483 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20484 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20486 * Creates a new Radio
20487 * @param {Object} config Configuration options
20489 Roo.form.Radio = function(){
20490 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20492 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20493 inputType: 'radio',
20496 * If this radio is part of a group, it will return the selected value
20499 getGroupValue : function(){
20500 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20504 onRender : function(ct, position){
20505 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20507 if(this.inputValue !== undefined){
20508 this.el.dom.value = this.inputValue;
20511 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20512 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20513 //var viewEl = this.wrap.createChild({
20514 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20515 //this.viewEl = viewEl;
20516 //this.wrap.on('click', this.onClick, this);
20518 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20519 //this.el.on('propertychange', this.setFromHidden, this); //ie
20524 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20525 // viewEl.on('click', this.onClick, this);
20528 this.el.dom.checked = 'checked' ;
20534 });//<script type="text/javascript">
20537 * Based Ext JS Library 1.1.1
20538 * Copyright(c) 2006-2007, Ext JS, LLC.
20544 * @class Roo.HtmlEditorCore
20545 * @extends Roo.Component
20546 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20548 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20551 Roo.HtmlEditorCore = function(config){
20554 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20559 * @event initialize
20560 * Fires when the editor is fully initialized (including the iframe)
20561 * @param {Roo.HtmlEditorCore} this
20566 * Fires when the editor is first receives the focus. Any insertion must wait
20567 * until after this event.
20568 * @param {Roo.HtmlEditorCore} this
20572 * @event beforesync
20573 * Fires before the textarea is updated with content from the editor iframe. Return false
20574 * to cancel the sync.
20575 * @param {Roo.HtmlEditorCore} this
20576 * @param {String} html
20580 * @event beforepush
20581 * Fires before the iframe editor is updated with content from the textarea. Return false
20582 * to cancel the push.
20583 * @param {Roo.HtmlEditorCore} this
20584 * @param {String} html
20589 * Fires when the textarea is updated with content from the editor iframe.
20590 * @param {Roo.HtmlEditorCore} this
20591 * @param {String} html
20596 * Fires when the iframe editor is updated with content from the textarea.
20597 * @param {Roo.HtmlEditorCore} this
20598 * @param {String} html
20603 * @event editorevent
20604 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20605 * @param {Roo.HtmlEditorCore} this
20611 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20613 // defaults : white / black...
20614 this.applyBlacklists();
20621 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20625 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20631 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20636 * @cfg {Number} height (in pixels)
20640 * @cfg {Number} width (in pixels)
20645 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20648 stylesheets: false,
20651 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20653 allowComments: false,
20657 // private properties
20658 validationEvent : false,
20660 initialized : false,
20662 sourceEditMode : false,
20663 onFocus : Roo.emptyFn,
20665 hideMode:'offsets',
20669 // blacklist + whitelisted elements..
20676 * Protected method that will not generally be called directly. It
20677 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20678 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20680 getDocMarkup : function(){
20684 // inherit styels from page...??
20685 if (this.stylesheets === false) {
20687 Roo.get(document.head).select('style').each(function(node) {
20688 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20691 Roo.get(document.head).select('link').each(function(node) {
20692 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20695 } else if (!this.stylesheets.length) {
20697 st = '<style type="text/css">' +
20698 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20701 for (var i in this.stylesheets) {
20702 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20707 st += '<style type="text/css">' +
20708 'IMG { cursor: pointer } ' +
20711 var cls = 'roo-htmleditor-body';
20713 if(this.bodyCls.length){
20714 cls += ' ' + this.bodyCls;
20717 return '<html><head>' + st +
20718 //<style type="text/css">' +
20719 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20721 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
20725 onRender : function(ct, position)
20728 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20729 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20732 this.el.dom.style.border = '0 none';
20733 this.el.dom.setAttribute('tabIndex', -1);
20734 this.el.addClass('x-hidden hide');
20738 if(Roo.isIE){ // fix IE 1px bogus margin
20739 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20743 this.frameId = Roo.id();
20747 var iframe = this.owner.wrap.createChild({
20749 cls: 'form-control', // bootstrap..
20751 name: this.frameId,
20752 frameBorder : 'no',
20753 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20758 this.iframe = iframe.dom;
20760 this.assignDocWin();
20762 this.doc.designMode = 'on';
20765 this.doc.write(this.getDocMarkup());
20769 var task = { // must defer to wait for browser to be ready
20771 //console.log("run task?" + this.doc.readyState);
20772 this.assignDocWin();
20773 if(this.doc.body || this.doc.readyState == 'complete'){
20775 this.doc.designMode="on";
20779 Roo.TaskMgr.stop(task);
20780 this.initEditor.defer(10, this);
20787 Roo.TaskMgr.start(task);
20792 onResize : function(w, h)
20794 Roo.log('resize: ' +w + ',' + h );
20795 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20799 if(typeof w == 'number'){
20801 this.iframe.style.width = w + 'px';
20803 if(typeof h == 'number'){
20805 this.iframe.style.height = h + 'px';
20807 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20814 * Toggles the editor between standard and source edit mode.
20815 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20817 toggleSourceEdit : function(sourceEditMode){
20819 this.sourceEditMode = sourceEditMode === true;
20821 if(this.sourceEditMode){
20823 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20826 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20827 //this.iframe.className = '';
20830 //this.setSize(this.owner.wrap.getSize());
20831 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20838 * Protected method that will not generally be called directly. If you need/want
20839 * custom HTML cleanup, this is the method you should override.
20840 * @param {String} html The HTML to be cleaned
20841 * return {String} The cleaned HTML
20843 cleanHtml : function(html){
20844 html = String(html);
20845 if(html.length > 5){
20846 if(Roo.isSafari){ // strip safari nonsense
20847 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20850 if(html == ' '){
20857 * HTML Editor -> Textarea
20858 * Protected method that will not generally be called directly. Syncs the contents
20859 * of the editor iframe with the textarea.
20861 syncValue : function(){
20862 if(this.initialized){
20863 var bd = (this.doc.body || this.doc.documentElement);
20864 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20865 var html = bd.innerHTML;
20867 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20868 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20870 html = '<div style="'+m[0]+'">' + html + '</div>';
20873 html = this.cleanHtml(html);
20874 // fix up the special chars.. normaly like back quotes in word...
20875 // however we do not want to do this with chinese..
20876 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20878 var cc = match.charCodeAt();
20880 // Get the character value, handling surrogate pairs
20881 if (match.length == 2) {
20882 // It's a surrogate pair, calculate the Unicode code point
20883 var high = match.charCodeAt(0) - 0xD800;
20884 var low = match.charCodeAt(1) - 0xDC00;
20885 cc = (high * 0x400) + low + 0x10000;
20887 (cc >= 0x4E00 && cc < 0xA000 ) ||
20888 (cc >= 0x3400 && cc < 0x4E00 ) ||
20889 (cc >= 0xf900 && cc < 0xfb00 )
20894 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20895 return "&#" + cc + ";";
20902 if(this.owner.fireEvent('beforesync', this, html) !== false){
20903 this.el.dom.value = html;
20904 this.owner.fireEvent('sync', this, html);
20910 * Protected method that will not generally be called directly. Pushes the value of the textarea
20911 * into the iframe editor.
20913 pushValue : function(){
20914 if(this.initialized){
20915 var v = this.el.dom.value.trim();
20917 // if(v.length < 1){
20921 if(this.owner.fireEvent('beforepush', this, v) !== false){
20922 var d = (this.doc.body || this.doc.documentElement);
20924 this.cleanUpPaste();
20925 this.el.dom.value = d.innerHTML;
20926 this.owner.fireEvent('push', this, v);
20932 deferFocus : function(){
20933 this.focus.defer(10, this);
20937 focus : function(){
20938 if(this.win && !this.sourceEditMode){
20945 assignDocWin: function()
20947 var iframe = this.iframe;
20950 this.doc = iframe.contentWindow.document;
20951 this.win = iframe.contentWindow;
20953 // if (!Roo.get(this.frameId)) {
20956 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20957 // this.win = Roo.get(this.frameId).dom.contentWindow;
20959 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20963 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20964 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20969 initEditor : function(){
20970 //console.log("INIT EDITOR");
20971 this.assignDocWin();
20975 this.doc.designMode="on";
20977 this.doc.write(this.getDocMarkup());
20980 var dbody = (this.doc.body || this.doc.documentElement);
20981 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20982 // this copies styles from the containing element into thsi one..
20983 // not sure why we need all of this..
20984 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20986 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20987 //ss['background-attachment'] = 'fixed'; // w3c
20988 dbody.bgProperties = 'fixed'; // ie
20989 //Roo.DomHelper.applyStyles(dbody, ss);
20990 Roo.EventManager.on(this.doc, {
20991 //'mousedown': this.onEditorEvent,
20992 'mouseup': this.onEditorEvent,
20993 'dblclick': this.onEditorEvent,
20994 'click': this.onEditorEvent,
20995 'keyup': this.onEditorEvent,
21000 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21002 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21003 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21005 this.initialized = true;
21007 this.owner.fireEvent('initialize', this);
21012 onDestroy : function(){
21018 //for (var i =0; i < this.toolbars.length;i++) {
21019 // // fixme - ask toolbars for heights?
21020 // this.toolbars[i].onDestroy();
21023 //this.wrap.dom.innerHTML = '';
21024 //this.wrap.remove();
21029 onFirstFocus : function(){
21031 this.assignDocWin();
21034 this.activated = true;
21037 if(Roo.isGecko){ // prevent silly gecko errors
21039 var s = this.win.getSelection();
21040 if(!s.focusNode || s.focusNode.nodeType != 3){
21041 var r = s.getRangeAt(0);
21042 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21047 this.execCmd('useCSS', true);
21048 this.execCmd('styleWithCSS', false);
21051 this.owner.fireEvent('activate', this);
21055 adjustFont: function(btn){
21056 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21057 //if(Roo.isSafari){ // safari
21060 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21061 if(Roo.isSafari){ // safari
21062 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21063 v = (v < 10) ? 10 : v;
21064 v = (v > 48) ? 48 : v;
21065 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21070 v = Math.max(1, v+adjust);
21072 this.execCmd('FontSize', v );
21075 onEditorEvent : function(e)
21077 this.owner.fireEvent('editorevent', this, e);
21078 // this.updateToolbar();
21079 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21082 insertTag : function(tg)
21084 // could be a bit smarter... -> wrap the current selected tRoo..
21085 if (tg.toLowerCase() == 'span' ||
21086 tg.toLowerCase() == 'code' ||
21087 tg.toLowerCase() == 'sup' ||
21088 tg.toLowerCase() == 'sub'
21091 range = this.createRange(this.getSelection());
21092 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21093 wrappingNode.appendChild(range.extractContents());
21094 range.insertNode(wrappingNode);
21101 this.execCmd("formatblock", tg);
21105 insertText : function(txt)
21109 var range = this.createRange();
21110 range.deleteContents();
21111 //alert(Sender.getAttribute('label'));
21113 range.insertNode(this.doc.createTextNode(txt));
21119 * Executes a Midas editor command on the editor document and performs necessary focus and
21120 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21121 * @param {String} cmd The Midas command
21122 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21124 relayCmd : function(cmd, value){
21126 this.execCmd(cmd, value);
21127 this.owner.fireEvent('editorevent', this);
21128 //this.updateToolbar();
21129 this.owner.deferFocus();
21133 * Executes a Midas editor command directly on the editor document.
21134 * For visual commands, you should use {@link #relayCmd} instead.
21135 * <b>This should only be called after the editor is initialized.</b>
21136 * @param {String} cmd The Midas command
21137 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21139 execCmd : function(cmd, value){
21140 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21147 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21149 * @param {String} text | dom node..
21151 insertAtCursor : function(text)
21154 if(!this.activated){
21160 var r = this.doc.selection.createRange();
21171 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21175 // from jquery ui (MIT licenced)
21177 var win = this.win;
21179 if (win.getSelection && win.getSelection().getRangeAt) {
21180 range = win.getSelection().getRangeAt(0);
21181 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21182 range.insertNode(node);
21183 } else if (win.document.selection && win.document.selection.createRange) {
21184 // no firefox support
21185 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21186 win.document.selection.createRange().pasteHTML(txt);
21188 // no firefox support
21189 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21190 this.execCmd('InsertHTML', txt);
21199 mozKeyPress : function(e){
21201 var c = e.getCharCode(), cmd;
21204 c = String.fromCharCode(c).toLowerCase();
21218 this.cleanUpPaste.defer(100, this);
21226 e.preventDefault();
21234 fixKeys : function(){ // load time branching for fastest keydown performance
21236 return function(e){
21237 var k = e.getKey(), r;
21240 r = this.doc.selection.createRange();
21243 r.pasteHTML('    ');
21250 r = this.doc.selection.createRange();
21252 var target = r.parentElement();
21253 if(!target || target.tagName.toLowerCase() != 'li'){
21255 r.pasteHTML('<br />');
21261 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21262 this.cleanUpPaste.defer(100, this);
21268 }else if(Roo.isOpera){
21269 return function(e){
21270 var k = e.getKey();
21274 this.execCmd('InsertHTML','    ');
21277 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21278 this.cleanUpPaste.defer(100, this);
21283 }else if(Roo.isSafari){
21284 return function(e){
21285 var k = e.getKey();
21289 this.execCmd('InsertText','\t');
21293 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21294 this.cleanUpPaste.defer(100, this);
21302 getAllAncestors: function()
21304 var p = this.getSelectedNode();
21307 a.push(p); // push blank onto stack..
21308 p = this.getParentElement();
21312 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21316 a.push(this.doc.body);
21320 lastSelNode : false,
21323 getSelection : function()
21325 this.assignDocWin();
21326 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21329 getSelectedNode: function()
21331 // this may only work on Gecko!!!
21333 // should we cache this!!!!
21338 var range = this.createRange(this.getSelection()).cloneRange();
21341 var parent = range.parentElement();
21343 var testRange = range.duplicate();
21344 testRange.moveToElementText(parent);
21345 if (testRange.inRange(range)) {
21348 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21351 parent = parent.parentElement;
21356 // is ancestor a text element.
21357 var ac = range.commonAncestorContainer;
21358 if (ac.nodeType == 3) {
21359 ac = ac.parentNode;
21362 var ar = ac.childNodes;
21365 var other_nodes = [];
21366 var has_other_nodes = false;
21367 for (var i=0;i<ar.length;i++) {
21368 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21371 // fullly contained node.
21373 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21378 // probably selected..
21379 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21380 other_nodes.push(ar[i]);
21384 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21389 has_other_nodes = true;
21391 if (!nodes.length && other_nodes.length) {
21392 nodes= other_nodes;
21394 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21400 createRange: function(sel)
21402 // this has strange effects when using with
21403 // top toolbar - not sure if it's a great idea.
21404 //this.editor.contentWindow.focus();
21405 if (typeof sel != "undefined") {
21407 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21409 return this.doc.createRange();
21412 return this.doc.createRange();
21415 getParentElement: function()
21418 this.assignDocWin();
21419 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21421 var range = this.createRange(sel);
21424 var p = range.commonAncestorContainer;
21425 while (p.nodeType == 3) { // text node
21436 * Range intersection.. the hard stuff...
21440 * [ -- selected range --- ]
21444 * if end is before start or hits it. fail.
21445 * if start is after end or hits it fail.
21447 * if either hits (but other is outside. - then it's not
21453 // @see http://www.thismuchiknow.co.uk/?p=64.
21454 rangeIntersectsNode : function(range, node)
21456 var nodeRange = node.ownerDocument.createRange();
21458 nodeRange.selectNode(node);
21460 nodeRange.selectNodeContents(node);
21463 var rangeStartRange = range.cloneRange();
21464 rangeStartRange.collapse(true);
21466 var rangeEndRange = range.cloneRange();
21467 rangeEndRange.collapse(false);
21469 var nodeStartRange = nodeRange.cloneRange();
21470 nodeStartRange.collapse(true);
21472 var nodeEndRange = nodeRange.cloneRange();
21473 nodeEndRange.collapse(false);
21475 return rangeStartRange.compareBoundaryPoints(
21476 Range.START_TO_START, nodeEndRange) == -1 &&
21477 rangeEndRange.compareBoundaryPoints(
21478 Range.START_TO_START, nodeStartRange) == 1;
21482 rangeCompareNode : function(range, node)
21484 var nodeRange = node.ownerDocument.createRange();
21486 nodeRange.selectNode(node);
21488 nodeRange.selectNodeContents(node);
21492 range.collapse(true);
21494 nodeRange.collapse(true);
21496 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21497 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21499 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21501 var nodeIsBefore = ss == 1;
21502 var nodeIsAfter = ee == -1;
21504 if (nodeIsBefore && nodeIsAfter) {
21507 if (!nodeIsBefore && nodeIsAfter) {
21508 return 1; //right trailed.
21511 if (nodeIsBefore && !nodeIsAfter) {
21512 return 2; // left trailed.
21518 // private? - in a new class?
21519 cleanUpPaste : function()
21521 // cleans up the whole document..
21522 Roo.log('cleanuppaste');
21524 this.cleanUpChildren(this.doc.body);
21525 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21526 if (clean != this.doc.body.innerHTML) {
21527 this.doc.body.innerHTML = clean;
21532 cleanWordChars : function(input) {// change the chars to hex code
21533 var he = Roo.HtmlEditorCore;
21535 var output = input;
21536 Roo.each(he.swapCodes, function(sw) {
21537 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21539 output = output.replace(swapper, sw[1]);
21546 cleanUpChildren : function (n)
21548 if (!n.childNodes.length) {
21551 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21552 this.cleanUpChild(n.childNodes[i]);
21559 cleanUpChild : function (node)
21562 //console.log(node);
21563 if (node.nodeName == "#text") {
21564 // clean up silly Windows -- stuff?
21567 if (node.nodeName == "#comment") {
21568 if (!this.allowComments) {
21569 node.parentNode.removeChild(node);
21571 // clean up silly Windows -- stuff?
21574 var lcname = node.tagName.toLowerCase();
21575 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21576 // whitelist of tags..
21578 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21580 node.parentNode.removeChild(node);
21585 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21587 // spans with no attributes - just remove them..
21588 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
21589 remove_keep_children = true;
21592 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21593 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21595 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21596 // remove_keep_children = true;
21599 if (remove_keep_children) {
21600 this.cleanUpChildren(node);
21601 // inserts everything just before this node...
21602 while (node.childNodes.length) {
21603 var cn = node.childNodes[0];
21604 node.removeChild(cn);
21605 node.parentNode.insertBefore(cn, node);
21607 node.parentNode.removeChild(node);
21611 if (!node.attributes || !node.attributes.length) {
21616 this.cleanUpChildren(node);
21620 function cleanAttr(n,v)
21623 if (v.match(/^\./) || v.match(/^\//)) {
21626 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21629 if (v.match(/^#/)) {
21632 if (v.match(/^\{/)) { // allow template editing.
21635 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21636 node.removeAttribute(n);
21640 var cwhite = this.cwhite;
21641 var cblack = this.cblack;
21643 function cleanStyle(n,v)
21645 if (v.match(/expression/)) { //XSS?? should we even bother..
21646 node.removeAttribute(n);
21650 var parts = v.split(/;/);
21653 Roo.each(parts, function(p) {
21654 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21658 var l = p.split(':').shift().replace(/\s+/g,'');
21659 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21661 if ( cwhite.length && cblack.indexOf(l) > -1) {
21662 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21663 //node.removeAttribute(n);
21667 // only allow 'c whitelisted system attributes'
21668 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21669 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21670 //node.removeAttribute(n);
21680 if (clean.length) {
21681 node.setAttribute(n, clean.join(';'));
21683 node.removeAttribute(n);
21689 for (var i = node.attributes.length-1; i > -1 ; i--) {
21690 var a = node.attributes[i];
21693 if (a.name.toLowerCase().substr(0,2)=='on') {
21694 node.removeAttribute(a.name);
21697 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21698 node.removeAttribute(a.name);
21701 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21702 cleanAttr(a.name,a.value); // fixme..
21705 if (a.name == 'style') {
21706 cleanStyle(a.name,a.value);
21709 /// clean up MS crap..
21710 // tecnically this should be a list of valid class'es..
21713 if (a.name == 'class') {
21714 if (a.value.match(/^Mso/)) {
21715 node.removeAttribute('class');
21718 if (a.value.match(/^body$/)) {
21719 node.removeAttribute('class');
21730 this.cleanUpChildren(node);
21736 * Clean up MS wordisms...
21738 cleanWord : function(node)
21741 this.cleanWord(this.doc.body);
21746 node.nodeName == 'SPAN' &&
21747 !node.hasAttributes() &&
21748 node.childNodes.length == 1 &&
21749 node.firstChild.nodeName == "#text"
21751 var textNode = node.firstChild;
21752 node.removeChild(textNode);
21753 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21754 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21756 node.parentNode.insertBefore(textNode, node);
21757 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21758 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21760 node.parentNode.removeChild(node);
21763 if (node.nodeName == "#text") {
21764 // clean up silly Windows -- stuff?
21767 if (node.nodeName == "#comment") {
21768 node.parentNode.removeChild(node);
21769 // clean up silly Windows -- stuff?
21773 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21774 node.parentNode.removeChild(node);
21777 //Roo.log(node.tagName);
21778 // remove - but keep children..
21779 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21780 //Roo.log('-- removed');
21781 while (node.childNodes.length) {
21782 var cn = node.childNodes[0];
21783 node.removeChild(cn);
21784 node.parentNode.insertBefore(cn, node);
21785 // move node to parent - and clean it..
21786 this.cleanWord(cn);
21788 node.parentNode.removeChild(node);
21789 /// no need to iterate chidlren = it's got none..
21790 //this.iterateChildren(node, this.cleanWord);
21794 if (node.className.length) {
21796 var cn = node.className.split(/\W+/);
21798 Roo.each(cn, function(cls) {
21799 if (cls.match(/Mso[a-zA-Z]+/)) {
21804 node.className = cna.length ? cna.join(' ') : '';
21806 node.removeAttribute("class");
21810 if (node.hasAttribute("lang")) {
21811 node.removeAttribute("lang");
21814 if (node.hasAttribute("style")) {
21816 var styles = node.getAttribute("style").split(";");
21818 Roo.each(styles, function(s) {
21819 if (!s.match(/:/)) {
21822 var kv = s.split(":");
21823 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21826 // what ever is left... we allow.
21829 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21830 if (!nstyle.length) {
21831 node.removeAttribute('style');
21834 this.iterateChildren(node, this.cleanWord);
21840 * iterateChildren of a Node, calling fn each time, using this as the scole..
21841 * @param {DomNode} node node to iterate children of.
21842 * @param {Function} fn method of this class to call on each item.
21844 iterateChildren : function(node, fn)
21846 if (!node.childNodes.length) {
21849 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21850 fn.call(this, node.childNodes[i])
21856 * cleanTableWidths.
21858 * Quite often pasting from word etc.. results in tables with column and widths.
21859 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21862 cleanTableWidths : function(node)
21867 this.cleanTableWidths(this.doc.body);
21872 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21875 Roo.log(node.tagName);
21876 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21877 this.iterateChildren(node, this.cleanTableWidths);
21880 if (node.hasAttribute('width')) {
21881 node.removeAttribute('width');
21885 if (node.hasAttribute("style")) {
21888 var styles = node.getAttribute("style").split(";");
21890 Roo.each(styles, function(s) {
21891 if (!s.match(/:/)) {
21894 var kv = s.split(":");
21895 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21898 // what ever is left... we allow.
21901 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21902 if (!nstyle.length) {
21903 node.removeAttribute('style');
21907 this.iterateChildren(node, this.cleanTableWidths);
21915 domToHTML : function(currentElement, depth, nopadtext) {
21917 depth = depth || 0;
21918 nopadtext = nopadtext || false;
21920 if (!currentElement) {
21921 return this.domToHTML(this.doc.body);
21924 //Roo.log(currentElement);
21926 var allText = false;
21927 var nodeName = currentElement.nodeName;
21928 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21930 if (nodeName == '#text') {
21932 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21937 if (nodeName != 'BODY') {
21940 // Prints the node tagName, such as <A>, <IMG>, etc
21943 for(i = 0; i < currentElement.attributes.length;i++) {
21945 var aname = currentElement.attributes.item(i).name;
21946 if (!currentElement.attributes.item(i).value.length) {
21949 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21952 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21961 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21964 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21969 // Traverse the tree
21971 var currentElementChild = currentElement.childNodes.item(i);
21972 var allText = true;
21973 var innerHTML = '';
21975 while (currentElementChild) {
21976 // Formatting code (indent the tree so it looks nice on the screen)
21977 var nopad = nopadtext;
21978 if (lastnode == 'SPAN') {
21982 if (currentElementChild.nodeName == '#text') {
21983 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21984 toadd = nopadtext ? toadd : toadd.trim();
21985 if (!nopad && toadd.length > 80) {
21986 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21988 innerHTML += toadd;
21991 currentElementChild = currentElement.childNodes.item(i);
21997 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21999 // Recursively traverse the tree structure of the child node
22000 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22001 lastnode = currentElementChild.nodeName;
22003 currentElementChild=currentElement.childNodes.item(i);
22009 // The remaining code is mostly for formatting the tree
22010 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22015 ret+= "</"+tagName+">";
22021 applyBlacklists : function()
22023 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22024 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22028 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22029 if (b.indexOf(tag) > -1) {
22032 this.white.push(tag);
22036 Roo.each(w, function(tag) {
22037 if (b.indexOf(tag) > -1) {
22040 if (this.white.indexOf(tag) > -1) {
22043 this.white.push(tag);
22048 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22049 if (w.indexOf(tag) > -1) {
22052 this.black.push(tag);
22056 Roo.each(b, function(tag) {
22057 if (w.indexOf(tag) > -1) {
22060 if (this.black.indexOf(tag) > -1) {
22063 this.black.push(tag);
22068 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22069 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22073 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22074 if (b.indexOf(tag) > -1) {
22077 this.cwhite.push(tag);
22081 Roo.each(w, function(tag) {
22082 if (b.indexOf(tag) > -1) {
22085 if (this.cwhite.indexOf(tag) > -1) {
22088 this.cwhite.push(tag);
22093 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22094 if (w.indexOf(tag) > -1) {
22097 this.cblack.push(tag);
22101 Roo.each(b, function(tag) {
22102 if (w.indexOf(tag) > -1) {
22105 if (this.cblack.indexOf(tag) > -1) {
22108 this.cblack.push(tag);
22113 setStylesheets : function(stylesheets)
22115 if(typeof(stylesheets) == 'string'){
22116 Roo.get(this.iframe.contentDocument.head).createChild({
22118 rel : 'stylesheet',
22127 Roo.each(stylesheets, function(s) {
22132 Roo.get(_this.iframe.contentDocument.head).createChild({
22134 rel : 'stylesheet',
22143 removeStylesheets : function()
22147 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22152 setStyle : function(style)
22154 Roo.get(this.iframe.contentDocument.head).createChild({
22163 // hide stuff that is not compatible
22177 * @event specialkey
22181 * @cfg {String} fieldClass @hide
22184 * @cfg {String} focusClass @hide
22187 * @cfg {String} autoCreate @hide
22190 * @cfg {String} inputType @hide
22193 * @cfg {String} invalidClass @hide
22196 * @cfg {String} invalidText @hide
22199 * @cfg {String} msgFx @hide
22202 * @cfg {String} validateOnBlur @hide
22206 Roo.HtmlEditorCore.white = [
22207 'area', 'br', 'img', 'input', 'hr', 'wbr',
22209 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22210 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22211 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22212 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22213 'table', 'ul', 'xmp',
22215 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22218 'dir', 'menu', 'ol', 'ul', 'dl',
22224 Roo.HtmlEditorCore.black = [
22225 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22227 'base', 'basefont', 'bgsound', 'blink', 'body',
22228 'frame', 'frameset', 'head', 'html', 'ilayer',
22229 'iframe', 'layer', 'link', 'meta', 'object',
22230 'script', 'style' ,'title', 'xml' // clean later..
22232 Roo.HtmlEditorCore.clean = [
22233 'script', 'style', 'title', 'xml'
22235 Roo.HtmlEditorCore.remove = [
22240 Roo.HtmlEditorCore.ablack = [
22244 Roo.HtmlEditorCore.aclean = [
22245 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22249 Roo.HtmlEditorCore.pwhite= [
22250 'http', 'https', 'mailto'
22253 // white listed style attributes.
22254 Roo.HtmlEditorCore.cwhite= [
22255 // 'text-align', /// default is to allow most things..
22261 // black listed style attributes.
22262 Roo.HtmlEditorCore.cblack= [
22263 // 'font-size' -- this can be set by the project
22267 Roo.HtmlEditorCore.swapCodes =[
22268 [ 8211, "–" ],
22269 [ 8212, "—" ],
22278 //<script type="text/javascript">
22281 * Ext JS Library 1.1.1
22282 * Copyright(c) 2006-2007, Ext JS, LLC.
22288 Roo.form.HtmlEditor = function(config){
22292 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22294 if (!this.toolbars) {
22295 this.toolbars = [];
22297 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22303 * @class Roo.form.HtmlEditor
22304 * @extends Roo.form.Field
22305 * Provides a lightweight HTML Editor component.
22307 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22309 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22310 * supported by this editor.</b><br/><br/>
22311 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22312 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22314 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22316 * @cfg {Boolean} clearUp
22320 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22325 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22330 * @cfg {Number} height (in pixels)
22334 * @cfg {Number} width (in pixels)
22339 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22342 stylesheets: false,
22346 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22351 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22357 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22362 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22367 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22369 allowComments: false,
22374 // private properties
22375 validationEvent : false,
22377 initialized : false,
22380 onFocus : Roo.emptyFn,
22382 hideMode:'offsets',
22384 actionMode : 'container', // defaults to hiding it...
22386 defaultAutoCreate : { // modified by initCompnoent..
22388 style:"width:500px;height:300px;",
22389 autocomplete: "new-password"
22393 initComponent : function(){
22396 * @event initialize
22397 * Fires when the editor is fully initialized (including the iframe)
22398 * @param {HtmlEditor} this
22403 * Fires when the editor is first receives the focus. Any insertion must wait
22404 * until after this event.
22405 * @param {HtmlEditor} this
22409 * @event beforesync
22410 * Fires before the textarea is updated with content from the editor iframe. Return false
22411 * to cancel the sync.
22412 * @param {HtmlEditor} this
22413 * @param {String} html
22417 * @event beforepush
22418 * Fires before the iframe editor is updated with content from the textarea. Return false
22419 * to cancel the push.
22420 * @param {HtmlEditor} this
22421 * @param {String} html
22426 * Fires when the textarea is updated with content from the editor iframe.
22427 * @param {HtmlEditor} this
22428 * @param {String} html
22433 * Fires when the iframe editor is updated with content from the textarea.
22434 * @param {HtmlEditor} this
22435 * @param {String} html
22439 * @event editmodechange
22440 * Fires when the editor switches edit modes
22441 * @param {HtmlEditor} this
22442 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22444 editmodechange: true,
22446 * @event editorevent
22447 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22448 * @param {HtmlEditor} this
22452 * @event firstfocus
22453 * Fires when on first focus - needed by toolbars..
22454 * @param {HtmlEditor} this
22459 * Auto save the htmlEditor value as a file into Events
22460 * @param {HtmlEditor} this
22464 * @event savedpreview
22465 * preview the saved version of htmlEditor
22466 * @param {HtmlEditor} this
22468 savedpreview: true,
22471 * @event stylesheetsclick
22472 * Fires when press the Sytlesheets button
22473 * @param {Roo.HtmlEditorCore} this
22475 stylesheetsclick: true
22477 this.defaultAutoCreate = {
22479 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22480 autocomplete: "new-password"
22485 * Protected method that will not generally be called directly. It
22486 * is called when the editor creates its toolbar. Override this method if you need to
22487 * add custom toolbar buttons.
22488 * @param {HtmlEditor} editor
22490 createToolbar : function(editor){
22491 Roo.log("create toolbars");
22492 if (!editor.toolbars || !editor.toolbars.length) {
22493 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22496 for (var i =0 ; i < editor.toolbars.length;i++) {
22497 editor.toolbars[i] = Roo.factory(
22498 typeof(editor.toolbars[i]) == 'string' ?
22499 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22500 Roo.form.HtmlEditor);
22501 editor.toolbars[i].init(editor);
22509 onRender : function(ct, position)
22512 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22514 this.wrap = this.el.wrap({
22515 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22518 this.editorcore.onRender(ct, position);
22520 if (this.resizable) {
22521 this.resizeEl = new Roo.Resizable(this.wrap, {
22525 minHeight : this.height,
22526 height: this.height,
22527 handles : this.resizable,
22530 resize : function(r, w, h) {
22531 _t.onResize(w,h); // -something
22537 this.createToolbar(this);
22541 this.setSize(this.wrap.getSize());
22543 if (this.resizeEl) {
22544 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22545 // should trigger onReize..
22548 this.keyNav = new Roo.KeyNav(this.el, {
22550 "tab" : function(e){
22551 e.preventDefault();
22553 var value = this.getValue();
22555 var start = this.el.dom.selectionStart;
22556 var end = this.el.dom.selectionEnd;
22560 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22561 this.el.dom.setSelectionRange(end + 1, end + 1);
22565 var f = value.substring(0, start).split("\t");
22567 if(f.pop().length != 0){
22571 this.setValue(f.join("\t") + value.substring(end));
22572 this.el.dom.setSelectionRange(start - 1, start - 1);
22576 "home" : function(e){
22577 e.preventDefault();
22579 var curr = this.el.dom.selectionStart;
22580 var lines = this.getValue().split("\n");
22587 this.el.dom.setSelectionRange(0, 0);
22593 for (var i = 0; i < lines.length;i++) {
22594 pos += lines[i].length;
22604 pos -= lines[i].length;
22610 this.el.dom.setSelectionRange(pos, pos);
22614 this.el.dom.selectionStart = pos;
22615 this.el.dom.selectionEnd = curr;
22618 "end" : function(e){
22619 e.preventDefault();
22621 var curr = this.el.dom.selectionStart;
22622 var lines = this.getValue().split("\n");
22629 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22635 for (var i = 0; i < lines.length;i++) {
22637 pos += lines[i].length;
22651 this.el.dom.setSelectionRange(pos, pos);
22655 this.el.dom.selectionStart = curr;
22656 this.el.dom.selectionEnd = pos;
22661 doRelay : function(foo, bar, hname){
22662 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22668 // if(this.autosave && this.w){
22669 // this.autoSaveFn = setInterval(this.autosave, 1000);
22674 onResize : function(w, h)
22676 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22681 if(typeof w == 'number'){
22682 var aw = w - this.wrap.getFrameWidth('lr');
22683 this.el.setWidth(this.adjustWidth('textarea', aw));
22686 if(typeof h == 'number'){
22688 for (var i =0; i < this.toolbars.length;i++) {
22689 // fixme - ask toolbars for heights?
22690 tbh += this.toolbars[i].tb.el.getHeight();
22691 if (this.toolbars[i].footer) {
22692 tbh += this.toolbars[i].footer.el.getHeight();
22699 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22700 ah -= 5; // knock a few pixes off for look..
22702 this.el.setHeight(this.adjustWidth('textarea', ah));
22706 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22707 this.editorcore.onResize(ew,eh);
22712 * Toggles the editor between standard and source edit mode.
22713 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22715 toggleSourceEdit : function(sourceEditMode)
22717 this.editorcore.toggleSourceEdit(sourceEditMode);
22719 if(this.editorcore.sourceEditMode){
22720 Roo.log('editor - showing textarea');
22723 // Roo.log(this.syncValue());
22724 this.editorcore.syncValue();
22725 this.el.removeClass('x-hidden');
22726 this.el.dom.removeAttribute('tabIndex');
22729 for (var i = 0; i < this.toolbars.length; i++) {
22730 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22731 this.toolbars[i].tb.hide();
22732 this.toolbars[i].footer.hide();
22737 Roo.log('editor - hiding textarea');
22739 // Roo.log(this.pushValue());
22740 this.editorcore.pushValue();
22742 this.el.addClass('x-hidden');
22743 this.el.dom.setAttribute('tabIndex', -1);
22745 for (var i = 0; i < this.toolbars.length; i++) {
22746 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22747 this.toolbars[i].tb.show();
22748 this.toolbars[i].footer.show();
22752 //this.deferFocus();
22755 this.setSize(this.wrap.getSize());
22756 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22758 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22761 // private (for BoxComponent)
22762 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22764 // private (for BoxComponent)
22765 getResizeEl : function(){
22769 // private (for BoxComponent)
22770 getPositionEl : function(){
22775 initEvents : function(){
22776 this.originalValue = this.getValue();
22780 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22783 markInvalid : Roo.emptyFn,
22785 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22788 clearInvalid : Roo.emptyFn,
22790 setValue : function(v){
22791 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22792 this.editorcore.pushValue();
22797 deferFocus : function(){
22798 this.focus.defer(10, this);
22802 focus : function(){
22803 this.editorcore.focus();
22809 onDestroy : function(){
22815 for (var i =0; i < this.toolbars.length;i++) {
22816 // fixme - ask toolbars for heights?
22817 this.toolbars[i].onDestroy();
22820 this.wrap.dom.innerHTML = '';
22821 this.wrap.remove();
22826 onFirstFocus : function(){
22827 //Roo.log("onFirstFocus");
22828 this.editorcore.onFirstFocus();
22829 for (var i =0; i < this.toolbars.length;i++) {
22830 this.toolbars[i].onFirstFocus();
22836 syncValue : function()
22838 this.editorcore.syncValue();
22841 pushValue : function()
22843 this.editorcore.pushValue();
22846 setStylesheets : function(stylesheets)
22848 this.editorcore.setStylesheets(stylesheets);
22851 removeStylesheets : function()
22853 this.editorcore.removeStylesheets();
22857 // hide stuff that is not compatible
22871 * @event specialkey
22875 * @cfg {String} fieldClass @hide
22878 * @cfg {String} focusClass @hide
22881 * @cfg {String} autoCreate @hide
22884 * @cfg {String} inputType @hide
22887 * @cfg {String} invalidClass @hide
22890 * @cfg {String} invalidText @hide
22893 * @cfg {String} msgFx @hide
22896 * @cfg {String} validateOnBlur @hide
22900 // <script type="text/javascript">
22903 * Ext JS Library 1.1.1
22904 * Copyright(c) 2006-2007, Ext JS, LLC.
22910 * @class Roo.form.HtmlEditorToolbar1
22915 new Roo.form.HtmlEditor({
22918 new Roo.form.HtmlEditorToolbar1({
22919 disable : { fonts: 1 , format: 1, ..., ... , ...],
22925 * @cfg {Object} disable List of elements to disable..
22926 * @cfg {Array} btns List of additional buttons.
22930 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22933 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22936 Roo.apply(this, config);
22938 // default disabled, based on 'good practice'..
22939 this.disable = this.disable || {};
22940 Roo.applyIf(this.disable, {
22943 specialElements : true
22947 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22948 // dont call parent... till later.
22951 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
22958 editorcore : false,
22960 * @cfg {Object} disable List of toolbar elements to disable
22967 * @cfg {String} createLinkText The default text for the create link prompt
22969 createLinkText : 'Please enter the URL for the link:',
22971 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22973 defaultLinkValue : 'http:/'+'/',
22977 * @cfg {Array} fontFamilies An array of available font families
22995 // "á" , ?? a acute?
23000 "°" // , // degrees
23002 // "é" , // e ecute
23003 // "ú" , // u ecute?
23006 specialElements : [
23008 text: "Insert Table",
23011 ihtml : '<table><tr><td>Cell</td></tr></table>'
23015 text: "Insert Image",
23018 ihtml : '<img src="about:blank"/>'
23027 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
23028 "input:submit", "input:button", "select", "textarea", "label" ],
23031 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
23033 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23042 * @cfg {String} defaultFont default font to use.
23044 defaultFont: 'tahoma',
23046 fontSelect : false,
23049 formatCombo : false,
23051 init : function(editor)
23053 this.editor = editor;
23054 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23055 var editorcore = this.editorcore;
23059 var fid = editorcore.frameId;
23061 function btn(id, toggle, handler){
23062 var xid = fid + '-'+ id ;
23066 cls : 'x-btn-icon x-edit-'+id,
23067 enableToggle:toggle !== false,
23068 scope: _t, // was editor...
23069 handler:handler||_t.relayBtnCmd,
23070 clickEvent:'mousedown',
23071 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23078 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23080 // stop form submits
23081 tb.el.on('click', function(e){
23082 e.preventDefault(); // what does this do?
23085 if(!this.disable.font) { // && !Roo.isSafari){
23086 /* why no safari for fonts
23087 editor.fontSelect = tb.el.createChild({
23090 cls:'x-font-select',
23091 html: this.createFontOptions()
23094 editor.fontSelect.on('change', function(){
23095 var font = editor.fontSelect.dom.value;
23096 editor.relayCmd('fontname', font);
23097 editor.deferFocus();
23101 editor.fontSelect.dom,
23107 if(!this.disable.formats){
23108 this.formatCombo = new Roo.form.ComboBox({
23109 store: new Roo.data.SimpleStore({
23112 data : this.formats // from states.js
23116 //autoCreate : {tag: "div", size: "20"},
23117 displayField:'tag',
23121 triggerAction: 'all',
23122 emptyText:'Add tag',
23123 selectOnFocus:true,
23126 'select': function(c, r, i) {
23127 editorcore.insertTag(r.get('tag'));
23133 tb.addField(this.formatCombo);
23137 if(!this.disable.format){
23142 btn('strikethrough')
23145 if(!this.disable.fontSize){
23150 btn('increasefontsize', false, editorcore.adjustFont),
23151 btn('decreasefontsize', false, editorcore.adjustFont)
23156 if(!this.disable.colors){
23159 id:editorcore.frameId +'-forecolor',
23160 cls:'x-btn-icon x-edit-forecolor',
23161 clickEvent:'mousedown',
23162 tooltip: this.buttonTips['forecolor'] || undefined,
23164 menu : new Roo.menu.ColorMenu({
23165 allowReselect: true,
23166 focus: Roo.emptyFn,
23169 selectHandler: function(cp, color){
23170 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23171 editor.deferFocus();
23174 clickEvent:'mousedown'
23177 id:editorcore.frameId +'backcolor',
23178 cls:'x-btn-icon x-edit-backcolor',
23179 clickEvent:'mousedown',
23180 tooltip: this.buttonTips['backcolor'] || undefined,
23182 menu : new Roo.menu.ColorMenu({
23183 focus: Roo.emptyFn,
23186 allowReselect: true,
23187 selectHandler: function(cp, color){
23189 editorcore.execCmd('useCSS', false);
23190 editorcore.execCmd('hilitecolor', color);
23191 editorcore.execCmd('useCSS', true);
23192 editor.deferFocus();
23194 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23195 Roo.isSafari || Roo.isIE ? '#'+color : color);
23196 editor.deferFocus();
23200 clickEvent:'mousedown'
23205 // now add all the items...
23208 if(!this.disable.alignments){
23211 btn('justifyleft'),
23212 btn('justifycenter'),
23213 btn('justifyright')
23217 //if(!Roo.isSafari){
23218 if(!this.disable.links){
23221 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23225 if(!this.disable.lists){
23228 btn('insertorderedlist'),
23229 btn('insertunorderedlist')
23232 if(!this.disable.sourceEdit){
23235 btn('sourceedit', true, function(btn){
23236 this.toggleSourceEdit(btn.pressed);
23243 // special menu.. - needs to be tidied up..
23244 if (!this.disable.special) {
23247 cls: 'x-edit-none',
23253 for (var i =0; i < this.specialChars.length; i++) {
23254 smenu.menu.items.push({
23256 html: this.specialChars[i],
23257 handler: function(a,b) {
23258 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23259 //editor.insertAtCursor(a.html);
23273 if (!this.disable.cleanStyles) {
23275 cls: 'x-btn-icon x-btn-clear',
23281 for (var i =0; i < this.cleanStyles.length; i++) {
23282 cmenu.menu.items.push({
23283 actiontype : this.cleanStyles[i],
23284 html: 'Remove ' + this.cleanStyles[i],
23285 handler: function(a,b) {
23288 var c = Roo.get(editorcore.doc.body);
23289 c.select('[style]').each(function(s) {
23290 s.dom.style.removeProperty(a.actiontype);
23292 editorcore.syncValue();
23297 cmenu.menu.items.push({
23298 actiontype : 'tablewidths',
23299 html: 'Remove Table Widths',
23300 handler: function(a,b) {
23301 editorcore.cleanTableWidths();
23302 editorcore.syncValue();
23306 cmenu.menu.items.push({
23307 actiontype : 'word',
23308 html: 'Remove MS Word Formating',
23309 handler: function(a,b) {
23310 editorcore.cleanWord();
23311 editorcore.syncValue();
23316 cmenu.menu.items.push({
23317 actiontype : 'all',
23318 html: 'Remove All Styles',
23319 handler: function(a,b) {
23321 var c = Roo.get(editorcore.doc.body);
23322 c.select('[style]').each(function(s) {
23323 s.dom.removeAttribute('style');
23325 editorcore.syncValue();
23330 cmenu.menu.items.push({
23331 actiontype : 'all',
23332 html: 'Remove All CSS Classes',
23333 handler: function(a,b) {
23335 var c = Roo.get(editorcore.doc.body);
23336 c.select('[class]').each(function(s) {
23337 s.dom.removeAttribute('class');
23339 editorcore.cleanWord();
23340 editorcore.syncValue();
23345 cmenu.menu.items.push({
23346 actiontype : 'tidy',
23347 html: 'Tidy HTML Source',
23348 handler: function(a,b) {
23349 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23350 editorcore.syncValue();
23359 if (!this.disable.specialElements) {
23362 cls: 'x-edit-none',
23367 for (var i =0; i < this.specialElements.length; i++) {
23368 semenu.menu.items.push(
23370 handler: function(a,b) {
23371 editor.insertAtCursor(this.ihtml);
23373 }, this.specialElements[i])
23385 for(var i =0; i< this.btns.length;i++) {
23386 var b = Roo.factory(this.btns[i],Roo.form);
23387 b.cls = 'x-edit-none';
23389 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23390 b.cls += ' x-init-enable';
23393 b.scope = editorcore;
23401 // disable everything...
23403 this.tb.items.each(function(item){
23406 item.id != editorcore.frameId+ '-sourceedit' &&
23407 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23413 this.rendered = true;
23415 // the all the btns;
23416 editor.on('editorevent', this.updateToolbar, this);
23417 // other toolbars need to implement this..
23418 //editor.on('editmodechange', this.updateToolbar, this);
23422 relayBtnCmd : function(btn) {
23423 this.editorcore.relayCmd(btn.cmd);
23425 // private used internally
23426 createLink : function(){
23427 Roo.log("create link?");
23428 var url = prompt(this.createLinkText, this.defaultLinkValue);
23429 if(url && url != 'http:/'+'/'){
23430 this.editorcore.relayCmd('createlink', url);
23436 * Protected method that will not generally be called directly. It triggers
23437 * a toolbar update by reading the markup state of the current selection in the editor.
23439 updateToolbar: function(){
23441 if(!this.editorcore.activated){
23442 this.editor.onFirstFocus();
23446 var btns = this.tb.items.map,
23447 doc = this.editorcore.doc,
23448 frameId = this.editorcore.frameId;
23450 if(!this.disable.font && !Roo.isSafari){
23452 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23453 if(name != this.fontSelect.dom.value){
23454 this.fontSelect.dom.value = name;
23458 if(!this.disable.format){
23459 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23460 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23461 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23462 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23464 if(!this.disable.alignments){
23465 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23466 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23467 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23469 if(!Roo.isSafari && !this.disable.lists){
23470 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23471 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23474 var ans = this.editorcore.getAllAncestors();
23475 if (this.formatCombo) {
23478 var store = this.formatCombo.store;
23479 this.formatCombo.setValue("");
23480 for (var i =0; i < ans.length;i++) {
23481 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23483 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23491 // hides menus... - so this cant be on a menu...
23492 Roo.menu.MenuMgr.hideAll();
23494 //this.editorsyncValue();
23498 createFontOptions : function(){
23499 var buf = [], fs = this.fontFamilies, ff, lc;
23503 for(var i = 0, len = fs.length; i< len; i++){
23505 lc = ff.toLowerCase();
23507 '<option value="',lc,'" style="font-family:',ff,';"',
23508 (this.defaultFont == lc ? ' selected="true">' : '>'),
23513 return buf.join('');
23516 toggleSourceEdit : function(sourceEditMode){
23518 Roo.log("toolbar toogle");
23519 if(sourceEditMode === undefined){
23520 sourceEditMode = !this.sourceEditMode;
23522 this.sourceEditMode = sourceEditMode === true;
23523 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23524 // just toggle the button?
23525 if(btn.pressed !== this.sourceEditMode){
23526 btn.toggle(this.sourceEditMode);
23530 if(sourceEditMode){
23531 Roo.log("disabling buttons");
23532 this.tb.items.each(function(item){
23533 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23539 Roo.log("enabling buttons");
23540 if(this.editorcore.initialized){
23541 this.tb.items.each(function(item){
23547 Roo.log("calling toggole on editor");
23548 // tell the editor that it's been pressed..
23549 this.editor.toggleSourceEdit(sourceEditMode);
23553 * Object collection of toolbar tooltips for the buttons in the editor. The key
23554 * is the command id associated with that button and the value is a valid QuickTips object.
23559 title: 'Bold (Ctrl+B)',
23560 text: 'Make the selected text bold.',
23561 cls: 'x-html-editor-tip'
23564 title: 'Italic (Ctrl+I)',
23565 text: 'Make the selected text italic.',
23566 cls: 'x-html-editor-tip'
23574 title: 'Bold (Ctrl+B)',
23575 text: 'Make the selected text bold.',
23576 cls: 'x-html-editor-tip'
23579 title: 'Italic (Ctrl+I)',
23580 text: 'Make the selected text italic.',
23581 cls: 'x-html-editor-tip'
23584 title: 'Underline (Ctrl+U)',
23585 text: 'Underline the selected text.',
23586 cls: 'x-html-editor-tip'
23589 title: 'Strikethrough',
23590 text: 'Strikethrough the selected text.',
23591 cls: 'x-html-editor-tip'
23593 increasefontsize : {
23594 title: 'Grow Text',
23595 text: 'Increase the font size.',
23596 cls: 'x-html-editor-tip'
23598 decreasefontsize : {
23599 title: 'Shrink Text',
23600 text: 'Decrease the font size.',
23601 cls: 'x-html-editor-tip'
23604 title: 'Text Highlight Color',
23605 text: 'Change the background color of the selected text.',
23606 cls: 'x-html-editor-tip'
23609 title: 'Font Color',
23610 text: 'Change the color of the selected text.',
23611 cls: 'x-html-editor-tip'
23614 title: 'Align Text Left',
23615 text: 'Align text to the left.',
23616 cls: 'x-html-editor-tip'
23619 title: 'Center Text',
23620 text: 'Center text in the editor.',
23621 cls: 'x-html-editor-tip'
23624 title: 'Align Text Right',
23625 text: 'Align text to the right.',
23626 cls: 'x-html-editor-tip'
23628 insertunorderedlist : {
23629 title: 'Bullet List',
23630 text: 'Start a bulleted list.',
23631 cls: 'x-html-editor-tip'
23633 insertorderedlist : {
23634 title: 'Numbered List',
23635 text: 'Start a numbered list.',
23636 cls: 'x-html-editor-tip'
23639 title: 'Hyperlink',
23640 text: 'Make the selected text a hyperlink.',
23641 cls: 'x-html-editor-tip'
23644 title: 'Source Edit',
23645 text: 'Switch to source editing mode.',
23646 cls: 'x-html-editor-tip'
23650 onDestroy : function(){
23653 this.tb.items.each(function(item){
23655 item.menu.removeAll();
23657 item.menu.el.destroy();
23665 onFirstFocus: function() {
23666 this.tb.items.each(function(item){
23675 // <script type="text/javascript">
23678 * Ext JS Library 1.1.1
23679 * Copyright(c) 2006-2007, Ext JS, LLC.
23686 * @class Roo.form.HtmlEditor.ToolbarContext
23691 new Roo.form.HtmlEditor({
23694 { xtype: 'ToolbarStandard', styles : {} }
23695 { xtype: 'ToolbarContext', disable : {} }
23701 * @config : {Object} disable List of elements to disable.. (not done yet.)
23702 * @config : {Object} styles Map of styles available.
23706 Roo.form.HtmlEditor.ToolbarContext = function(config)
23709 Roo.apply(this, config);
23710 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23711 // dont call parent... till later.
23712 this.styles = this.styles || {};
23717 Roo.form.HtmlEditor.ToolbarContext.types = {
23729 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23795 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23800 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23810 style : 'fontFamily',
23811 displayField: 'display',
23812 optname : 'font-family',
23861 // should we really allow this??
23862 // should this just be
23873 style : 'fontFamily',
23874 displayField: 'display',
23875 optname : 'font-family',
23882 style : 'fontFamily',
23883 displayField: 'display',
23884 optname : 'font-family',
23891 style : 'fontFamily',
23892 displayField: 'display',
23893 optname : 'font-family',
23904 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23905 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23907 Roo.form.HtmlEditor.ToolbarContext.options = {
23909 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23910 [ 'Courier New', 'Courier New'],
23911 [ 'Tahoma', 'Tahoma'],
23912 [ 'Times New Roman,serif', 'Times'],
23913 [ 'Verdana','Verdana' ]
23917 // fixme - these need to be configurable..
23920 //Roo.form.HtmlEditor.ToolbarContext.types
23923 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
23930 editorcore : false,
23932 * @cfg {Object} disable List of toolbar elements to disable
23937 * @cfg {Object} styles List of styles
23938 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
23940 * These must be defined in the page, so they get rendered correctly..
23951 init : function(editor)
23953 this.editor = editor;
23954 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23955 var editorcore = this.editorcore;
23957 var fid = editorcore.frameId;
23959 function btn(id, toggle, handler){
23960 var xid = fid + '-'+ id ;
23964 cls : 'x-btn-icon x-edit-'+id,
23965 enableToggle:toggle !== false,
23966 scope: editorcore, // was editor...
23967 handler:handler||editorcore.relayBtnCmd,
23968 clickEvent:'mousedown',
23969 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23973 // create a new element.
23974 var wdiv = editor.wrap.createChild({
23976 }, editor.wrap.dom.firstChild.nextSibling, true);
23978 // can we do this more than once??
23980 // stop form submits
23983 // disable everything...
23984 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23985 this.toolbars = {};
23987 for (var i in ty) {
23989 this.toolbars[i] = this.buildToolbar(ty[i],i);
23991 this.tb = this.toolbars.BODY;
23993 this.buildFooter();
23994 this.footer.show();
23995 editor.on('hide', function( ) { this.footer.hide() }, this);
23996 editor.on('show', function( ) { this.footer.show() }, this);
23999 this.rendered = true;
24001 // the all the btns;
24002 editor.on('editorevent', this.updateToolbar, this);
24003 // other toolbars need to implement this..
24004 //editor.on('editmodechange', this.updateToolbar, this);
24010 * Protected method that will not generally be called directly. It triggers
24011 * a toolbar update by reading the markup state of the current selection in the editor.
24013 * Note you can force an update by calling on('editorevent', scope, false)
24015 updateToolbar: function(editor,ev,sel){
24018 // capture mouse up - this is handy for selecting images..
24019 // perhaps should go somewhere else...
24020 if(!this.editorcore.activated){
24021 this.editor.onFirstFocus();
24027 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24028 // selectNode - might want to handle IE?
24030 (ev.type == 'mouseup' || ev.type == 'click' ) &&
24031 ev.target && ev.target.tagName == 'IMG') {
24032 // they have click on an image...
24033 // let's see if we can change the selection...
24036 var nodeRange = sel.ownerDocument.createRange();
24038 nodeRange.selectNode(sel);
24040 nodeRange.selectNodeContents(sel);
24042 //nodeRange.collapse(true);
24043 var s = this.editorcore.win.getSelection();
24044 s.removeAllRanges();
24045 s.addRange(nodeRange);
24049 var updateFooter = sel ? false : true;
24052 var ans = this.editorcore.getAllAncestors();
24055 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24058 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
24059 sel = sel ? sel : this.editorcore.doc.body;
24060 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24063 // pick a menu that exists..
24064 var tn = sel.tagName.toUpperCase();
24065 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24067 tn = sel.tagName.toUpperCase();
24069 var lastSel = this.tb.selectedNode;
24071 this.tb.selectedNode = sel;
24073 // if current menu does not match..
24075 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24078 ///console.log("show: " + tn);
24079 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24082 this.tb.items.first().el.innerHTML = tn + ': ';
24085 // update attributes
24086 if (this.tb.fields) {
24087 this.tb.fields.each(function(e) {
24089 e.setValue(sel.style[e.stylename]);
24092 e.setValue(sel.getAttribute(e.attrname));
24096 var hasStyles = false;
24097 for(var i in this.styles) {
24104 var st = this.tb.fields.item(0);
24106 st.store.removeAll();
24109 var cn = sel.className.split(/\s+/);
24112 if (this.styles['*']) {
24114 Roo.each(this.styles['*'], function(v) {
24115 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24118 if (this.styles[tn]) {
24119 Roo.each(this.styles[tn], function(v) {
24120 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24124 st.store.loadData(avs);
24128 // flag our selected Node.
24129 this.tb.selectedNode = sel;
24132 Roo.menu.MenuMgr.hideAll();
24136 if (!updateFooter) {
24137 //this.footDisp.dom.innerHTML = '';
24140 // update the footer
24144 this.footerEls = ans.reverse();
24145 Roo.each(this.footerEls, function(a,i) {
24146 if (!a) { return; }
24147 html += html.length ? ' > ' : '';
24149 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24154 var sz = this.footDisp.up('td').getSize();
24155 this.footDisp.dom.style.width = (sz.width -10) + 'px';
24156 this.footDisp.dom.style.marginLeft = '5px';
24158 this.footDisp.dom.style.overflow = 'hidden';
24160 this.footDisp.dom.innerHTML = html;
24162 //this.editorsyncValue();
24169 onDestroy : function(){
24172 this.tb.items.each(function(item){
24174 item.menu.removeAll();
24176 item.menu.el.destroy();
24184 onFirstFocus: function() {
24185 // need to do this for all the toolbars..
24186 this.tb.items.each(function(item){
24190 buildToolbar: function(tlist, nm)
24192 var editor = this.editor;
24193 var editorcore = this.editorcore;
24194 // create a new element.
24195 var wdiv = editor.wrap.createChild({
24197 }, editor.wrap.dom.firstChild.nextSibling, true);
24200 var tb = new Roo.Toolbar(wdiv);
24203 tb.add(nm+ ": ");
24206 for(var i in this.styles) {
24211 if (styles && styles.length) {
24213 // this needs a multi-select checkbox...
24214 tb.addField( new Roo.form.ComboBox({
24215 store: new Roo.data.SimpleStore({
24217 fields: ['val', 'selected'],
24220 name : '-roo-edit-className',
24221 attrname : 'className',
24222 displayField: 'val',
24226 triggerAction: 'all',
24227 emptyText:'Select Style',
24228 selectOnFocus:true,
24231 'select': function(c, r, i) {
24232 // initial support only for on class per el..
24233 tb.selectedNode.className = r ? r.get('val') : '';
24234 editorcore.syncValue();
24241 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24242 var tbops = tbc.options;
24244 for (var i in tlist) {
24246 var item = tlist[i];
24247 tb.add(item.title + ": ");
24250 //optname == used so you can configure the options available..
24251 var opts = item.opts ? item.opts : false;
24252 if (item.optname) {
24253 opts = tbops[item.optname];
24258 // opts == pulldown..
24259 tb.addField( new Roo.form.ComboBox({
24260 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24262 fields: ['val', 'display'],
24265 name : '-roo-edit-' + i,
24267 stylename : item.style ? item.style : false,
24268 displayField: item.displayField ? item.displayField : 'val',
24269 valueField : 'val',
24271 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24273 triggerAction: 'all',
24274 emptyText:'Select',
24275 selectOnFocus:true,
24276 width: item.width ? item.width : 130,
24278 'select': function(c, r, i) {
24280 tb.selectedNode.style[c.stylename] = r.get('val');
24283 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24292 tb.addField( new Roo.form.TextField({
24295 //allowBlank:false,
24300 tb.addField( new Roo.form.TextField({
24301 name: '-roo-edit-' + i,
24308 'change' : function(f, nv, ov) {
24309 tb.selectedNode.setAttribute(f.attrname, nv);
24310 editorcore.syncValue();
24323 text: 'Stylesheets',
24326 click : function ()
24328 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24336 text: 'Remove Tag',
24339 click : function ()
24342 // undo does not work.
24344 var sn = tb.selectedNode;
24346 var pn = sn.parentNode;
24348 var stn = sn.childNodes[0];
24349 var en = sn.childNodes[sn.childNodes.length - 1 ];
24350 while (sn.childNodes.length) {
24351 var node = sn.childNodes[0];
24352 sn.removeChild(node);
24354 pn.insertBefore(node, sn);
24357 pn.removeChild(sn);
24358 var range = editorcore.createRange();
24360 range.setStart(stn,0);
24361 range.setEnd(en,0); //????
24362 //range.selectNode(sel);
24365 var selection = editorcore.getSelection();
24366 selection.removeAllRanges();
24367 selection.addRange(range);
24371 //_this.updateToolbar(null, null, pn);
24372 _this.updateToolbar(null, null, null);
24373 _this.footDisp.dom.innerHTML = '';
24383 tb.el.on('click', function(e){
24384 e.preventDefault(); // what does this do?
24386 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24389 // dont need to disable them... as they will get hidden
24394 buildFooter : function()
24397 var fel = this.editor.wrap.createChild();
24398 this.footer = new Roo.Toolbar(fel);
24399 // toolbar has scrolly on left / right?
24400 var footDisp= new Roo.Toolbar.Fill();
24406 handler : function() {
24407 _t.footDisp.scrollTo('left',0,true)
24411 this.footer.add( footDisp );
24416 handler : function() {
24418 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24422 var fel = Roo.get(footDisp.el);
24423 fel.addClass('x-editor-context');
24424 this.footDispWrap = fel;
24425 this.footDispWrap.overflow = 'hidden';
24427 this.footDisp = fel.createChild();
24428 this.footDispWrap.on('click', this.onContextClick, this)
24432 onContextClick : function (ev,dom)
24434 ev.preventDefault();
24435 var cn = dom.className;
24437 if (!cn.match(/x-ed-loc-/)) {
24440 var n = cn.split('-').pop();
24441 var ans = this.footerEls;
24445 var range = this.editorcore.createRange();
24447 range.selectNodeContents(sel);
24448 //range.selectNode(sel);
24451 var selection = this.editorcore.getSelection();
24452 selection.removeAllRanges();
24453 selection.addRange(range);
24457 this.updateToolbar(null, null, sel);
24474 * Ext JS Library 1.1.1
24475 * Copyright(c) 2006-2007, Ext JS, LLC.
24477 * Originally Released Under LGPL - original licence link has changed is not relivant.
24480 * <script type="text/javascript">
24484 * @class Roo.form.BasicForm
24485 * @extends Roo.util.Observable
24486 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24488 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24489 * @param {Object} config Configuration options
24491 Roo.form.BasicForm = function(el, config){
24492 this.allItems = [];
24493 this.childForms = [];
24494 Roo.apply(this, config);
24496 * The Roo.form.Field items in this form.
24497 * @type MixedCollection
24501 this.items = new Roo.util.MixedCollection(false, function(o){
24502 return o.id || (o.id = Roo.id());
24506 * @event beforeaction
24507 * Fires before any action is performed. Return false to cancel the action.
24508 * @param {Form} this
24509 * @param {Action} action The action to be performed
24511 beforeaction: true,
24513 * @event actionfailed
24514 * Fires when an action fails.
24515 * @param {Form} this
24516 * @param {Action} action The action that failed
24518 actionfailed : true,
24520 * @event actioncomplete
24521 * Fires when an action is completed.
24522 * @param {Form} this
24523 * @param {Action} action The action that completed
24525 actioncomplete : true
24530 Roo.form.BasicForm.superclass.constructor.call(this);
24532 Roo.form.BasicForm.popover.apply();
24535 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24537 * @cfg {String} method
24538 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24541 * @cfg {DataReader} reader
24542 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24543 * This is optional as there is built-in support for processing JSON.
24546 * @cfg {DataReader} errorReader
24547 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24548 * This is completely optional as there is built-in support for processing JSON.
24551 * @cfg {String} url
24552 * The URL to use for form actions if one isn't supplied in the action options.
24555 * @cfg {Boolean} fileUpload
24556 * Set to true if this form is a file upload.
24560 * @cfg {Object} baseParams
24561 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24566 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24571 activeAction : null,
24574 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24575 * or setValues() data instead of when the form was first created.
24577 trackResetOnLoad : false,
24581 * childForms - used for multi-tab forms
24584 childForms : false,
24587 * allItems - full list of fields.
24593 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24594 * element by passing it or its id or mask the form itself by passing in true.
24597 waitMsgTarget : false,
24602 disableMask : false,
24605 * @cfg {Boolean} errorMask (true|false) default false
24610 * @cfg {Number} maskOffset Default 100
24615 initEl : function(el){
24616 this.el = Roo.get(el);
24617 this.id = this.el.id || Roo.id();
24618 this.el.on('submit', this.onSubmit, this);
24619 this.el.addClass('x-form');
24623 onSubmit : function(e){
24628 * Returns true if client-side validation on the form is successful.
24631 isValid : function(){
24633 var target = false;
24634 this.items.each(function(f){
24641 if(!target && f.el.isVisible(true)){
24646 if(this.errorMask && !valid){
24647 Roo.form.BasicForm.popover.mask(this, target);
24653 * Returns array of invalid form fields.
24657 invalidFields : function()
24660 this.items.each(function(f){
24673 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24676 isDirty : function(){
24678 this.items.each(function(f){
24688 * Returns true if any fields in this form have changed since their original load. (New version)
24692 hasChanged : function()
24695 this.items.each(function(f){
24696 if(f.hasChanged()){
24705 * Resets all hasChanged to 'false' -
24706 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24707 * So hasChanged storage is only to be used for this purpose
24710 resetHasChanged : function()
24712 this.items.each(function(f){
24713 f.resetHasChanged();
24720 * Performs a predefined action (submit or load) or custom actions you define on this form.
24721 * @param {String} actionName The name of the action type
24722 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24723 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24724 * accept other config options):
24726 Property Type Description
24727 ---------------- --------------- ----------------------------------------------------------------------------------
24728 url String The url for the action (defaults to the form's url)
24729 method String The form method to use (defaults to the form's method, or POST if not defined)
24730 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
24731 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
24732 validate the form on the client (defaults to false)
24734 * @return {BasicForm} this
24736 doAction : function(action, options){
24737 if(typeof action == 'string'){
24738 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24740 if(this.fireEvent('beforeaction', this, action) !== false){
24741 this.beforeAction(action);
24742 action.run.defer(100, action);
24748 * Shortcut to do a submit action.
24749 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24750 * @return {BasicForm} this
24752 submit : function(options){
24753 this.doAction('submit', options);
24758 * Shortcut to do a load action.
24759 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24760 * @return {BasicForm} this
24762 load : function(options){
24763 this.doAction('load', options);
24768 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24769 * @param {Record} record The record to edit
24770 * @return {BasicForm} this
24772 updateRecord : function(record){
24773 record.beginEdit();
24774 var fs = record.fields;
24775 fs.each(function(f){
24776 var field = this.findField(f.name);
24778 record.set(f.name, field.getValue());
24786 * Loads an Roo.data.Record into this form.
24787 * @param {Record} record The record to load
24788 * @return {BasicForm} this
24790 loadRecord : function(record){
24791 this.setValues(record.data);
24796 beforeAction : function(action){
24797 var o = action.options;
24799 if(!this.disableMask) {
24800 if(this.waitMsgTarget === true){
24801 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24802 }else if(this.waitMsgTarget){
24803 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24804 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24806 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24814 afterAction : function(action, success){
24815 this.activeAction = null;
24816 var o = action.options;
24818 if(!this.disableMask) {
24819 if(this.waitMsgTarget === true){
24821 }else if(this.waitMsgTarget){
24822 this.waitMsgTarget.unmask();
24824 Roo.MessageBox.updateProgress(1);
24825 Roo.MessageBox.hide();
24833 Roo.callback(o.success, o.scope, [this, action]);
24834 this.fireEvent('actioncomplete', this, action);
24838 // failure condition..
24839 // we have a scenario where updates need confirming.
24840 // eg. if a locking scenario exists..
24841 // we look for { errors : { needs_confirm : true }} in the response.
24843 (typeof(action.result) != 'undefined') &&
24844 (typeof(action.result.errors) != 'undefined') &&
24845 (typeof(action.result.errors.needs_confirm) != 'undefined')
24848 Roo.MessageBox.confirm(
24849 "Change requires confirmation",
24850 action.result.errorMsg,
24855 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
24865 Roo.callback(o.failure, o.scope, [this, action]);
24866 // show an error message if no failed handler is set..
24867 if (!this.hasListener('actionfailed')) {
24868 Roo.MessageBox.alert("Error",
24869 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24870 action.result.errorMsg :
24871 "Saving Failed, please check your entries or try again"
24875 this.fireEvent('actionfailed', this, action);
24881 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24882 * @param {String} id The value to search for
24885 findField : function(id){
24886 var field = this.items.get(id);
24888 this.items.each(function(f){
24889 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24895 return field || null;
24899 * Add a secondary form to this one,
24900 * Used to provide tabbed forms. One form is primary, with hidden values
24901 * which mirror the elements from the other forms.
24903 * @param {Roo.form.Form} form to add.
24906 addForm : function(form)
24909 if (this.childForms.indexOf(form) > -1) {
24913 this.childForms.push(form);
24915 Roo.each(form.allItems, function (fe) {
24917 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24918 if (this.findField(n)) { // already added..
24921 var add = new Roo.form.Hidden({
24924 add.render(this.el);
24931 * Mark fields in this form invalid in bulk.
24932 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24933 * @return {BasicForm} this
24935 markInvalid : function(errors){
24936 if(errors instanceof Array){
24937 for(var i = 0, len = errors.length; i < len; i++){
24938 var fieldError = errors[i];
24939 var f = this.findField(fieldError.id);
24941 f.markInvalid(fieldError.msg);
24947 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24948 field.markInvalid(errors[id]);
24952 Roo.each(this.childForms || [], function (f) {
24953 f.markInvalid(errors);
24960 * Set values for fields in this form in bulk.
24961 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24962 * @return {BasicForm} this
24964 setValues : function(values){
24965 if(values instanceof Array){ // array of objects
24966 for(var i = 0, len = values.length; i < len; i++){
24968 var f = this.findField(v.id);
24970 f.setValue(v.value);
24971 if(this.trackResetOnLoad){
24972 f.originalValue = f.getValue();
24976 }else{ // object hash
24979 if(typeof values[id] != 'function' && (field = this.findField(id))){
24981 if (field.setFromData &&
24982 field.valueField &&
24983 field.displayField &&
24984 // combos' with local stores can
24985 // be queried via setValue()
24986 // to set their value..
24987 (field.store && !field.store.isLocal)
24991 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24992 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24993 field.setFromData(sd);
24996 field.setValue(values[id]);
25000 if(this.trackResetOnLoad){
25001 field.originalValue = field.getValue();
25006 this.resetHasChanged();
25009 Roo.each(this.childForms || [], function (f) {
25010 f.setValues(values);
25011 f.resetHasChanged();
25018 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25019 * they are returned as an array.
25020 * @param {Boolean} asString
25023 getValues : function(asString){
25024 if (this.childForms) {
25025 // copy values from the child forms
25026 Roo.each(this.childForms, function (f) {
25027 this.setValues(f.getValues());
25032 if (typeof(FormData) != 'undefined' && asString !== true) {
25033 // this relies on a 'recent' version of chrome apparently...
25035 var fd = (new FormData(this.el.dom)).entries();
25037 var ent = fd.next();
25038 while (!ent.done) {
25039 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25050 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25051 if(asString === true){
25054 return Roo.urlDecode(fs);
25058 * Returns the fields in this form as an object with key/value pairs.
25059 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25062 getFieldValues : function(with_hidden)
25064 if (this.childForms) {
25065 // copy values from the child forms
25066 // should this call getFieldValues - probably not as we do not currently copy
25067 // hidden fields when we generate..
25068 Roo.each(this.childForms, function (f) {
25069 this.setValues(f.getValues());
25074 this.items.each(function(f){
25075 if (!f.getName()) {
25078 var v = f.getValue();
25079 if (f.inputType =='radio') {
25080 if (typeof(ret[f.getName()]) == 'undefined') {
25081 ret[f.getName()] = ''; // empty..
25084 if (!f.el.dom.checked) {
25088 v = f.el.dom.value;
25092 // not sure if this supported any more..
25093 if ((typeof(v) == 'object') && f.getRawValue) {
25094 v = f.getRawValue() ; // dates..
25096 // combo boxes where name != hiddenName...
25097 if (f.name != f.getName()) {
25098 ret[f.name] = f.getRawValue();
25100 ret[f.getName()] = v;
25107 * Clears all invalid messages in this form.
25108 * @return {BasicForm} this
25110 clearInvalid : function(){
25111 this.items.each(function(f){
25115 Roo.each(this.childForms || [], function (f) {
25124 * Resets this form.
25125 * @return {BasicForm} this
25127 reset : function(){
25128 this.items.each(function(f){
25132 Roo.each(this.childForms || [], function (f) {
25135 this.resetHasChanged();
25141 * Add Roo.form components to this form.
25142 * @param {Field} field1
25143 * @param {Field} field2 (optional)
25144 * @param {Field} etc (optional)
25145 * @return {BasicForm} this
25148 this.items.addAll(Array.prototype.slice.call(arguments, 0));
25154 * Removes a field from the items collection (does NOT remove its markup).
25155 * @param {Field} field
25156 * @return {BasicForm} this
25158 remove : function(field){
25159 this.items.remove(field);
25164 * Looks at the fields in this form, checks them for an id attribute,
25165 * and calls applyTo on the existing dom element with that id.
25166 * @return {BasicForm} this
25168 render : function(){
25169 this.items.each(function(f){
25170 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25178 * Calls {@link Ext#apply} for all fields in this form with the passed object.
25179 * @param {Object} values
25180 * @return {BasicForm} this
25182 applyToFields : function(o){
25183 this.items.each(function(f){
25190 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25191 * @param {Object} values
25192 * @return {BasicForm} this
25194 applyIfToFields : function(o){
25195 this.items.each(function(f){
25203 Roo.BasicForm = Roo.form.BasicForm;
25205 Roo.apply(Roo.form.BasicForm, {
25219 intervalID : false,
25225 if(this.isApplied){
25230 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25231 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25232 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25233 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25236 this.maskEl.top.enableDisplayMode("block");
25237 this.maskEl.left.enableDisplayMode("block");
25238 this.maskEl.bottom.enableDisplayMode("block");
25239 this.maskEl.right.enableDisplayMode("block");
25241 Roo.get(document.body).on('click', function(){
25245 Roo.get(document.body).on('touchstart', function(){
25249 this.isApplied = true
25252 mask : function(form, target)
25256 this.target = target;
25258 if(!this.form.errorMask || !target.el){
25262 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25264 var ot = this.target.el.calcOffsetsTo(scrollable);
25266 var scrollTo = ot[1] - this.form.maskOffset;
25268 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25270 scrollable.scrollTo('top', scrollTo);
25272 var el = this.target.wrap || this.target.el;
25274 var box = el.getBox();
25276 this.maskEl.top.setStyle('position', 'absolute');
25277 this.maskEl.top.setStyle('z-index', 10000);
25278 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25279 this.maskEl.top.setLeft(0);
25280 this.maskEl.top.setTop(0);
25281 this.maskEl.top.show();
25283 this.maskEl.left.setStyle('position', 'absolute');
25284 this.maskEl.left.setStyle('z-index', 10000);
25285 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25286 this.maskEl.left.setLeft(0);
25287 this.maskEl.left.setTop(box.y - this.padding);
25288 this.maskEl.left.show();
25290 this.maskEl.bottom.setStyle('position', 'absolute');
25291 this.maskEl.bottom.setStyle('z-index', 10000);
25292 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25293 this.maskEl.bottom.setLeft(0);
25294 this.maskEl.bottom.setTop(box.bottom + this.padding);
25295 this.maskEl.bottom.show();
25297 this.maskEl.right.setStyle('position', 'absolute');
25298 this.maskEl.right.setStyle('z-index', 10000);
25299 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25300 this.maskEl.right.setLeft(box.right + this.padding);
25301 this.maskEl.right.setTop(box.y - this.padding);
25302 this.maskEl.right.show();
25304 this.intervalID = window.setInterval(function() {
25305 Roo.form.BasicForm.popover.unmask();
25308 window.onwheel = function(){ return false;};
25310 (function(){ this.isMasked = true; }).defer(500, this);
25314 unmask : function()
25316 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25320 this.maskEl.top.setStyle('position', 'absolute');
25321 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25322 this.maskEl.top.hide();
25324 this.maskEl.left.setStyle('position', 'absolute');
25325 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25326 this.maskEl.left.hide();
25328 this.maskEl.bottom.setStyle('position', 'absolute');
25329 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25330 this.maskEl.bottom.hide();
25332 this.maskEl.right.setStyle('position', 'absolute');
25333 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25334 this.maskEl.right.hide();
25336 window.onwheel = function(){ return true;};
25338 if(this.intervalID){
25339 window.clearInterval(this.intervalID);
25340 this.intervalID = false;
25343 this.isMasked = false;
25351 * Ext JS Library 1.1.1
25352 * Copyright(c) 2006-2007, Ext JS, LLC.
25354 * Originally Released Under LGPL - original licence link has changed is not relivant.
25357 * <script type="text/javascript">
25361 * @class Roo.form.Form
25362 * @extends Roo.form.BasicForm
25363 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25364 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25366 * @param {Object} config Configuration options
25368 Roo.form.Form = function(config){
25370 if (config.items) {
25371 xitems = config.items;
25372 delete config.items;
25376 Roo.form.Form.superclass.constructor.call(this, null, config);
25377 this.url = this.url || this.action;
25379 this.root = new Roo.form.Layout(Roo.applyIf({
25383 this.active = this.root;
25385 * Array of all the buttons that have been added to this form via {@link addButton}
25389 this.allItems = [];
25392 * @event clientvalidation
25393 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25394 * @param {Form} this
25395 * @param {Boolean} valid true if the form has passed client-side validation
25397 clientvalidation: true,
25400 * Fires when the form is rendered
25401 * @param {Roo.form.Form} form
25406 if (this.progressUrl) {
25407 // push a hidden field onto the list of fields..
25411 name : 'UPLOAD_IDENTIFIER'
25416 Roo.each(xitems, this.addxtype, this);
25420 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25422 * @cfg {Roo.Button} buttons[] buttons at bottom of form
25426 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25429 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25432 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25434 buttonAlign:'center',
25437 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25442 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25443 * This property cascades to child containers if not set.
25448 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25449 * fires a looping event with that state. This is required to bind buttons to the valid
25450 * state using the config value formBind:true on the button.
25452 monitorValid : false,
25455 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25460 * @cfg {String} progressUrl - Url to return progress data
25463 progressUrl : false,
25465 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25466 * sending a formdata with extra parameters - eg uploaded elements.
25472 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25473 * fields are added and the column is closed. If no fields are passed the column remains open
25474 * until end() is called.
25475 * @param {Object} config The config to pass to the column
25476 * @param {Field} field1 (optional)
25477 * @param {Field} field2 (optional)
25478 * @param {Field} etc (optional)
25479 * @return Column The column container object
25481 column : function(c){
25482 var col = new Roo.form.Column(c);
25484 if(arguments.length > 1){ // duplicate code required because of Opera
25485 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25492 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25493 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25494 * until end() is called.
25495 * @param {Object} config The config to pass to the fieldset
25496 * @param {Field} field1 (optional)
25497 * @param {Field} field2 (optional)
25498 * @param {Field} etc (optional)
25499 * @return FieldSet The fieldset container object
25501 fieldset : function(c){
25502 var fs = new Roo.form.FieldSet(c);
25504 if(arguments.length > 1){ // duplicate code required because of Opera
25505 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25512 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25513 * fields are added and the container is closed. If no fields are passed the container remains open
25514 * until end() is called.
25515 * @param {Object} config The config to pass to the Layout
25516 * @param {Field} field1 (optional)
25517 * @param {Field} field2 (optional)
25518 * @param {Field} etc (optional)
25519 * @return Layout The container object
25521 container : function(c){
25522 var l = new Roo.form.Layout(c);
25524 if(arguments.length > 1){ // duplicate code required because of Opera
25525 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25532 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25533 * @param {Object} container A Roo.form.Layout or subclass of Layout
25534 * @return {Form} this
25536 start : function(c){
25537 // cascade label info
25538 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25539 this.active.stack.push(c);
25540 c.ownerCt = this.active;
25546 * Closes the current open container
25547 * @return {Form} this
25550 if(this.active == this.root){
25553 this.active = this.active.ownerCt;
25558 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25559 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25560 * as the label of the field.
25561 * @param {Field} field1
25562 * @param {Field} field2 (optional)
25563 * @param {Field} etc. (optional)
25564 * @return {Form} this
25567 this.active.stack.push.apply(this.active.stack, arguments);
25568 this.allItems.push.apply(this.allItems,arguments);
25570 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25571 if(a[i].isFormField){
25576 Roo.form.Form.superclass.add.apply(this, r);
25586 * Find any element that has been added to a form, using it's ID or name
25587 * This can include framesets, columns etc. along with regular fields..
25588 * @param {String} id - id or name to find.
25590 * @return {Element} e - or false if nothing found.
25592 findbyId : function(id)
25598 Roo.each(this.allItems, function(f){
25599 if (f.id == id || f.name == id ){
25610 * Render this form into the passed container. This should only be called once!
25611 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25612 * @return {Form} this
25614 render : function(ct)
25620 var o = this.autoCreate || {
25622 method : this.method || 'POST',
25623 id : this.id || Roo.id()
25625 this.initEl(ct.createChild(o));
25627 this.root.render(this.el);
25631 this.items.each(function(f){
25632 f.render('x-form-el-'+f.id);
25635 if(this.buttons.length > 0){
25636 // tables are required to maintain order and for correct IE layout
25637 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25638 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25639 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25641 var tr = tb.getElementsByTagName('tr')[0];
25642 for(var i = 0, len = this.buttons.length; i < len; i++) {
25643 var b = this.buttons[i];
25644 var td = document.createElement('td');
25645 td.className = 'x-form-btn-td';
25646 b.render(tr.appendChild(td));
25649 if(this.monitorValid){ // initialize after render
25650 this.startMonitoring();
25652 this.fireEvent('rendered', this);
25657 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25658 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25659 * object or a valid Roo.DomHelper element config
25660 * @param {Function} handler The function called when the button is clicked
25661 * @param {Object} scope (optional) The scope of the handler function
25662 * @return {Roo.Button}
25664 addButton : function(config, handler, scope){
25668 minWidth: this.minButtonWidth,
25671 if(typeof config == "string"){
25674 Roo.apply(bc, config);
25676 var btn = new Roo.Button(null, bc);
25677 this.buttons.push(btn);
25682 * Adds a series of form elements (using the xtype property as the factory method.
25683 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25684 * @param {Object} config
25687 addxtype : function()
25689 var ar = Array.prototype.slice.call(arguments, 0);
25691 for(var i = 0; i < ar.length; i++) {
25693 continue; // skip -- if this happends something invalid got sent, we
25694 // should ignore it, as basically that interface element will not show up
25695 // and that should be pretty obvious!!
25698 if (Roo.form[ar[i].xtype]) {
25700 var fe = Roo.factory(ar[i], Roo.form);
25706 fe.store.form = this;
25711 this.allItems.push(fe);
25712 if (fe.items && fe.addxtype) {
25713 fe.addxtype.apply(fe, fe.items);
25723 // console.log('adding ' + ar[i].xtype);
25725 if (ar[i].xtype == 'Button') {
25726 //console.log('adding button');
25727 //console.log(ar[i]);
25728 this.addButton(ar[i]);
25729 this.allItems.push(fe);
25733 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25734 alert('end is not supported on xtype any more, use items');
25736 // //console.log('adding end');
25744 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25745 * option "monitorValid"
25747 startMonitoring : function(){
25750 Roo.TaskMgr.start({
25751 run : this.bindHandler,
25752 interval : this.monitorPoll || 200,
25759 * Stops monitoring of the valid state of this form
25761 stopMonitoring : function(){
25762 this.bound = false;
25766 bindHandler : function(){
25768 return false; // stops binding
25771 this.items.each(function(f){
25772 if(!f.isValid(true)){
25777 for(var i = 0, len = this.buttons.length; i < len; i++){
25778 var btn = this.buttons[i];
25779 if(btn.formBind === true && btn.disabled === valid){
25780 btn.setDisabled(!valid);
25783 this.fireEvent('clientvalidation', this, valid);
25797 Roo.Form = Roo.form.Form;
25800 * Ext JS Library 1.1.1
25801 * Copyright(c) 2006-2007, Ext JS, LLC.
25803 * Originally Released Under LGPL - original licence link has changed is not relivant.
25806 * <script type="text/javascript">
25809 // as we use this in bootstrap.
25810 Roo.namespace('Roo.form');
25812 * @class Roo.form.Action
25813 * Internal Class used to handle form actions
25815 * @param {Roo.form.BasicForm} el The form element or its id
25816 * @param {Object} config Configuration options
25821 // define the action interface
25822 Roo.form.Action = function(form, options){
25824 this.options = options || {};
25827 * Client Validation Failed
25830 Roo.form.Action.CLIENT_INVALID = 'client';
25832 * Server Validation Failed
25835 Roo.form.Action.SERVER_INVALID = 'server';
25837 * Connect to Server Failed
25840 Roo.form.Action.CONNECT_FAILURE = 'connect';
25842 * Reading Data from Server Failed
25845 Roo.form.Action.LOAD_FAILURE = 'load';
25847 Roo.form.Action.prototype = {
25849 failureType : undefined,
25850 response : undefined,
25851 result : undefined,
25853 // interface method
25854 run : function(options){
25858 // interface method
25859 success : function(response){
25863 // interface method
25864 handleResponse : function(response){
25868 // default connection failure
25869 failure : function(response){
25871 this.response = response;
25872 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25873 this.form.afterAction(this, false);
25876 processResponse : function(response){
25877 this.response = response;
25878 if(!response.responseText){
25881 this.result = this.handleResponse(response);
25882 return this.result;
25885 // utility functions used internally
25886 getUrl : function(appendParams){
25887 var url = this.options.url || this.form.url || this.form.el.dom.action;
25889 var p = this.getParams();
25891 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25897 getMethod : function(){
25898 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25901 getParams : function(){
25902 var bp = this.form.baseParams;
25903 var p = this.options.params;
25905 if(typeof p == "object"){
25906 p = Roo.urlEncode(Roo.applyIf(p, bp));
25907 }else if(typeof p == 'string' && bp){
25908 p += '&' + Roo.urlEncode(bp);
25911 p = Roo.urlEncode(bp);
25916 createCallback : function(){
25918 success: this.success,
25919 failure: this.failure,
25921 timeout: (this.form.timeout*1000),
25922 upload: this.form.fileUpload ? this.success : undefined
25927 Roo.form.Action.Submit = function(form, options){
25928 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25931 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25934 haveProgress : false,
25935 uploadComplete : false,
25937 // uploadProgress indicator.
25938 uploadProgress : function()
25940 if (!this.form.progressUrl) {
25944 if (!this.haveProgress) {
25945 Roo.MessageBox.progress("Uploading", "Uploading");
25947 if (this.uploadComplete) {
25948 Roo.MessageBox.hide();
25952 this.haveProgress = true;
25954 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25956 var c = new Roo.data.Connection();
25958 url : this.form.progressUrl,
25963 success : function(req){
25964 //console.log(data);
25968 rdata = Roo.decode(req.responseText)
25970 Roo.log("Invalid data from server..");
25974 if (!rdata || !rdata.success) {
25976 Roo.MessageBox.alert(Roo.encode(rdata));
25979 var data = rdata.data;
25981 if (this.uploadComplete) {
25982 Roo.MessageBox.hide();
25987 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25988 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25991 this.uploadProgress.defer(2000,this);
25994 failure: function(data) {
25995 Roo.log('progress url failed ');
26006 // run get Values on the form, so it syncs any secondary forms.
26007 this.form.getValues();
26009 var o = this.options;
26010 var method = this.getMethod();
26011 var isPost = method == 'POST';
26012 if(o.clientValidation === false || this.form.isValid()){
26014 if (this.form.progressUrl) {
26015 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26016 (new Date() * 1) + '' + Math.random());
26021 Roo.Ajax.request(Roo.apply(this.createCallback(), {
26022 form:this.form.el.dom,
26023 url:this.getUrl(!isPost),
26025 params:isPost ? this.getParams() : null,
26026 isUpload: this.form.fileUpload,
26027 formData : this.form.formData
26030 this.uploadProgress();
26032 }else if (o.clientValidation !== false){ // client validation failed
26033 this.failureType = Roo.form.Action.CLIENT_INVALID;
26034 this.form.afterAction(this, false);
26038 success : function(response)
26040 this.uploadComplete= true;
26041 if (this.haveProgress) {
26042 Roo.MessageBox.hide();
26046 var result = this.processResponse(response);
26047 if(result === true || result.success){
26048 this.form.afterAction(this, true);
26052 this.form.markInvalid(result.errors);
26053 this.failureType = Roo.form.Action.SERVER_INVALID;
26055 this.form.afterAction(this, false);
26057 failure : function(response)
26059 this.uploadComplete= true;
26060 if (this.haveProgress) {
26061 Roo.MessageBox.hide();
26064 this.response = response;
26065 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26066 this.form.afterAction(this, false);
26069 handleResponse : function(response){
26070 if(this.form.errorReader){
26071 var rs = this.form.errorReader.read(response);
26074 for(var i = 0, len = rs.records.length; i < len; i++) {
26075 var r = rs.records[i];
26076 errors[i] = r.data;
26079 if(errors.length < 1){
26083 success : rs.success,
26089 ret = Roo.decode(response.responseText);
26093 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26103 Roo.form.Action.Load = function(form, options){
26104 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26105 this.reader = this.form.reader;
26108 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26113 Roo.Ajax.request(Roo.apply(
26114 this.createCallback(), {
26115 method:this.getMethod(),
26116 url:this.getUrl(false),
26117 params:this.getParams()
26121 success : function(response){
26123 var result = this.processResponse(response);
26124 if(result === true || !result.success || !result.data){
26125 this.failureType = Roo.form.Action.LOAD_FAILURE;
26126 this.form.afterAction(this, false);
26129 this.form.clearInvalid();
26130 this.form.setValues(result.data);
26131 this.form.afterAction(this, true);
26134 handleResponse : function(response){
26135 if(this.form.reader){
26136 var rs = this.form.reader.read(response);
26137 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26139 success : rs.success,
26143 return Roo.decode(response.responseText);
26147 Roo.form.Action.ACTION_TYPES = {
26148 'load' : Roo.form.Action.Load,
26149 'submit' : Roo.form.Action.Submit
26152 * Ext JS Library 1.1.1
26153 * Copyright(c) 2006-2007, Ext JS, LLC.
26155 * Originally Released Under LGPL - original licence link has changed is not relivant.
26158 * <script type="text/javascript">
26162 * @class Roo.form.Layout
26163 * @extends Roo.Component
26164 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26165 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26167 * @param {Object} config Configuration options
26169 Roo.form.Layout = function(config){
26171 if (config.items) {
26172 xitems = config.items;
26173 delete config.items;
26175 Roo.form.Layout.superclass.constructor.call(this, config);
26177 Roo.each(xitems, this.addxtype, this);
26181 Roo.extend(Roo.form.Layout, Roo.Component, {
26183 * @cfg {String/Object} autoCreate
26184 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26187 * @cfg {String/Object/Function} style
26188 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26189 * a function which returns such a specification.
26192 * @cfg {String} labelAlign
26193 * Valid values are "left," "top" and "right" (defaults to "left")
26196 * @cfg {Number} labelWidth
26197 * Fixed width in pixels of all field labels (defaults to undefined)
26200 * @cfg {Boolean} clear
26201 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26205 * @cfg {String} labelSeparator
26206 * The separator to use after field labels (defaults to ':')
26208 labelSeparator : ':',
26210 * @cfg {Boolean} hideLabels
26211 * True to suppress the display of field labels in this layout (defaults to false)
26213 hideLabels : false,
26216 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26221 onRender : function(ct, position){
26222 if(this.el){ // from markup
26223 this.el = Roo.get(this.el);
26224 }else { // generate
26225 var cfg = this.getAutoCreate();
26226 this.el = ct.createChild(cfg, position);
26229 this.el.applyStyles(this.style);
26231 if(this.labelAlign){
26232 this.el.addClass('x-form-label-'+this.labelAlign);
26234 if(this.hideLabels){
26235 this.labelStyle = "display:none";
26236 this.elementStyle = "padding-left:0;";
26238 if(typeof this.labelWidth == 'number'){
26239 this.labelStyle = "width:"+this.labelWidth+"px;";
26240 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26242 if(this.labelAlign == 'top'){
26243 this.labelStyle = "width:auto;";
26244 this.elementStyle = "padding-left:0;";
26247 var stack = this.stack;
26248 var slen = stack.length;
26250 if(!this.fieldTpl){
26251 var t = new Roo.Template(
26252 '<div class="x-form-item {5}">',
26253 '<label for="{0}" style="{2}">{1}{4}</label>',
26254 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26256 '</div><div class="x-form-clear-left"></div>'
26258 t.disableFormats = true;
26260 Roo.form.Layout.prototype.fieldTpl = t;
26262 for(var i = 0; i < slen; i++) {
26263 if(stack[i].isFormField){
26264 this.renderField(stack[i]);
26266 this.renderComponent(stack[i]);
26271 this.el.createChild({cls:'x-form-clear'});
26276 renderField : function(f){
26277 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26280 f.labelStyle||this.labelStyle||'', //2
26281 this.elementStyle||'', //3
26282 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26283 f.itemCls||this.itemCls||'' //5
26284 ], true).getPrevSibling());
26288 renderComponent : function(c){
26289 c.render(c.isLayout ? this.el : this.el.createChild());
26292 * Adds a object form elements (using the xtype property as the factory method.)
26293 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
26294 * @param {Object} config
26296 addxtype : function(o)
26298 // create the lement.
26299 o.form = this.form;
26300 var fe = Roo.factory(o, Roo.form);
26301 this.form.allItems.push(fe);
26302 this.stack.push(fe);
26304 if (fe.isFormField) {
26305 this.form.items.add(fe);
26313 * @class Roo.form.Column
26314 * @extends Roo.form.Layout
26315 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26317 * @param {Object} config Configuration options
26319 Roo.form.Column = function(config){
26320 Roo.form.Column.superclass.constructor.call(this, config);
26323 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26325 * @cfg {Number/String} width
26326 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26329 * @cfg {String/Object} autoCreate
26330 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26334 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26337 onRender : function(ct, position){
26338 Roo.form.Column.superclass.onRender.call(this, ct, position);
26340 this.el.setWidth(this.width);
26347 * @class Roo.form.Row
26348 * @extends Roo.form.Layout
26349 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26350 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26352 * @param {Object} config Configuration options
26356 Roo.form.Row = function(config){
26357 Roo.form.Row.superclass.constructor.call(this, config);
26360 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26362 * @cfg {Number/String} width
26363 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26366 * @cfg {Number/String} height
26367 * The fixed height of the column in pixels or CSS value (defaults to "auto")
26369 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26373 onRender : function(ct, position){
26374 //console.log('row render');
26376 var t = new Roo.Template(
26377 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26378 '<label for="{0}" style="{2}">{1}{4}</label>',
26379 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26383 t.disableFormats = true;
26385 Roo.form.Layout.prototype.rowTpl = t;
26387 this.fieldTpl = this.rowTpl;
26389 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26390 var labelWidth = 100;
26392 if ((this.labelAlign != 'top')) {
26393 if (typeof this.labelWidth == 'number') {
26394 labelWidth = this.labelWidth
26396 this.padWidth = 20 + labelWidth;
26400 Roo.form.Column.superclass.onRender.call(this, ct, position);
26402 this.el.setWidth(this.width);
26405 this.el.setHeight(this.height);
26410 renderField : function(f){
26411 f.fieldEl = this.fieldTpl.append(this.el, [
26412 f.id, f.fieldLabel,
26413 f.labelStyle||this.labelStyle||'',
26414 this.elementStyle||'',
26415 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26416 f.itemCls||this.itemCls||'',
26417 f.width ? f.width + this.padWidth : 160 + this.padWidth
26424 * @class Roo.form.FieldSet
26425 * @extends Roo.form.Layout
26426 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26427 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26429 * @param {Object} config Configuration options
26431 Roo.form.FieldSet = function(config){
26432 Roo.form.FieldSet.superclass.constructor.call(this, config);
26435 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26437 * @cfg {String} legend
26438 * The text to display as the legend for the FieldSet (defaults to '')
26441 * @cfg {String/Object} autoCreate
26442 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26446 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26449 onRender : function(ct, position){
26450 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26452 this.setLegend(this.legend);
26457 setLegend : function(text){
26459 this.el.child('legend').update(text);
26464 * Ext JS Library 1.1.1
26465 * Copyright(c) 2006-2007, Ext JS, LLC.
26467 * Originally Released Under LGPL - original licence link has changed is not relivant.
26470 * <script type="text/javascript">
26473 * @class Roo.form.VTypes
26474 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26477 Roo.form.VTypes = function(){
26478 // closure these in so they are only created once.
26479 var alpha = /^[a-zA-Z_]+$/;
26480 var alphanum = /^[a-zA-Z0-9_]+$/;
26481 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26482 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26484 // All these messages and functions are configurable
26487 * The function used to validate email addresses
26488 * @param {String} value The email address
26490 'email' : function(v){
26491 return email.test(v);
26494 * The error text to display when the email validation function returns false
26497 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26499 * The keystroke filter mask to be applied on email input
26502 'emailMask' : /[a-z0-9_\.\-@]/i,
26505 * The function used to validate URLs
26506 * @param {String} value The URL
26508 'url' : function(v){
26509 return url.test(v);
26512 * The error text to display when the url validation function returns false
26515 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26518 * The function used to validate alpha values
26519 * @param {String} value The value
26521 'alpha' : function(v){
26522 return alpha.test(v);
26525 * The error text to display when the alpha validation function returns false
26528 'alphaText' : 'This field should only contain letters and _',
26530 * The keystroke filter mask to be applied on alpha input
26533 'alphaMask' : /[a-z_]/i,
26536 * The function used to validate alphanumeric values
26537 * @param {String} value The value
26539 'alphanum' : function(v){
26540 return alphanum.test(v);
26543 * The error text to display when the alphanumeric validation function returns false
26546 'alphanumText' : 'This field should only contain letters, numbers and _',
26548 * The keystroke filter mask to be applied on alphanumeric input
26551 'alphanumMask' : /[a-z0-9_]/i
26553 }();//<script type="text/javascript">
26556 * @class Roo.form.FCKeditor
26557 * @extends Roo.form.TextArea
26558 * Wrapper around the FCKEditor http://www.fckeditor.net
26560 * Creates a new FCKeditor
26561 * @param {Object} config Configuration options
26563 Roo.form.FCKeditor = function(config){
26564 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26567 * @event editorinit
26568 * Fired when the editor is initialized - you can add extra handlers here..
26569 * @param {FCKeditor} this
26570 * @param {Object} the FCK object.
26577 Roo.form.FCKeditor.editors = { };
26578 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26580 //defaultAutoCreate : {
26581 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26585 * @cfg {Object} fck options - see fck manual for details.
26590 * @cfg {Object} fck toolbar set (Basic or Default)
26592 toolbarSet : 'Basic',
26594 * @cfg {Object} fck BasePath
26596 basePath : '/fckeditor/',
26604 onRender : function(ct, position)
26607 this.defaultAutoCreate = {
26609 style:"width:300px;height:60px;",
26610 autocomplete: "new-password"
26613 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26616 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26617 if(this.preventScrollbars){
26618 this.el.setStyle("overflow", "hidden");
26620 this.el.setHeight(this.growMin);
26623 //console.log('onrender' + this.getId() );
26624 Roo.form.FCKeditor.editors[this.getId()] = this;
26627 this.replaceTextarea() ;
26631 getEditor : function() {
26632 return this.fckEditor;
26635 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26636 * @param {Mixed} value The value to set
26640 setValue : function(value)
26642 //console.log('setValue: ' + value);
26644 if(typeof(value) == 'undefined') { // not sure why this is happending...
26647 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26649 //if(!this.el || !this.getEditor()) {
26650 // this.value = value;
26651 //this.setValue.defer(100,this,[value]);
26655 if(!this.getEditor()) {
26659 this.getEditor().SetData(value);
26666 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26667 * @return {Mixed} value The field value
26669 getValue : function()
26672 if (this.frame && this.frame.dom.style.display == 'none') {
26673 return Roo.form.FCKeditor.superclass.getValue.call(this);
26676 if(!this.el || !this.getEditor()) {
26678 // this.getValue.defer(100,this);
26683 var value=this.getEditor().GetData();
26684 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26685 return Roo.form.FCKeditor.superclass.getValue.call(this);
26691 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26692 * @return {Mixed} value The field value
26694 getRawValue : function()
26696 if (this.frame && this.frame.dom.style.display == 'none') {
26697 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26700 if(!this.el || !this.getEditor()) {
26701 //this.getRawValue.defer(100,this);
26708 var value=this.getEditor().GetData();
26709 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26710 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26714 setSize : function(w,h) {
26718 //if (this.frame && this.frame.dom.style.display == 'none') {
26719 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26722 //if(!this.el || !this.getEditor()) {
26723 // this.setSize.defer(100,this, [w,h]);
26729 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26731 this.frame.dom.setAttribute('width', w);
26732 this.frame.dom.setAttribute('height', h);
26733 this.frame.setSize(w,h);
26737 toggleSourceEdit : function(value) {
26741 this.el.dom.style.display = value ? '' : 'none';
26742 this.frame.dom.style.display = value ? 'none' : '';
26747 focus: function(tag)
26749 if (this.frame.dom.style.display == 'none') {
26750 return Roo.form.FCKeditor.superclass.focus.call(this);
26752 if(!this.el || !this.getEditor()) {
26753 this.focus.defer(100,this, [tag]);
26760 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26761 this.getEditor().Focus();
26763 if (!this.getEditor().Selection.GetSelection()) {
26764 this.focus.defer(100,this, [tag]);
26769 var r = this.getEditor().EditorDocument.createRange();
26770 r.setStart(tgs[0],0);
26771 r.setEnd(tgs[0],0);
26772 this.getEditor().Selection.GetSelection().removeAllRanges();
26773 this.getEditor().Selection.GetSelection().addRange(r);
26774 this.getEditor().Focus();
26781 replaceTextarea : function()
26783 if ( document.getElementById( this.getId() + '___Frame' ) ) {
26786 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26788 // We must check the elements firstly using the Id and then the name.
26789 var oTextarea = document.getElementById( this.getId() );
26791 var colElementsByName = document.getElementsByName( this.getId() ) ;
26793 oTextarea.style.display = 'none' ;
26795 if ( oTextarea.tabIndex ) {
26796 this.TabIndex = oTextarea.tabIndex ;
26799 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26800 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26801 this.frame = Roo.get(this.getId() + '___Frame')
26804 _getConfigHtml : function()
26808 for ( var o in this.fckconfig ) {
26809 sConfig += sConfig.length > 0 ? '&' : '';
26810 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26813 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26817 _getIFrameHtml : function()
26819 var sFile = 'fckeditor.html' ;
26820 /* no idea what this is about..
26823 if ( (/fcksource=true/i).test( window.top.location.search ) )
26824 sFile = 'fckeditor.original.html' ;
26829 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26830 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
26833 var html = '<iframe id="' + this.getId() +
26834 '___Frame" src="' + sLink +
26835 '" width="' + this.width +
26836 '" height="' + this.height + '"' +
26837 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
26838 ' frameborder="0" scrolling="no"></iframe>' ;
26843 _insertHtmlBefore : function( html, element )
26845 if ( element.insertAdjacentHTML ) {
26847 element.insertAdjacentHTML( 'beforeBegin', html ) ;
26849 var oRange = document.createRange() ;
26850 oRange.setStartBefore( element ) ;
26851 var oFragment = oRange.createContextualFragment( html );
26852 element.parentNode.insertBefore( oFragment, element ) ;
26865 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26867 function FCKeditor_OnComplete(editorInstance){
26868 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26869 f.fckEditor = editorInstance;
26870 //console.log("loaded");
26871 f.fireEvent('editorinit', f, editorInstance);
26891 //<script type="text/javascript">
26893 * @class Roo.form.GridField
26894 * @extends Roo.form.Field
26895 * Embed a grid (or editable grid into a form)
26898 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26900 * xgrid.store = Roo.data.Store
26901 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26902 * xgrid.store.reader = Roo.data.JsonReader
26906 * Creates a new GridField
26907 * @param {Object} config Configuration options
26909 Roo.form.GridField = function(config){
26910 Roo.form.GridField.superclass.constructor.call(this, config);
26914 Roo.extend(Roo.form.GridField, Roo.form.Field, {
26916 * @cfg {Number} width - used to restrict width of grid..
26920 * @cfg {Number} height - used to restrict height of grid..
26924 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26930 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26931 * {tag: "input", type: "checkbox", autocomplete: "off"})
26933 // defaultAutoCreate : { tag: 'div' },
26934 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26936 * @cfg {String} addTitle Text to include for adding a title.
26940 onResize : function(){
26941 Roo.form.Field.superclass.onResize.apply(this, arguments);
26944 initEvents : function(){
26945 // Roo.form.Checkbox.superclass.initEvents.call(this);
26946 // has no events...
26951 getResizeEl : function(){
26955 getPositionEl : function(){
26960 onRender : function(ct, position){
26962 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26963 var style = this.style;
26966 Roo.form.GridField.superclass.onRender.call(this, ct, position);
26967 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26968 this.viewEl = this.wrap.createChild({ tag: 'div' });
26970 this.viewEl.applyStyles(style);
26973 this.viewEl.setWidth(this.width);
26976 this.viewEl.setHeight(this.height);
26978 //if(this.inputValue !== undefined){
26979 //this.setValue(this.value);
26982 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26985 this.grid.render();
26986 this.grid.getDataSource().on('remove', this.refreshValue, this);
26987 this.grid.getDataSource().on('update', this.refreshValue, this);
26988 this.grid.on('afteredit', this.refreshValue, this);
26994 * Sets the value of the item.
26995 * @param {String} either an object or a string..
26997 setValue : function(v){
26999 v = v || []; // empty set..
27000 // this does not seem smart - it really only affects memoryproxy grids..
27001 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27002 var ds = this.grid.getDataSource();
27003 // assumes a json reader..
27005 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
27006 ds.loadData( data);
27008 // clear selection so it does not get stale.
27009 if (this.grid.sm) {
27010 this.grid.sm.clearSelections();
27013 Roo.form.GridField.superclass.setValue.call(this, v);
27014 this.refreshValue();
27015 // should load data in the grid really....
27019 refreshValue: function() {
27021 this.grid.getDataSource().each(function(r) {
27024 this.el.dom.value = Roo.encode(val);
27032 * Ext JS Library 1.1.1
27033 * Copyright(c) 2006-2007, Ext JS, LLC.
27035 * Originally Released Under LGPL - original licence link has changed is not relivant.
27038 * <script type="text/javascript">
27041 * @class Roo.form.DisplayField
27042 * @extends Roo.form.Field
27043 * A generic Field to display non-editable data.
27044 * @cfg {Boolean} closable (true|false) default false
27046 * Creates a new Display Field item.
27047 * @param {Object} config Configuration options
27049 Roo.form.DisplayField = function(config){
27050 Roo.form.DisplayField.superclass.constructor.call(this, config);
27055 * Fires after the click the close btn
27056 * @param {Roo.form.DisplayField} this
27062 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
27063 inputType: 'hidden',
27069 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27071 focusClass : undefined,
27073 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27075 fieldClass: 'x-form-field',
27078 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27080 valueRenderer: undefined,
27084 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27085 * {tag: "input", type: "checkbox", autocomplete: "off"})
27088 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27092 onResize : function(){
27093 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27097 initEvents : function(){
27098 // Roo.form.Checkbox.superclass.initEvents.call(this);
27099 // has no events...
27102 this.closeEl.on('click', this.onClose, this);
27108 getResizeEl : function(){
27112 getPositionEl : function(){
27117 onRender : function(ct, position){
27119 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27120 //if(this.inputValue !== undefined){
27121 this.wrap = this.el.wrap();
27123 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27126 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27129 if (this.bodyStyle) {
27130 this.viewEl.applyStyles(this.bodyStyle);
27132 //this.viewEl.setStyle('padding', '2px');
27134 this.setValue(this.value);
27139 initValue : Roo.emptyFn,
27144 onClick : function(){
27149 * Sets the checked state of the checkbox.
27150 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27152 setValue : function(v){
27154 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
27155 // this might be called before we have a dom element..
27156 if (!this.viewEl) {
27159 this.viewEl.dom.innerHTML = html;
27160 Roo.form.DisplayField.superclass.setValue.call(this, v);
27164 onClose : function(e)
27166 e.preventDefault();
27168 this.fireEvent('close', this);
27177 * @class Roo.form.DayPicker
27178 * @extends Roo.form.Field
27179 * A Day picker show [M] [T] [W] ....
27181 * Creates a new Day Picker
27182 * @param {Object} config Configuration options
27184 Roo.form.DayPicker= function(config){
27185 Roo.form.DayPicker.superclass.constructor.call(this, config);
27189 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
27191 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27193 focusClass : undefined,
27195 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27197 fieldClass: "x-form-field",
27200 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27201 * {tag: "input", type: "checkbox", autocomplete: "off"})
27203 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27206 actionMode : 'viewEl',
27210 inputType : 'hidden',
27213 inputElement: false, // real input element?
27214 basedOn: false, // ????
27216 isFormField: true, // not sure where this is needed!!!!
27218 onResize : function(){
27219 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27220 if(!this.boxLabel){
27221 this.el.alignTo(this.wrap, 'c-c');
27225 initEvents : function(){
27226 Roo.form.Checkbox.superclass.initEvents.call(this);
27227 this.el.on("click", this.onClick, this);
27228 this.el.on("change", this.onClick, this);
27232 getResizeEl : function(){
27236 getPositionEl : function(){
27242 onRender : function(ct, position){
27243 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27245 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27247 var r1 = '<table><tr>';
27248 var r2 = '<tr class="x-form-daypick-icons">';
27249 for (var i=0; i < 7; i++) {
27250 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27251 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
27254 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27255 viewEl.select('img').on('click', this.onClick, this);
27256 this.viewEl = viewEl;
27259 // this will not work on Chrome!!!
27260 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
27261 this.el.on('propertychange', this.setFromHidden, this); //ie
27269 initValue : Roo.emptyFn,
27272 * Returns the checked state of the checkbox.
27273 * @return {Boolean} True if checked, else false
27275 getValue : function(){
27276 return this.el.dom.value;
27281 onClick : function(e){
27282 //this.setChecked(!this.checked);
27283 Roo.get(e.target).toggleClass('x-menu-item-checked');
27284 this.refreshValue();
27285 //if(this.el.dom.checked != this.checked){
27286 // this.setValue(this.el.dom.checked);
27291 refreshValue : function()
27294 this.viewEl.select('img',true).each(function(e,i,n) {
27295 val += e.is(".x-menu-item-checked") ? String(n) : '';
27297 this.setValue(val, true);
27301 * Sets the checked state of the checkbox.
27302 * On is always based on a string comparison between inputValue and the param.
27303 * @param {Boolean/String} value - the value to set
27304 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27306 setValue : function(v,suppressEvent){
27307 if (!this.el.dom) {
27310 var old = this.el.dom.value ;
27311 this.el.dom.value = v;
27312 if (suppressEvent) {
27316 // update display..
27317 this.viewEl.select('img',true).each(function(e,i,n) {
27319 var on = e.is(".x-menu-item-checked");
27320 var newv = v.indexOf(String(n)) > -1;
27322 e.toggleClass('x-menu-item-checked');
27328 this.fireEvent('change', this, v, old);
27333 // handle setting of hidden value by some other method!!?!?
27334 setFromHidden: function()
27339 //console.log("SET FROM HIDDEN");
27340 //alert('setFrom hidden');
27341 this.setValue(this.el.dom.value);
27344 onDestroy : function()
27347 Roo.get(this.viewEl).remove();
27350 Roo.form.DayPicker.superclass.onDestroy.call(this);
27354 * RooJS Library 1.1.1
27355 * Copyright(c) 2008-2011 Alan Knowles
27362 * @class Roo.form.ComboCheck
27363 * @extends Roo.form.ComboBox
27364 * A combobox for multiple select items.
27366 * FIXME - could do with a reset button..
27369 * Create a new ComboCheck
27370 * @param {Object} config Configuration options
27372 Roo.form.ComboCheck = function(config){
27373 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27374 // should verify some data...
27376 // hiddenName = required..
27377 // displayField = required
27378 // valudField == required
27379 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27381 Roo.each(req, function(e) {
27382 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27383 throw "Roo.form.ComboCheck : missing value for: " + e;
27390 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27395 selectedClass: 'x-menu-item-checked',
27398 onRender : function(ct, position){
27404 var cls = 'x-combo-list';
27407 this.tpl = new Roo.Template({
27408 html : '<div class="'+cls+'-item x-menu-check-item">' +
27409 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27410 '<span>{' + this.displayField + '}</span>' +
27417 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27418 this.view.singleSelect = false;
27419 this.view.multiSelect = true;
27420 this.view.toggleSelect = true;
27421 this.pageTb.add(new Roo.Toolbar.Fill(), {
27424 handler: function()
27431 onViewOver : function(e, t){
27437 onViewClick : function(doFocus,index){
27441 select: function () {
27442 //Roo.log("SELECT CALLED");
27445 selectByValue : function(xv, scrollIntoView){
27446 var ar = this.getValueArray();
27449 Roo.each(ar, function(v) {
27450 if(v === undefined || v === null){
27453 var r = this.findRecord(this.valueField, v);
27455 sels.push(this.store.indexOf(r))
27459 this.view.select(sels);
27465 onSelect : function(record, index){
27466 // Roo.log("onselect Called");
27467 // this is only called by the clear button now..
27468 this.view.clearSelections();
27469 this.setValue('[]');
27470 if (this.value != this.valueBefore) {
27471 this.fireEvent('change', this, this.value, this.valueBefore);
27472 this.valueBefore = this.value;
27475 getValueArray : function()
27480 //Roo.log(this.value);
27481 if (typeof(this.value) == 'undefined') {
27484 var ar = Roo.decode(this.value);
27485 return ar instanceof Array ? ar : []; //?? valid?
27488 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27493 expand : function ()
27496 Roo.form.ComboCheck.superclass.expand.call(this);
27497 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27498 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27503 collapse : function(){
27504 Roo.form.ComboCheck.superclass.collapse.call(this);
27505 var sl = this.view.getSelectedIndexes();
27506 var st = this.store;
27510 Roo.each(sl, function(i) {
27512 nv.push(r.get(this.valueField));
27514 this.setValue(Roo.encode(nv));
27515 if (this.value != this.valueBefore) {
27517 this.fireEvent('change', this, this.value, this.valueBefore);
27518 this.valueBefore = this.value;
27523 setValue : function(v){
27527 var vals = this.getValueArray();
27529 Roo.each(vals, function(k) {
27530 var r = this.findRecord(this.valueField, k);
27532 tv.push(r.data[this.displayField]);
27533 }else if(this.valueNotFoundText !== undefined){
27534 tv.push( this.valueNotFoundText );
27539 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27540 this.hiddenField.value = v;
27546 * Ext JS Library 1.1.1
27547 * Copyright(c) 2006-2007, Ext JS, LLC.
27549 * Originally Released Under LGPL - original licence link has changed is not relivant.
27552 * <script type="text/javascript">
27556 * @class Roo.form.Signature
27557 * @extends Roo.form.Field
27561 * @param {Object} config Configuration options
27564 Roo.form.Signature = function(config){
27565 Roo.form.Signature.superclass.constructor.call(this, config);
27567 this.addEvents({// not in used??
27570 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27571 * @param {Roo.form.Signature} combo This combo box
27576 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27577 * @param {Roo.form.ComboBox} combo This combo box
27578 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27584 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27586 * @cfg {Object} labels Label to use when rendering a form.
27590 * confirm : "Confirm"
27595 confirm : "Confirm"
27598 * @cfg {Number} width The signature panel width (defaults to 300)
27602 * @cfg {Number} height The signature panel height (defaults to 100)
27606 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27608 allowBlank : false,
27611 // {Object} signPanel The signature SVG panel element (defaults to {})
27613 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27614 isMouseDown : false,
27615 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27616 isConfirmed : false,
27617 // {String} signatureTmp SVG mapping string (defaults to empty string)
27621 defaultAutoCreate : { // modified by initCompnoent..
27627 onRender : function(ct, position){
27629 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27631 this.wrap = this.el.wrap({
27632 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27635 this.createToolbar(this);
27636 this.signPanel = this.wrap.createChild({
27638 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27642 this.svgID = Roo.id();
27643 this.svgEl = this.signPanel.createChild({
27644 xmlns : 'http://www.w3.org/2000/svg',
27646 id : this.svgID + "-svg",
27648 height: this.height,
27649 viewBox: '0 0 '+this.width+' '+this.height,
27653 id: this.svgID + "-svg-r",
27655 height: this.height,
27660 id: this.svgID + "-svg-l",
27662 y1: (this.height*0.8), // start set the line in 80% of height
27663 x2: this.width, // end
27664 y2: (this.height*0.8), // end set the line in 80% of height
27666 'stroke-width': "1",
27667 'stroke-dasharray': "3",
27668 'shape-rendering': "crispEdges",
27669 'pointer-events': "none"
27673 id: this.svgID + "-svg-p",
27675 'stroke-width': "3",
27677 'pointer-events': 'none'
27682 this.svgBox = this.svgEl.dom.getScreenCTM();
27684 createSVG : function(){
27685 var svg = this.signPanel;
27686 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27689 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27690 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27691 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27692 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27693 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27694 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27695 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27698 isTouchEvent : function(e){
27699 return e.type.match(/^touch/);
27701 getCoords : function (e) {
27702 var pt = this.svgEl.dom.createSVGPoint();
27705 if (this.isTouchEvent(e)) {
27706 pt.x = e.targetTouches[0].clientX;
27707 pt.y = e.targetTouches[0].clientY;
27709 var a = this.svgEl.dom.getScreenCTM();
27710 var b = a.inverse();
27711 var mx = pt.matrixTransform(b);
27712 return mx.x + ',' + mx.y;
27714 //mouse event headler
27715 down : function (e) {
27716 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27717 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27719 this.isMouseDown = true;
27721 e.preventDefault();
27723 move : function (e) {
27724 if (this.isMouseDown) {
27725 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27726 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27729 e.preventDefault();
27731 up : function (e) {
27732 this.isMouseDown = false;
27733 var sp = this.signatureTmp.split(' ');
27736 if(!sp[sp.length-2].match(/^L/)){
27740 this.signatureTmp = sp.join(" ");
27743 if(this.getValue() != this.signatureTmp){
27744 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27745 this.isConfirmed = false;
27747 e.preventDefault();
27751 * Protected method that will not generally be called directly. It
27752 * is called when the editor creates its toolbar. Override this method if you need to
27753 * add custom toolbar buttons.
27754 * @param {HtmlEditor} editor
27756 createToolbar : function(editor){
27757 function btn(id, toggle, handler){
27758 var xid = fid + '-'+ id ;
27762 cls : 'x-btn-icon x-edit-'+id,
27763 enableToggle:toggle !== false,
27764 scope: editor, // was editor...
27765 handler:handler||editor.relayBtnCmd,
27766 clickEvent:'mousedown',
27767 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27773 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27777 cls : ' x-signature-btn x-signature-'+id,
27778 scope: editor, // was editor...
27779 handler: this.reset,
27780 clickEvent:'mousedown',
27781 text: this.labels.clear
27788 cls : ' x-signature-btn x-signature-'+id,
27789 scope: editor, // was editor...
27790 handler: this.confirmHandler,
27791 clickEvent:'mousedown',
27792 text: this.labels.confirm
27799 * when user is clicked confirm then show this image.....
27801 * @return {String} Image Data URI
27803 getImageDataURI : function(){
27804 var svg = this.svgEl.dom.parentNode.innerHTML;
27805 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27810 * @return {Boolean} this.isConfirmed
27812 getConfirmed : function(){
27813 return this.isConfirmed;
27817 * @return {Number} this.width
27819 getWidth : function(){
27824 * @return {Number} this.height
27826 getHeight : function(){
27827 return this.height;
27830 getSignature : function(){
27831 return this.signatureTmp;
27834 reset : function(){
27835 this.signatureTmp = '';
27836 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27837 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27838 this.isConfirmed = false;
27839 Roo.form.Signature.superclass.reset.call(this);
27841 setSignature : function(s){
27842 this.signatureTmp = s;
27843 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27844 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27846 this.isConfirmed = false;
27847 Roo.form.Signature.superclass.reset.call(this);
27850 // Roo.log(this.signPanel.dom.contentWindow.up())
27853 setConfirmed : function(){
27857 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27860 confirmHandler : function(){
27861 if(!this.getSignature()){
27865 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27866 this.setValue(this.getSignature());
27867 this.isConfirmed = true;
27869 this.fireEvent('confirm', this);
27872 // Subclasses should provide the validation implementation by overriding this
27873 validateValue : function(value){
27874 if(this.allowBlank){
27878 if(this.isConfirmed){
27885 * Ext JS Library 1.1.1
27886 * Copyright(c) 2006-2007, Ext JS, LLC.
27888 * Originally Released Under LGPL - original licence link has changed is not relivant.
27891 * <script type="text/javascript">
27896 * @class Roo.form.ComboBox
27897 * @extends Roo.form.TriggerField
27898 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27900 * Create a new ComboBox.
27901 * @param {Object} config Configuration options
27903 Roo.form.Select = function(config){
27904 Roo.form.Select.superclass.constructor.call(this, config);
27908 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27910 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27913 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27914 * rendering into an Roo.Editor, defaults to false)
27917 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27918 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27921 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27924 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27925 * the dropdown list (defaults to undefined, with no header element)
27929 * @cfg {String/Roo.Template} tpl The template to use to render the output
27933 defaultAutoCreate : {tag: "select" },
27935 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27937 listWidth: undefined,
27939 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27940 * mode = 'remote' or 'text' if mode = 'local')
27942 displayField: undefined,
27944 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27945 * mode = 'remote' or 'value' if mode = 'local').
27946 * Note: use of a valueField requires the user make a selection
27947 * in order for a value to be mapped.
27949 valueField: undefined,
27953 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27954 * field's data value (defaults to the underlying DOM element's name)
27956 hiddenName: undefined,
27958 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27962 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27964 selectedClass: 'x-combo-selected',
27966 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
27967 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27968 * which displays a downward arrow icon).
27970 triggerClass : 'x-form-arrow-trigger',
27972 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27976 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27977 * anchor positions (defaults to 'tl-bl')
27979 listAlign: 'tl-bl?',
27981 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27985 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
27986 * query specified by the allQuery config option (defaults to 'query')
27988 triggerAction: 'query',
27990 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27991 * (defaults to 4, does not apply if editable = false)
27995 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27996 * delay (typeAheadDelay) if it matches a known value (defaults to false)
28000 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28001 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28005 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28006 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
28010 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
28011 * when editable = true (defaults to false)
28013 selectOnFocus:false,
28015 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28017 queryParam: 'query',
28019 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
28020 * when mode = 'remote' (defaults to 'Loading...')
28022 loadingText: 'Loading...',
28024 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28028 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28032 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28033 * traditional select (defaults to true)
28037 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28041 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28045 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28046 * listWidth has a higher value)
28050 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28051 * allow the user to set arbitrary text into the field (defaults to false)
28053 forceSelection:false,
28055 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28056 * if typeAhead = true (defaults to 250)
28058 typeAheadDelay : 250,
28060 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28061 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28063 valueNotFoundText : undefined,
28066 * @cfg {String} defaultValue The value displayed after loading the store.
28071 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28073 blockFocus : false,
28076 * @cfg {Boolean} disableClear Disable showing of clear button.
28078 disableClear : false,
28080 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
28082 alwaysQuery : false,
28088 // element that contains real text value.. (when hidden is used..)
28091 onRender : function(ct, position){
28092 Roo.form.Field.prototype.onRender.call(this, ct, position);
28095 this.store.on('beforeload', this.onBeforeLoad, this);
28096 this.store.on('load', this.onLoad, this);
28097 this.store.on('loadexception', this.onLoadException, this);
28098 this.store.load({});
28106 initEvents : function(){
28107 //Roo.form.ComboBox.superclass.initEvents.call(this);
28111 onDestroy : function(){
28114 this.store.un('beforeload', this.onBeforeLoad, this);
28115 this.store.un('load', this.onLoad, this);
28116 this.store.un('loadexception', this.onLoadException, this);
28118 //Roo.form.ComboBox.superclass.onDestroy.call(this);
28122 fireKey : function(e){
28123 if(e.isNavKeyPress() && !this.list.isVisible()){
28124 this.fireEvent("specialkey", this, e);
28129 onResize: function(w, h){
28137 * Allow or prevent the user from directly editing the field text. If false is passed,
28138 * the user will only be able to select from the items defined in the dropdown list. This method
28139 * is the runtime equivalent of setting the 'editable' config option at config time.
28140 * @param {Boolean} value True to allow the user to directly edit the field text
28142 setEditable : function(value){
28147 onBeforeLoad : function(){
28149 Roo.log("Select before load");
28152 this.innerList.update(this.loadingText ?
28153 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28154 //this.restrictHeight();
28155 this.selectedIndex = -1;
28159 onLoad : function(){
28162 var dom = this.el.dom;
28163 dom.innerHTML = '';
28164 var od = dom.ownerDocument;
28166 if (this.emptyText) {
28167 var op = od.createElement('option');
28168 op.setAttribute('value', '');
28169 op.innerHTML = String.format('{0}', this.emptyText);
28170 dom.appendChild(op);
28172 if(this.store.getCount() > 0){
28174 var vf = this.valueField;
28175 var df = this.displayField;
28176 this.store.data.each(function(r) {
28177 // which colmsn to use... testing - cdoe / title..
28178 var op = od.createElement('option');
28179 op.setAttribute('value', r.data[vf]);
28180 op.innerHTML = String.format('{0}', r.data[df]);
28181 dom.appendChild(op);
28183 if (typeof(this.defaultValue != 'undefined')) {
28184 this.setValue(this.defaultValue);
28189 //this.onEmptyResults();
28194 onLoadException : function()
28196 dom.innerHTML = '';
28198 Roo.log("Select on load exception");
28202 Roo.log(this.store.reader.jsonData);
28203 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28204 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28210 onTypeAhead : function(){
28215 onSelect : function(record, index){
28216 Roo.log('on select?');
28218 if(this.fireEvent('beforeselect', this, record, index) !== false){
28219 this.setFromData(index > -1 ? record.data : false);
28221 this.fireEvent('select', this, record, index);
28226 * Returns the currently selected field value or empty string if no value is set.
28227 * @return {String} value The selected value
28229 getValue : function(){
28230 var dom = this.el.dom;
28231 this.value = dom.options[dom.selectedIndex].value;
28237 * Clears any text/value currently set in the field
28239 clearValue : function(){
28241 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28246 * Sets the specified value into the field. If the value finds a match, the corresponding record text
28247 * will be displayed in the field. If the value does not match the data value of an existing item,
28248 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28249 * Otherwise the field will be blank (although the value will still be set).
28250 * @param {String} value The value to match
28252 setValue : function(v){
28253 var d = this.el.dom;
28254 for (var i =0; i < d.options.length;i++) {
28255 if (v == d.options[i].value) {
28256 d.selectedIndex = i;
28264 * @property {Object} the last set data for the element
28269 * Sets the value of the field based on a object which is related to the record format for the store.
28270 * @param {Object} value the value to set as. or false on reset?
28272 setFromData : function(o){
28273 Roo.log('setfrom data?');
28279 reset : function(){
28283 findRecord : function(prop, value){
28288 if(this.store.getCount() > 0){
28289 this.store.each(function(r){
28290 if(r.data[prop] == value){
28300 getName: function()
28302 // returns hidden if it's set..
28303 if (!this.rendered) {return ''};
28304 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
28312 onEmptyResults : function(){
28313 Roo.log('empty results');
28318 * Returns true if the dropdown list is expanded, else false.
28320 isExpanded : function(){
28325 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28326 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28327 * @param {String} value The data value of the item to select
28328 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28329 * selected item if it is not currently in view (defaults to true)
28330 * @return {Boolean} True if the value matched an item in the list, else false
28332 selectByValue : function(v, scrollIntoView){
28333 Roo.log('select By Value');
28336 if(v !== undefined && v !== null){
28337 var r = this.findRecord(this.valueField || this.displayField, v);
28339 this.select(this.store.indexOf(r), scrollIntoView);
28347 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28348 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28349 * @param {Number} index The zero-based index of the list item to select
28350 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28351 * selected item if it is not currently in view (defaults to true)
28353 select : function(index, scrollIntoView){
28354 Roo.log('select ');
28357 this.selectedIndex = index;
28358 this.view.select(index);
28359 if(scrollIntoView !== false){
28360 var el = this.view.getNode(index);
28362 this.innerList.scrollChildIntoView(el, false);
28370 validateBlur : function(){
28377 initQuery : function(){
28378 this.doQuery(this.getRawValue());
28382 doForce : function(){
28383 if(this.el.dom.value.length > 0){
28384 this.el.dom.value =
28385 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28391 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28392 * query allowing the query action to be canceled if needed.
28393 * @param {String} query The SQL query to execute
28394 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28395 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28396 * saved in the current store (defaults to false)
28398 doQuery : function(q, forceAll){
28400 Roo.log('doQuery?');
28401 if(q === undefined || q === null){
28406 forceAll: forceAll,
28410 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28414 forceAll = qe.forceAll;
28415 if(forceAll === true || (q.length >= this.minChars)){
28416 if(this.lastQuery != q || this.alwaysQuery){
28417 this.lastQuery = q;
28418 if(this.mode == 'local'){
28419 this.selectedIndex = -1;
28421 this.store.clearFilter();
28423 this.store.filter(this.displayField, q);
28427 this.store.baseParams[this.queryParam] = q;
28429 params: this.getParams(q)
28434 this.selectedIndex = -1;
28441 getParams : function(q){
28443 //p[this.queryParam] = q;
28446 p.limit = this.pageSize;
28452 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28454 collapse : function(){
28459 collapseIf : function(e){
28464 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28466 expand : function(){
28474 * @cfg {Boolean} grow
28478 * @cfg {Number} growMin
28482 * @cfg {Number} growMax
28490 setWidth : function()
28494 getResizeEl : function(){
28497 });//<script type="text/javasscript">
28501 * @class Roo.DDView
28502 * A DnD enabled version of Roo.View.
28503 * @param {Element/String} container The Element in which to create the View.
28504 * @param {String} tpl The template string used to create the markup for each element of the View
28505 * @param {Object} config The configuration properties. These include all the config options of
28506 * {@link Roo.View} plus some specific to this class.<br>
28508 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28509 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28511 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28512 .x-view-drag-insert-above {
28513 border-top:1px dotted #3366cc;
28515 .x-view-drag-insert-below {
28516 border-bottom:1px dotted #3366cc;
28522 Roo.DDView = function(container, tpl, config) {
28523 Roo.DDView.superclass.constructor.apply(this, arguments);
28524 this.getEl().setStyle("outline", "0px none");
28525 this.getEl().unselectable();
28526 if (this.dragGroup) {
28527 this.setDraggable(this.dragGroup.split(","));
28529 if (this.dropGroup) {
28530 this.setDroppable(this.dropGroup.split(","));
28532 if (this.deletable) {
28533 this.setDeletable();
28535 this.isDirtyFlag = false;
28541 Roo.extend(Roo.DDView, Roo.View, {
28542 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28543 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28544 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28545 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28549 reset: Roo.emptyFn,
28551 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28553 validate: function() {
28557 destroy: function() {
28558 this.purgeListeners();
28559 this.getEl.removeAllListeners();
28560 this.getEl().remove();
28561 if (this.dragZone) {
28562 if (this.dragZone.destroy) {
28563 this.dragZone.destroy();
28566 if (this.dropZone) {
28567 if (this.dropZone.destroy) {
28568 this.dropZone.destroy();
28573 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28574 getName: function() {
28578 /** Loads the View from a JSON string representing the Records to put into the Store. */
28579 setValue: function(v) {
28581 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28584 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28585 this.store.proxy = new Roo.data.MemoryProxy(data);
28589 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28590 getValue: function() {
28592 this.store.each(function(rec) {
28593 result += rec.id + ',';
28595 return result.substr(0, result.length - 1) + ')';
28598 getIds: function() {
28599 var i = 0, result = new Array(this.store.getCount());
28600 this.store.each(function(rec) {
28601 result[i++] = rec.id;
28606 isDirty: function() {
28607 return this.isDirtyFlag;
28611 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28612 * whole Element becomes the target, and this causes the drop gesture to append.
28614 getTargetFromEvent : function(e) {
28615 var target = e.getTarget();
28616 while ((target !== null) && (target.parentNode != this.el.dom)) {
28617 target = target.parentNode;
28620 target = this.el.dom.lastChild || this.el.dom;
28626 * Create the drag data which consists of an object which has the property "ddel" as
28627 * the drag proxy element.
28629 getDragData : function(e) {
28630 var target = this.findItemFromChild(e.getTarget());
28632 this.handleSelection(e);
28633 var selNodes = this.getSelectedNodes();
28636 copy: this.copy || (this.allowCopy && e.ctrlKey),
28640 var selectedIndices = this.getSelectedIndexes();
28641 for (var i = 0; i < selectedIndices.length; i++) {
28642 dragData.records.push(this.store.getAt(selectedIndices[i]));
28644 if (selNodes.length == 1) {
28645 dragData.ddel = target.cloneNode(true); // the div element
28647 var div = document.createElement('div'); // create the multi element drag "ghost"
28648 div.className = 'multi-proxy';
28649 for (var i = 0, len = selNodes.length; i < len; i++) {
28650 div.appendChild(selNodes[i].cloneNode(true));
28652 dragData.ddel = div;
28654 //console.log(dragData)
28655 //console.log(dragData.ddel.innerHTML)
28658 //console.log('nodragData')
28662 /** Specify to which ddGroup items in this DDView may be dragged. */
28663 setDraggable: function(ddGroup) {
28664 if (ddGroup instanceof Array) {
28665 Roo.each(ddGroup, this.setDraggable, this);
28668 if (this.dragZone) {
28669 this.dragZone.addToGroup(ddGroup);
28671 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28672 containerScroll: true,
28676 // Draggability implies selection. DragZone's mousedown selects the element.
28677 if (!this.multiSelect) { this.singleSelect = true; }
28679 // Wire the DragZone's handlers up to methods in *this*
28680 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28684 /** Specify from which ddGroup this DDView accepts drops. */
28685 setDroppable: function(ddGroup) {
28686 if (ddGroup instanceof Array) {
28687 Roo.each(ddGroup, this.setDroppable, this);
28690 if (this.dropZone) {
28691 this.dropZone.addToGroup(ddGroup);
28693 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28694 containerScroll: true,
28698 // Wire the DropZone's handlers up to methods in *this*
28699 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28700 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28701 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28702 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28703 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28707 /** Decide whether to drop above or below a View node. */
28708 getDropPoint : function(e, n, dd){
28709 if (n == this.el.dom) { return "above"; }
28710 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28711 var c = t + (b - t) / 2;
28712 var y = Roo.lib.Event.getPageY(e);
28720 onNodeEnter : function(n, dd, e, data){
28724 onNodeOver : function(n, dd, e, data){
28725 var pt = this.getDropPoint(e, n, dd);
28726 // set the insert point style on the target node
28727 var dragElClass = this.dropNotAllowed;
28730 if (pt == "above"){
28731 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28732 targetElClass = "x-view-drag-insert-above";
28734 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28735 targetElClass = "x-view-drag-insert-below";
28737 if (this.lastInsertClass != targetElClass){
28738 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28739 this.lastInsertClass = targetElClass;
28742 return dragElClass;
28745 onNodeOut : function(n, dd, e, data){
28746 this.removeDropIndicators(n);
28749 onNodeDrop : function(n, dd, e, data){
28750 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28753 var pt = this.getDropPoint(e, n, dd);
28754 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28755 if (pt == "below") { insertAt++; }
28756 for (var i = 0; i < data.records.length; i++) {
28757 var r = data.records[i];
28758 var dup = this.store.getById(r.id);
28759 if (dup && (dd != this.dragZone)) {
28760 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28763 this.store.insert(insertAt++, r.copy());
28765 data.source.isDirtyFlag = true;
28767 this.store.insert(insertAt++, r);
28769 this.isDirtyFlag = true;
28772 this.dragZone.cachedTarget = null;
28776 removeDropIndicators : function(n){
28778 Roo.fly(n).removeClass([
28779 "x-view-drag-insert-above",
28780 "x-view-drag-insert-below"]);
28781 this.lastInsertClass = "_noclass";
28786 * Utility method. Add a delete option to the DDView's context menu.
28787 * @param {String} imageUrl The URL of the "delete" icon image.
28789 setDeletable: function(imageUrl) {
28790 if (!this.singleSelect && !this.multiSelect) {
28791 this.singleSelect = true;
28793 var c = this.getContextMenu();
28794 this.contextMenu.on("itemclick", function(item) {
28797 this.remove(this.getSelectedIndexes());
28801 this.contextMenu.add({
28808 /** Return the context menu for this DDView. */
28809 getContextMenu: function() {
28810 if (!this.contextMenu) {
28811 // Create the View's context menu
28812 this.contextMenu = new Roo.menu.Menu({
28813 id: this.id + "-contextmenu"
28815 this.el.on("contextmenu", this.showContextMenu, this);
28817 return this.contextMenu;
28820 disableContextMenu: function() {
28821 if (this.contextMenu) {
28822 this.el.un("contextmenu", this.showContextMenu, this);
28826 showContextMenu: function(e, item) {
28827 item = this.findItemFromChild(e.getTarget());
28830 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28831 this.contextMenu.showAt(e.getXY());
28836 * Remove {@link Roo.data.Record}s at the specified indices.
28837 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28839 remove: function(selectedIndices) {
28840 selectedIndices = [].concat(selectedIndices);
28841 for (var i = 0; i < selectedIndices.length; i++) {
28842 var rec = this.store.getAt(selectedIndices[i]);
28843 this.store.remove(rec);
28848 * Double click fires the event, but also, if this is draggable, and there is only one other
28849 * related DropZone, it transfers the selected node.
28851 onDblClick : function(e){
28852 var item = this.findItemFromChild(e.getTarget());
28854 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28857 if (this.dragGroup) {
28858 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28859 while (targets.indexOf(this.dropZone) > -1) {
28860 targets.remove(this.dropZone);
28862 if (targets.length == 1) {
28863 this.dragZone.cachedTarget = null;
28864 var el = Roo.get(targets[0].getEl());
28865 var box = el.getBox(true);
28866 targets[0].onNodeDrop(el.dom, {
28868 xy: [box.x, box.y + box.height - 1]
28869 }, null, this.getDragData(e));
28875 handleSelection: function(e) {
28876 this.dragZone.cachedTarget = null;
28877 var item = this.findItemFromChild(e.getTarget());
28879 this.clearSelections(true);
28882 if (item && (this.multiSelect || this.singleSelect)){
28883 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28884 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28885 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28886 this.unselect(item);
28888 this.select(item, this.multiSelect && e.ctrlKey);
28889 this.lastSelection = item;
28894 onItemClick : function(item, index, e){
28895 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28901 unselect : function(nodeInfo, suppressEvent){
28902 var node = this.getNode(nodeInfo);
28903 if(node && this.isSelected(node)){
28904 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28905 Roo.fly(node).removeClass(this.selectedClass);
28906 this.selections.remove(node);
28907 if(!suppressEvent){
28908 this.fireEvent("selectionchange", this, this.selections);
28916 * Ext JS Library 1.1.1
28917 * Copyright(c) 2006-2007, Ext JS, LLC.
28919 * Originally Released Under LGPL - original licence link has changed is not relivant.
28922 * <script type="text/javascript">
28926 * @class Roo.LayoutManager
28927 * @extends Roo.util.Observable
28928 * Base class for layout managers.
28930 Roo.LayoutManager = function(container, config){
28931 Roo.LayoutManager.superclass.constructor.call(this);
28932 this.el = Roo.get(container);
28933 // ie scrollbar fix
28934 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28935 document.body.scroll = "no";
28936 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28937 this.el.position('relative');
28939 this.id = this.el.id;
28940 this.el.addClass("x-layout-container");
28941 /** false to disable window resize monitoring @type Boolean */
28942 this.monitorWindowResize = true;
28947 * Fires when a layout is performed.
28948 * @param {Roo.LayoutManager} this
28952 * @event regionresized
28953 * Fires when the user resizes a region.
28954 * @param {Roo.LayoutRegion} region The resized region
28955 * @param {Number} newSize The new size (width for east/west, height for north/south)
28957 "regionresized" : true,
28959 * @event regioncollapsed
28960 * Fires when a region is collapsed.
28961 * @param {Roo.LayoutRegion} region The collapsed region
28963 "regioncollapsed" : true,
28965 * @event regionexpanded
28966 * Fires when a region is expanded.
28967 * @param {Roo.LayoutRegion} region The expanded region
28969 "regionexpanded" : true
28971 this.updating = false;
28972 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28975 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28977 * Returns true if this layout is currently being updated
28978 * @return {Boolean}
28980 isUpdating : function(){
28981 return this.updating;
28985 * Suspend the LayoutManager from doing auto-layouts while
28986 * making multiple add or remove calls
28988 beginUpdate : function(){
28989 this.updating = true;
28993 * Restore auto-layouts and optionally disable the manager from performing a layout
28994 * @param {Boolean} noLayout true to disable a layout update
28996 endUpdate : function(noLayout){
28997 this.updating = false;
29003 layout: function(){
29007 onRegionResized : function(region, newSize){
29008 this.fireEvent("regionresized", region, newSize);
29012 onRegionCollapsed : function(region){
29013 this.fireEvent("regioncollapsed", region);
29016 onRegionExpanded : function(region){
29017 this.fireEvent("regionexpanded", region);
29021 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29022 * performs box-model adjustments.
29023 * @return {Object} The size as an object {width: (the width), height: (the height)}
29025 getViewSize : function(){
29027 if(this.el.dom != document.body){
29028 size = this.el.getSize();
29030 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29032 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29033 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29038 * Returns the Element this layout is bound to.
29039 * @return {Roo.Element}
29041 getEl : function(){
29046 * Returns the specified region.
29047 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29048 * @return {Roo.LayoutRegion}
29050 getRegion : function(target){
29051 return this.regions[target.toLowerCase()];
29054 onWindowResize : function(){
29055 if(this.monitorWindowResize){
29061 * Ext JS Library 1.1.1
29062 * Copyright(c) 2006-2007, Ext JS, LLC.
29064 * Originally Released Under LGPL - original licence link has changed is not relivant.
29067 * <script type="text/javascript">
29070 * @class Roo.BorderLayout
29071 * @extends Roo.LayoutManager
29072 * @children Roo.ContentPanel
29073 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29074 * please see: <br><br>
29075 * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
29076 * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
29079 var layout = new Roo.BorderLayout(document.body, {
29113 preferredTabWidth: 150
29118 var CP = Roo.ContentPanel;
29120 layout.beginUpdate();
29121 layout.add("north", new CP("north", "North"));
29122 layout.add("south", new CP("south", {title: "South", closable: true}));
29123 layout.add("west", new CP("west", {title: "West"}));
29124 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29125 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29126 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29127 layout.getRegion("center").showPanel("center1");
29128 layout.endUpdate();
29131 <b>The container the layout is rendered into can be either the body element or any other element.
29132 If it is not the body element, the container needs to either be an absolute positioned element,
29133 or you will need to add "position:relative" to the css of the container. You will also need to specify
29134 the container size if it is not the body element.</b>
29137 * Create a new BorderLayout
29138 * @param {String/HTMLElement/Element} container The container this layout is bound to
29139 * @param {Object} config Configuration options
29141 Roo.BorderLayout = function(container, config){
29142 config = config || {};
29143 Roo.BorderLayout.superclass.constructor.call(this, container, config);
29144 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29145 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29146 var target = this.factory.validRegions[i];
29147 if(config[target]){
29148 this.addRegion(target, config[target]);
29153 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29156 * @cfg {Roo.LayoutRegion} east
29159 * @cfg {Roo.LayoutRegion} west
29162 * @cfg {Roo.LayoutRegion} north
29165 * @cfg {Roo.LayoutRegion} south
29168 * @cfg {Roo.LayoutRegion} center
29171 * Creates and adds a new region if it doesn't already exist.
29172 * @param {String} target The target region key (north, south, east, west or center).
29173 * @param {Object} config The regions config object
29174 * @return {BorderLayoutRegion} The new region
29176 addRegion : function(target, config){
29177 if(!this.regions[target]){
29178 var r = this.factory.create(target, this, config);
29179 this.bindRegion(target, r);
29181 return this.regions[target];
29185 bindRegion : function(name, r){
29186 this.regions[name] = r;
29187 r.on("visibilitychange", this.layout, this);
29188 r.on("paneladded", this.layout, this);
29189 r.on("panelremoved", this.layout, this);
29190 r.on("invalidated", this.layout, this);
29191 r.on("resized", this.onRegionResized, this);
29192 r.on("collapsed", this.onRegionCollapsed, this);
29193 r.on("expanded", this.onRegionExpanded, this);
29197 * Performs a layout update.
29199 layout : function(){
29200 if(this.updating) {
29203 var size = this.getViewSize();
29204 var w = size.width;
29205 var h = size.height;
29210 //var x = 0, y = 0;
29212 var rs = this.regions;
29213 var north = rs["north"];
29214 var south = rs["south"];
29215 var west = rs["west"];
29216 var east = rs["east"];
29217 var center = rs["center"];
29218 //if(this.hideOnLayout){ // not supported anymore
29219 //c.el.setStyle("display", "none");
29221 if(north && north.isVisible()){
29222 var b = north.getBox();
29223 var m = north.getMargins();
29224 b.width = w - (m.left+m.right);
29227 centerY = b.height + b.y + m.bottom;
29228 centerH -= centerY;
29229 north.updateBox(this.safeBox(b));
29231 if(south && south.isVisible()){
29232 var b = south.getBox();
29233 var m = south.getMargins();
29234 b.width = w - (m.left+m.right);
29236 var totalHeight = (b.height + m.top + m.bottom);
29237 b.y = h - totalHeight + m.top;
29238 centerH -= totalHeight;
29239 south.updateBox(this.safeBox(b));
29241 if(west && west.isVisible()){
29242 var b = west.getBox();
29243 var m = west.getMargins();
29244 b.height = centerH - (m.top+m.bottom);
29246 b.y = centerY + m.top;
29247 var totalWidth = (b.width + m.left + m.right);
29248 centerX += totalWidth;
29249 centerW -= totalWidth;
29250 west.updateBox(this.safeBox(b));
29252 if(east && east.isVisible()){
29253 var b = east.getBox();
29254 var m = east.getMargins();
29255 b.height = centerH - (m.top+m.bottom);
29256 var totalWidth = (b.width + m.left + m.right);
29257 b.x = w - totalWidth + m.left;
29258 b.y = centerY + m.top;
29259 centerW -= totalWidth;
29260 east.updateBox(this.safeBox(b));
29263 var m = center.getMargins();
29265 x: centerX + m.left,
29266 y: centerY + m.top,
29267 width: centerW - (m.left+m.right),
29268 height: centerH - (m.top+m.bottom)
29270 //if(this.hideOnLayout){
29271 //center.el.setStyle("display", "block");
29273 center.updateBox(this.safeBox(centerBox));
29276 this.fireEvent("layout", this);
29280 safeBox : function(box){
29281 box.width = Math.max(0, box.width);
29282 box.height = Math.max(0, box.height);
29287 * Adds a ContentPanel (or subclass) to this layout.
29288 * @param {String} target The target region key (north, south, east, west or center).
29289 * @param {Roo.ContentPanel} panel The panel to add
29290 * @return {Roo.ContentPanel} The added panel
29292 add : function(target, panel){
29294 target = target.toLowerCase();
29295 return this.regions[target].add(panel);
29299 * Remove a ContentPanel (or subclass) to this layout.
29300 * @param {String} target The target region key (north, south, east, west or center).
29301 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29302 * @return {Roo.ContentPanel} The removed panel
29304 remove : function(target, panel){
29305 target = target.toLowerCase();
29306 return this.regions[target].remove(panel);
29310 * Searches all regions for a panel with the specified id
29311 * @param {String} panelId
29312 * @return {Roo.ContentPanel} The panel or null if it wasn't found
29314 findPanel : function(panelId){
29315 var rs = this.regions;
29316 for(var target in rs){
29317 if(typeof rs[target] != "function"){
29318 var p = rs[target].getPanel(panelId);
29328 * Searches all regions for a panel with the specified id and activates (shows) it.
29329 * @param {String/ContentPanel} panelId The panels id or the panel itself
29330 * @return {Roo.ContentPanel} The shown panel or null
29332 showPanel : function(panelId) {
29333 var rs = this.regions;
29334 for(var target in rs){
29335 var r = rs[target];
29336 if(typeof r != "function"){
29337 if(r.hasPanel(panelId)){
29338 return r.showPanel(panelId);
29346 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29347 * @param {Roo.state.Provider} provider (optional) An alternate state provider
29349 restoreState : function(provider){
29351 provider = Roo.state.Manager;
29353 var sm = new Roo.LayoutStateManager();
29354 sm.init(this, provider);
29358 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
29359 * object should contain properties for each region to add ContentPanels to, and each property's value should be
29360 * a valid ContentPanel config object. Example:
29362 // Create the main layout
29363 var layout = new Roo.BorderLayout('main-ct', {
29374 // Create and add multiple ContentPanels at once via configs
29377 id: 'source-files',
29379 title:'Ext Source Files',
29392 * @param {Object} regions An object containing ContentPanel configs by region name
29394 batchAdd : function(regions){
29395 this.beginUpdate();
29396 for(var rname in regions){
29397 var lr = this.regions[rname];
29399 this.addTypedPanels(lr, regions[rname]);
29406 addTypedPanels : function(lr, ps){
29407 if(typeof ps == 'string'){
29408 lr.add(new Roo.ContentPanel(ps));
29410 else if(ps instanceof Array){
29411 for(var i =0, len = ps.length; i < len; i++){
29412 this.addTypedPanels(lr, ps[i]);
29415 else if(!ps.events){ // raw config?
29417 delete ps.el; // prevent conflict
29418 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29420 else { // panel object assumed!
29425 * Adds a xtype elements to the layout.
29429 xtype : 'ContentPanel',
29436 xtype : 'NestedLayoutPanel',
29442 items : [ ... list of content panels or nested layout panels.. ]
29446 * @param {Object} cfg Xtype definition of item to add.
29448 addxtype : function(cfg)
29450 // basically accepts a pannel...
29451 // can accept a layout region..!?!?
29452 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29454 if (!cfg.xtype.match(/Panel$/)) {
29459 if (typeof(cfg.region) == 'undefined') {
29460 Roo.log("Failed to add Panel, region was not set");
29464 var region = cfg.region;
29470 xitems = cfg.items;
29477 case 'ContentPanel': // ContentPanel (el, cfg)
29478 case 'ScrollPanel': // ContentPanel (el, cfg)
29480 if(cfg.autoCreate) {
29481 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29483 var el = this.el.createChild();
29484 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29487 this.add(region, ret);
29491 case 'TreePanel': // our new panel!
29492 cfg.el = this.el.createChild();
29493 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29494 this.add(region, ret);
29497 case 'NestedLayoutPanel':
29498 // create a new Layout (which is a Border Layout...
29499 var el = this.el.createChild();
29500 var clayout = cfg.layout;
29502 clayout.items = clayout.items || [];
29503 // replace this exitems with the clayout ones..
29504 xitems = clayout.items;
29507 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29508 cfg.background = false;
29510 var layout = new Roo.BorderLayout(el, clayout);
29512 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29513 //console.log('adding nested layout panel ' + cfg.toSource());
29514 this.add(region, ret);
29515 nb = {}; /// find first...
29520 // needs grid and region
29522 //var el = this.getRegion(region).el.createChild();
29523 var el = this.el.createChild();
29524 // create the grid first...
29526 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29528 if (region == 'center' && this.active ) {
29529 cfg.background = false;
29531 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29533 this.add(region, ret);
29534 if (cfg.background) {
29535 ret.on('activate', function(gp) {
29536 if (!gp.grid.rendered) {
29551 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29553 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29554 this.add(region, ret);
29557 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29561 // GridPanel (grid, cfg)
29564 this.beginUpdate();
29568 Roo.each(xitems, function(i) {
29569 region = nb && i.region ? i.region : false;
29571 var add = ret.addxtype(i);
29574 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29575 if (!i.background) {
29576 abn[region] = nb[region] ;
29583 // make the last non-background panel active..
29584 //if (nb) { Roo.log(abn); }
29587 for(var r in abn) {
29588 region = this.getRegion(r);
29590 // tried using nb[r], but it does not work..
29592 region.showPanel(abn[r]);
29603 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29604 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29605 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29606 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29609 var CP = Roo.ContentPanel;
29611 var layout = Roo.BorderLayout.create({
29615 panels: [new CP("north", "North")]
29624 panels: [new CP("west", {title: "West"})]
29633 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29642 panels: [new CP("south", {title: "South", closable: true})]
29649 preferredTabWidth: 150,
29651 new CP("center1", {title: "Close Me", closable: true}),
29652 new CP("center2", {title: "Center Panel", closable: false})
29657 layout.getRegion("center").showPanel("center1");
29662 Roo.BorderLayout.create = function(config, targetEl){
29663 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29664 layout.beginUpdate();
29665 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29666 for(var j = 0, jlen = regions.length; j < jlen; j++){
29667 var lr = regions[j];
29668 if(layout.regions[lr] && config[lr].panels){
29669 var r = layout.regions[lr];
29670 var ps = config[lr].panels;
29671 layout.addTypedPanels(r, ps);
29674 layout.endUpdate();
29679 Roo.BorderLayout.RegionFactory = {
29681 validRegions : ["north","south","east","west","center"],
29684 create : function(target, mgr, config){
29685 target = target.toLowerCase();
29686 if(config.lightweight || config.basic){
29687 return new Roo.BasicLayoutRegion(mgr, config, target);
29691 return new Roo.NorthLayoutRegion(mgr, config);
29693 return new Roo.SouthLayoutRegion(mgr, config);
29695 return new Roo.EastLayoutRegion(mgr, config);
29697 return new Roo.WestLayoutRegion(mgr, config);
29699 return new Roo.CenterLayoutRegion(mgr, config);
29701 throw 'Layout region "'+target+'" not supported.';
29705 * Ext JS Library 1.1.1
29706 * Copyright(c) 2006-2007, Ext JS, LLC.
29708 * Originally Released Under LGPL - original licence link has changed is not relivant.
29711 * <script type="text/javascript">
29715 * @class Roo.BasicLayoutRegion
29716 * @extends Roo.util.Observable
29717 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29718 * and does not have a titlebar, tabs or any other features. All it does is size and position
29719 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29721 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29723 this.position = pos;
29726 * @scope Roo.BasicLayoutRegion
29730 * @event beforeremove
29731 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29732 * @param {Roo.LayoutRegion} this
29733 * @param {Roo.ContentPanel} panel The panel
29734 * @param {Object} e The cancel event object
29736 "beforeremove" : true,
29738 * @event invalidated
29739 * Fires when the layout for this region is changed.
29740 * @param {Roo.LayoutRegion} this
29742 "invalidated" : true,
29744 * @event visibilitychange
29745 * Fires when this region is shown or hidden
29746 * @param {Roo.LayoutRegion} this
29747 * @param {Boolean} visibility true or false
29749 "visibilitychange" : true,
29751 * @event paneladded
29752 * Fires when a panel is added.
29753 * @param {Roo.LayoutRegion} this
29754 * @param {Roo.ContentPanel} panel The panel
29756 "paneladded" : true,
29758 * @event panelremoved
29759 * Fires when a panel is removed.
29760 * @param {Roo.LayoutRegion} this
29761 * @param {Roo.ContentPanel} panel The panel
29763 "panelremoved" : true,
29765 * @event beforecollapse
29766 * Fires when this region before collapse.
29767 * @param {Roo.LayoutRegion} this
29769 "beforecollapse" : true,
29772 * Fires when this region is collapsed.
29773 * @param {Roo.LayoutRegion} this
29775 "collapsed" : true,
29778 * Fires when this region is expanded.
29779 * @param {Roo.LayoutRegion} this
29784 * Fires when this region is slid into view.
29785 * @param {Roo.LayoutRegion} this
29787 "slideshow" : true,
29790 * Fires when this region slides out of view.
29791 * @param {Roo.LayoutRegion} this
29793 "slidehide" : true,
29795 * @event panelactivated
29796 * Fires when a panel is activated.
29797 * @param {Roo.LayoutRegion} this
29798 * @param {Roo.ContentPanel} panel The activated panel
29800 "panelactivated" : true,
29803 * Fires when the user resizes this region.
29804 * @param {Roo.LayoutRegion} this
29805 * @param {Number} newSize The new size (width for east/west, height for north/south)
29809 /** A collection of panels in this region. @type Roo.util.MixedCollection */
29810 this.panels = new Roo.util.MixedCollection();
29811 this.panels.getKey = this.getPanelId.createDelegate(this);
29813 this.activePanel = null;
29814 // ensure listeners are added...
29816 if (config.listeners || config.events) {
29817 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29818 listeners : config.listeners || {},
29819 events : config.events || {}
29823 if(skipConfig !== true){
29824 this.applyConfig(config);
29828 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29829 getPanelId : function(p){
29833 applyConfig : function(config){
29834 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29835 this.config = config;
29840 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
29841 * the width, for horizontal (north, south) the height.
29842 * @param {Number} newSize The new width or height
29844 resizeTo : function(newSize){
29845 var el = this.el ? this.el :
29846 (this.activePanel ? this.activePanel.getEl() : null);
29848 switch(this.position){
29851 el.setWidth(newSize);
29852 this.fireEvent("resized", this, newSize);
29856 el.setHeight(newSize);
29857 this.fireEvent("resized", this, newSize);
29863 getBox : function(){
29864 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29867 getMargins : function(){
29868 return this.margins;
29871 updateBox : function(box){
29873 var el = this.activePanel.getEl();
29874 el.dom.style.left = box.x + "px";
29875 el.dom.style.top = box.y + "px";
29876 this.activePanel.setSize(box.width, box.height);
29880 * Returns the container element for this region.
29881 * @return {Roo.Element}
29883 getEl : function(){
29884 return this.activePanel;
29888 * Returns true if this region is currently visible.
29889 * @return {Boolean}
29891 isVisible : function(){
29892 return this.activePanel ? true : false;
29895 setActivePanel : function(panel){
29896 panel = this.getPanel(panel);
29897 if(this.activePanel && this.activePanel != panel){
29898 this.activePanel.setActiveState(false);
29899 this.activePanel.getEl().setLeftTop(-10000,-10000);
29901 this.activePanel = panel;
29902 panel.setActiveState(true);
29904 panel.setSize(this.box.width, this.box.height);
29906 this.fireEvent("panelactivated", this, panel);
29907 this.fireEvent("invalidated");
29911 * Show the specified panel.
29912 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29913 * @return {Roo.ContentPanel} The shown panel or null
29915 showPanel : function(panel){
29916 if(panel = this.getPanel(panel)){
29917 this.setActivePanel(panel);
29923 * Get the active panel for this region.
29924 * @return {Roo.ContentPanel} The active panel or null
29926 getActivePanel : function(){
29927 return this.activePanel;
29931 * Add the passed ContentPanel(s)
29932 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29933 * @return {Roo.ContentPanel} The panel added (if only one was added)
29935 add : function(panel){
29936 if(arguments.length > 1){
29937 for(var i = 0, len = arguments.length; i < len; i++) {
29938 this.add(arguments[i]);
29942 if(this.hasPanel(panel)){
29943 this.showPanel(panel);
29946 var el = panel.getEl();
29947 if(el.dom.parentNode != this.mgr.el.dom){
29948 this.mgr.el.dom.appendChild(el.dom);
29950 if(panel.setRegion){
29951 panel.setRegion(this);
29953 this.panels.add(panel);
29954 el.setStyle("position", "absolute");
29955 if(!panel.background){
29956 this.setActivePanel(panel);
29957 if(this.config.initialSize && this.panels.getCount()==1){
29958 this.resizeTo(this.config.initialSize);
29961 this.fireEvent("paneladded", this, panel);
29966 * Returns true if the panel is in this region.
29967 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29968 * @return {Boolean}
29970 hasPanel : function(panel){
29971 if(typeof panel == "object"){ // must be panel obj
29972 panel = panel.getId();
29974 return this.getPanel(panel) ? true : false;
29978 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29979 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29980 * @param {Boolean} preservePanel Overrides the config preservePanel option
29981 * @return {Roo.ContentPanel} The panel that was removed
29983 remove : function(panel, preservePanel){
29984 panel = this.getPanel(panel);
29989 this.fireEvent("beforeremove", this, panel, e);
29990 if(e.cancel === true){
29993 var panelId = panel.getId();
29994 this.panels.removeKey(panelId);
29999 * Returns the panel specified or null if it's not in this region.
30000 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30001 * @return {Roo.ContentPanel}
30003 getPanel : function(id){
30004 if(typeof id == "object"){ // must be panel obj
30007 return this.panels.get(id);
30011 * Returns this regions position (north/south/east/west/center).
30014 getPosition: function(){
30015 return this.position;
30019 * Ext JS Library 1.1.1
30020 * Copyright(c) 2006-2007, Ext JS, LLC.
30022 * Originally Released Under LGPL - original licence link has changed is not relivant.
30025 * <script type="text/javascript">
30029 * @class Roo.LayoutRegion
30030 * @extends Roo.BasicLayoutRegion
30031 * This class represents a region in a layout manager.
30032 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
30033 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
30034 * @cfg {Boolean} floatable False to disable floating (defaults to true)
30035 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30036 * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
30037 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
30038 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
30039 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
30040 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
30041 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
30042 * @cfg {String} title The title for the region (overrides panel titles)
30043 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
30044 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30045 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
30046 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30047 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
30048 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30049 * the space available, similar to FireFox 1.5 tabs (defaults to false)
30050 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
30051 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
30052 * @cfg {Boolean} showPin True to show a pin button
30053 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
30054 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
30055 * @cfg {Boolean} disableTabTips True to disable tab tooltips
30056 * @cfg {Number} width For East/West panels
30057 * @cfg {Number} height For North/South panels
30058 * @cfg {Boolean} split To show the splitter
30059 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
30061 Roo.LayoutRegion = function(mgr, config, pos){
30062 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30063 var dh = Roo.DomHelper;
30064 /** This region's container element
30065 * @type Roo.Element */
30066 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30067 /** This region's title element
30068 * @type Roo.Element */
30070 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30071 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
30072 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30074 this.titleEl.enableDisplayMode();
30075 /** This region's title text element
30076 * @type HTMLElement */
30077 this.titleTextEl = this.titleEl.dom.firstChild;
30078 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30079 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30080 this.closeBtn.enableDisplayMode();
30081 this.closeBtn.on("click", this.closeClicked, this);
30082 this.closeBtn.hide();
30084 this.createBody(config);
30085 this.visible = true;
30086 this.collapsed = false;
30088 if(config.hideWhenEmpty){
30090 this.on("paneladded", this.validateVisibility, this);
30091 this.on("panelremoved", this.validateVisibility, this);
30093 this.applyConfig(config);
30096 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30098 createBody : function(){
30099 /** This region's body element
30100 * @type Roo.Element */
30101 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30104 applyConfig : function(c){
30105 if(c.collapsible && this.position != "center" && !this.collapsedEl){
30106 var dh = Roo.DomHelper;
30107 if(c.titlebar !== false){
30108 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30109 this.collapseBtn.on("click", this.collapse, this);
30110 this.collapseBtn.enableDisplayMode();
30112 if(c.showPin === true || this.showPin){
30113 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30114 this.stickBtn.enableDisplayMode();
30115 this.stickBtn.on("click", this.expand, this);
30116 this.stickBtn.hide();
30119 /** This region's collapsed element
30120 * @type Roo.Element */
30121 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30122 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30124 if(c.floatable !== false){
30125 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30126 this.collapsedEl.on("click", this.collapseClick, this);
30129 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30130 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30131 id: "message", unselectable: "on", style:{"float":"left"}});
30132 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30134 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30135 this.expandBtn.on("click", this.expand, this);
30137 if(this.collapseBtn){
30138 this.collapseBtn.setVisible(c.collapsible == true);
30140 this.cmargins = c.cmargins || this.cmargins ||
30141 (this.position == "west" || this.position == "east" ?
30142 {top: 0, left: 2, right:2, bottom: 0} :
30143 {top: 2, left: 0, right:0, bottom: 2});
30144 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30145 this.bottomTabs = c.tabPosition != "top";
30146 this.autoScroll = c.autoScroll || false;
30147 if(this.autoScroll){
30148 this.bodyEl.setStyle("overflow", "auto");
30150 this.bodyEl.setStyle("overflow", "hidden");
30152 //if(c.titlebar !== false){
30153 if((!c.titlebar && !c.title) || c.titlebar === false){
30154 this.titleEl.hide();
30156 this.titleEl.show();
30158 this.titleTextEl.innerHTML = c.title;
30162 this.duration = c.duration || .30;
30163 this.slideDuration = c.slideDuration || .45;
30166 this.collapse(true);
30173 * Returns true if this region is currently visible.
30174 * @return {Boolean}
30176 isVisible : function(){
30177 return this.visible;
30181 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30182 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
30184 setCollapsedTitle : function(title){
30185 title = title || " ";
30186 if(this.collapsedTitleTextEl){
30187 this.collapsedTitleTextEl.innerHTML = title;
30191 getBox : function(){
30193 if(!this.collapsed){
30194 b = this.el.getBox(false, true);
30196 b = this.collapsedEl.getBox(false, true);
30201 getMargins : function(){
30202 return this.collapsed ? this.cmargins : this.margins;
30205 highlight : function(){
30206 this.el.addClass("x-layout-panel-dragover");
30209 unhighlight : function(){
30210 this.el.removeClass("x-layout-panel-dragover");
30213 updateBox : function(box){
30215 if(!this.collapsed){
30216 this.el.dom.style.left = box.x + "px";
30217 this.el.dom.style.top = box.y + "px";
30218 this.updateBody(box.width, box.height);
30220 this.collapsedEl.dom.style.left = box.x + "px";
30221 this.collapsedEl.dom.style.top = box.y + "px";
30222 this.collapsedEl.setSize(box.width, box.height);
30225 this.tabs.autoSizeTabs();
30229 updateBody : function(w, h){
30231 this.el.setWidth(w);
30232 w -= this.el.getBorderWidth("rl");
30233 if(this.config.adjustments){
30234 w += this.config.adjustments[0];
30238 this.el.setHeight(h);
30239 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30240 h -= this.el.getBorderWidth("tb");
30241 if(this.config.adjustments){
30242 h += this.config.adjustments[1];
30244 this.bodyEl.setHeight(h);
30246 h = this.tabs.syncHeight(h);
30249 if(this.panelSize){
30250 w = w !== null ? w : this.panelSize.width;
30251 h = h !== null ? h : this.panelSize.height;
30253 if(this.activePanel){
30254 var el = this.activePanel.getEl();
30255 w = w !== null ? w : el.getWidth();
30256 h = h !== null ? h : el.getHeight();
30257 this.panelSize = {width: w, height: h};
30258 this.activePanel.setSize(w, h);
30260 if(Roo.isIE && this.tabs){
30261 this.tabs.el.repaint();
30266 * Returns the container element for this region.
30267 * @return {Roo.Element}
30269 getEl : function(){
30274 * Hides this region.
30277 if(!this.collapsed){
30278 this.el.dom.style.left = "-2000px";
30281 this.collapsedEl.dom.style.left = "-2000px";
30282 this.collapsedEl.hide();
30284 this.visible = false;
30285 this.fireEvent("visibilitychange", this, false);
30289 * Shows this region if it was previously hidden.
30292 if(!this.collapsed){
30295 this.collapsedEl.show();
30297 this.visible = true;
30298 this.fireEvent("visibilitychange", this, true);
30301 closeClicked : function(){
30302 if(this.activePanel){
30303 this.remove(this.activePanel);
30307 collapseClick : function(e){
30309 e.stopPropagation();
30312 e.stopPropagation();
30318 * Collapses this region.
30319 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30321 collapse : function(skipAnim, skipCheck){
30322 if(this.collapsed) {
30326 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30328 this.collapsed = true;
30330 this.split.el.hide();
30332 if(this.config.animate && skipAnim !== true){
30333 this.fireEvent("invalidated", this);
30334 this.animateCollapse();
30336 this.el.setLocation(-20000,-20000);
30338 this.collapsedEl.show();
30339 this.fireEvent("collapsed", this);
30340 this.fireEvent("invalidated", this);
30346 animateCollapse : function(){
30351 * Expands this region if it was previously collapsed.
30352 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30353 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30355 expand : function(e, skipAnim){
30357 e.stopPropagation();
30359 if(!this.collapsed || this.el.hasActiveFx()) {
30363 this.afterSlideIn();
30366 this.collapsed = false;
30367 if(this.config.animate && skipAnim !== true){
30368 this.animateExpand();
30372 this.split.el.show();
30374 this.collapsedEl.setLocation(-2000,-2000);
30375 this.collapsedEl.hide();
30376 this.fireEvent("invalidated", this);
30377 this.fireEvent("expanded", this);
30381 animateExpand : function(){
30385 initTabs : function()
30387 this.bodyEl.setStyle("overflow", "hidden");
30388 var ts = new Roo.TabPanel(
30391 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30392 disableTooltips: this.config.disableTabTips,
30393 toolbar : this.config.toolbar
30396 if(this.config.hideTabs){
30397 ts.stripWrap.setDisplayed(false);
30400 ts.resizeTabs = this.config.resizeTabs === true;
30401 ts.minTabWidth = this.config.minTabWidth || 40;
30402 ts.maxTabWidth = this.config.maxTabWidth || 250;
30403 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30404 ts.monitorResize = false;
30405 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30406 ts.bodyEl.addClass('x-layout-tabs-body');
30407 this.panels.each(this.initPanelAsTab, this);
30410 initPanelAsTab : function(panel){
30411 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30412 this.config.closeOnTab && panel.isClosable());
30413 if(panel.tabTip !== undefined){
30414 ti.setTooltip(panel.tabTip);
30416 ti.on("activate", function(){
30417 this.setActivePanel(panel);
30419 if(this.config.closeOnTab){
30420 ti.on("beforeclose", function(t, e){
30422 this.remove(panel);
30428 updatePanelTitle : function(panel, title){
30429 if(this.activePanel == panel){
30430 this.updateTitle(title);
30433 var ti = this.tabs.getTab(panel.getEl().id);
30435 if(panel.tabTip !== undefined){
30436 ti.setTooltip(panel.tabTip);
30441 updateTitle : function(title){
30442 if(this.titleTextEl && !this.config.title){
30443 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30447 setActivePanel : function(panel){
30448 panel = this.getPanel(panel);
30449 if(this.activePanel && this.activePanel != panel){
30450 this.activePanel.setActiveState(false);
30452 this.activePanel = panel;
30453 panel.setActiveState(true);
30454 if(this.panelSize){
30455 panel.setSize(this.panelSize.width, this.panelSize.height);
30458 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30460 this.updateTitle(panel.getTitle());
30462 this.fireEvent("invalidated", this);
30464 this.fireEvent("panelactivated", this, panel);
30468 * Shows the specified panel.
30469 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30470 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30472 showPanel : function(panel)
30474 panel = this.getPanel(panel);
30477 var tab = this.tabs.getTab(panel.getEl().id);
30478 if(tab.isHidden()){
30479 this.tabs.unhideTab(tab.id);
30483 this.setActivePanel(panel);
30490 * Get the active panel for this region.
30491 * @return {Roo.ContentPanel} The active panel or null
30493 getActivePanel : function(){
30494 return this.activePanel;
30497 validateVisibility : function(){
30498 if(this.panels.getCount() < 1){
30499 this.updateTitle(" ");
30500 this.closeBtn.hide();
30503 if(!this.isVisible()){
30510 * Adds the passed ContentPanel(s) to this region.
30511 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30512 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30514 add : function(panel){
30515 if(arguments.length > 1){
30516 for(var i = 0, len = arguments.length; i < len; i++) {
30517 this.add(arguments[i]);
30521 if(this.hasPanel(panel)){
30522 this.showPanel(panel);
30525 panel.setRegion(this);
30526 this.panels.add(panel);
30527 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30528 this.bodyEl.dom.appendChild(panel.getEl().dom);
30529 if(panel.background !== true){
30530 this.setActivePanel(panel);
30532 this.fireEvent("paneladded", this, panel);
30538 this.initPanelAsTab(panel);
30540 if(panel.background !== true){
30541 this.tabs.activate(panel.getEl().id);
30543 this.fireEvent("paneladded", this, panel);
30548 * Hides the tab for the specified panel.
30549 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30551 hidePanel : function(panel){
30552 if(this.tabs && (panel = this.getPanel(panel))){
30553 this.tabs.hideTab(panel.getEl().id);
30558 * Unhides the tab for a previously hidden panel.
30559 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30561 unhidePanel : function(panel){
30562 if(this.tabs && (panel = this.getPanel(panel))){
30563 this.tabs.unhideTab(panel.getEl().id);
30567 clearPanels : function(){
30568 while(this.panels.getCount() > 0){
30569 this.remove(this.panels.first());
30574 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30575 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30576 * @param {Boolean} preservePanel Overrides the config preservePanel option
30577 * @return {Roo.ContentPanel} The panel that was removed
30579 remove : function(panel, preservePanel){
30580 panel = this.getPanel(panel);
30585 this.fireEvent("beforeremove", this, panel, e);
30586 if(e.cancel === true){
30589 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30590 var panelId = panel.getId();
30591 this.panels.removeKey(panelId);
30593 document.body.appendChild(panel.getEl().dom);
30596 this.tabs.removeTab(panel.getEl().id);
30597 }else if (!preservePanel){
30598 this.bodyEl.dom.removeChild(panel.getEl().dom);
30600 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30601 var p = this.panels.first();
30602 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30603 tempEl.appendChild(p.getEl().dom);
30604 this.bodyEl.update("");
30605 this.bodyEl.dom.appendChild(p.getEl().dom);
30607 this.updateTitle(p.getTitle());
30609 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30610 this.setActivePanel(p);
30612 panel.setRegion(null);
30613 if(this.activePanel == panel){
30614 this.activePanel = null;
30616 if(this.config.autoDestroy !== false && preservePanel !== true){
30617 try{panel.destroy();}catch(e){}
30619 this.fireEvent("panelremoved", this, panel);
30624 * Returns the TabPanel component used by this region
30625 * @return {Roo.TabPanel}
30627 getTabs : function(){
30631 createTool : function(parentEl, className){
30632 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30633 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30634 btn.addClassOnOver("x-layout-tools-button-over");
30639 * Ext JS Library 1.1.1
30640 * Copyright(c) 2006-2007, Ext JS, LLC.
30642 * Originally Released Under LGPL - original licence link has changed is not relivant.
30645 * <script type="text/javascript">
30651 * @class Roo.SplitLayoutRegion
30652 * @extends Roo.LayoutRegion
30653 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30655 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30656 this.cursor = cursor;
30657 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30660 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30661 splitTip : "Drag to resize.",
30662 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30663 useSplitTips : false,
30665 applyConfig : function(config){
30666 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30669 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30670 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30671 /** The SplitBar for this region
30672 * @type Roo.SplitBar */
30673 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30674 this.split.on("moved", this.onSplitMove, this);
30675 this.split.useShim = config.useShim === true;
30676 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30677 if(this.useSplitTips){
30678 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30680 if(config.collapsible){
30681 this.split.el.on("dblclick", this.collapse, this);
30684 if(typeof config.minSize != "undefined"){
30685 this.split.minSize = config.minSize;
30687 if(typeof config.maxSize != "undefined"){
30688 this.split.maxSize = config.maxSize;
30690 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30691 this.hideSplitter();
30696 getHMaxSize : function(){
30697 var cmax = this.config.maxSize || 10000;
30698 var center = this.mgr.getRegion("center");
30699 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30702 getVMaxSize : function(){
30703 var cmax = this.config.maxSize || 10000;
30704 var center = this.mgr.getRegion("center");
30705 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30708 onSplitMove : function(split, newSize){
30709 this.fireEvent("resized", this, newSize);
30713 * Returns the {@link Roo.SplitBar} for this region.
30714 * @return {Roo.SplitBar}
30716 getSplitBar : function(){
30721 this.hideSplitter();
30722 Roo.SplitLayoutRegion.superclass.hide.call(this);
30725 hideSplitter : function(){
30727 this.split.el.setLocation(-2000,-2000);
30728 this.split.el.hide();
30734 this.split.el.show();
30736 Roo.SplitLayoutRegion.superclass.show.call(this);
30739 beforeSlide: function(){
30740 if(Roo.isGecko){// firefox overflow auto bug workaround
30741 this.bodyEl.clip();
30743 this.tabs.bodyEl.clip();
30745 if(this.activePanel){
30746 this.activePanel.getEl().clip();
30748 if(this.activePanel.beforeSlide){
30749 this.activePanel.beforeSlide();
30755 afterSlide : function(){
30756 if(Roo.isGecko){// firefox overflow auto bug workaround
30757 this.bodyEl.unclip();
30759 this.tabs.bodyEl.unclip();
30761 if(this.activePanel){
30762 this.activePanel.getEl().unclip();
30763 if(this.activePanel.afterSlide){
30764 this.activePanel.afterSlide();
30770 initAutoHide : function(){
30771 if(this.autoHide !== false){
30772 if(!this.autoHideHd){
30773 var st = new Roo.util.DelayedTask(this.slideIn, this);
30774 this.autoHideHd = {
30775 "mouseout": function(e){
30776 if(!e.within(this.el, true)){
30780 "mouseover" : function(e){
30786 this.el.on(this.autoHideHd);
30790 clearAutoHide : function(){
30791 if(this.autoHide !== false){
30792 this.el.un("mouseout", this.autoHideHd.mouseout);
30793 this.el.un("mouseover", this.autoHideHd.mouseover);
30797 clearMonitor : function(){
30798 Roo.get(document).un("click", this.slideInIf, this);
30801 // these names are backwards but not changed for compat
30802 slideOut : function(){
30803 if(this.isSlid || this.el.hasActiveFx()){
30806 this.isSlid = true;
30807 if(this.collapseBtn){
30808 this.collapseBtn.hide();
30810 this.closeBtnState = this.closeBtn.getStyle('display');
30811 this.closeBtn.hide();
30813 this.stickBtn.show();
30816 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30817 this.beforeSlide();
30818 this.el.setStyle("z-index", 10001);
30819 this.el.slideIn(this.getSlideAnchor(), {
30820 callback: function(){
30822 this.initAutoHide();
30823 Roo.get(document).on("click", this.slideInIf, this);
30824 this.fireEvent("slideshow", this);
30831 afterSlideIn : function(){
30832 this.clearAutoHide();
30833 this.isSlid = false;
30834 this.clearMonitor();
30835 this.el.setStyle("z-index", "");
30836 if(this.collapseBtn){
30837 this.collapseBtn.show();
30839 this.closeBtn.setStyle('display', this.closeBtnState);
30841 this.stickBtn.hide();
30843 this.fireEvent("slidehide", this);
30846 slideIn : function(cb){
30847 if(!this.isSlid || this.el.hasActiveFx()){
30851 this.isSlid = false;
30852 this.beforeSlide();
30853 this.el.slideOut(this.getSlideAnchor(), {
30854 callback: function(){
30855 this.el.setLeftTop(-10000, -10000);
30857 this.afterSlideIn();
30865 slideInIf : function(e){
30866 if(!e.within(this.el)){
30871 animateCollapse : function(){
30872 this.beforeSlide();
30873 this.el.setStyle("z-index", 20000);
30874 var anchor = this.getSlideAnchor();
30875 this.el.slideOut(anchor, {
30876 callback : function(){
30877 this.el.setStyle("z-index", "");
30878 this.collapsedEl.slideIn(anchor, {duration:.3});
30880 this.el.setLocation(-10000,-10000);
30882 this.fireEvent("collapsed", this);
30889 animateExpand : function(){
30890 this.beforeSlide();
30891 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30892 this.el.setStyle("z-index", 20000);
30893 this.collapsedEl.hide({
30896 this.el.slideIn(this.getSlideAnchor(), {
30897 callback : function(){
30898 this.el.setStyle("z-index", "");
30901 this.split.el.show();
30903 this.fireEvent("invalidated", this);
30904 this.fireEvent("expanded", this);
30932 getAnchor : function(){
30933 return this.anchors[this.position];
30936 getCollapseAnchor : function(){
30937 return this.canchors[this.position];
30940 getSlideAnchor : function(){
30941 return this.sanchors[this.position];
30944 getAlignAdj : function(){
30945 var cm = this.cmargins;
30946 switch(this.position){
30962 getExpandAdj : function(){
30963 var c = this.collapsedEl, cm = this.cmargins;
30964 switch(this.position){
30966 return [-(cm.right+c.getWidth()+cm.left), 0];
30969 return [cm.right+c.getWidth()+cm.left, 0];
30972 return [0, -(cm.top+cm.bottom+c.getHeight())];
30975 return [0, cm.top+cm.bottom+c.getHeight()];
30981 * Ext JS Library 1.1.1
30982 * Copyright(c) 2006-2007, Ext JS, LLC.
30984 * Originally Released Under LGPL - original licence link has changed is not relivant.
30987 * <script type="text/javascript">
30990 * These classes are private internal classes
30992 Roo.CenterLayoutRegion = function(mgr, config){
30993 Roo.LayoutRegion.call(this, mgr, config, "center");
30994 this.visible = true;
30995 this.minWidth = config.minWidth || 20;
30996 this.minHeight = config.minHeight || 20;
30999 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31001 // center panel can't be hidden
31005 // center panel can't be hidden
31008 getMinWidth: function(){
31009 return this.minWidth;
31012 getMinHeight: function(){
31013 return this.minHeight;
31018 Roo.NorthLayoutRegion = function(mgr, config){
31019 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31021 this.split.placement = Roo.SplitBar.TOP;
31022 this.split.orientation = Roo.SplitBar.VERTICAL;
31023 this.split.el.addClass("x-layout-split-v");
31025 var size = config.initialSize || config.height;
31026 if(typeof size != "undefined"){
31027 this.el.setHeight(size);
31030 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31031 orientation: Roo.SplitBar.VERTICAL,
31032 getBox : function(){
31033 if(this.collapsed){
31034 return this.collapsedEl.getBox();
31036 var box = this.el.getBox();
31038 box.height += this.split.el.getHeight();
31043 updateBox : function(box){
31044 if(this.split && !this.collapsed){
31045 box.height -= this.split.el.getHeight();
31046 this.split.el.setLeft(box.x);
31047 this.split.el.setTop(box.y+box.height);
31048 this.split.el.setWidth(box.width);
31050 if(this.collapsed){
31051 this.updateBody(box.width, null);
31053 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31057 Roo.SouthLayoutRegion = function(mgr, config){
31058 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31060 this.split.placement = Roo.SplitBar.BOTTOM;
31061 this.split.orientation = Roo.SplitBar.VERTICAL;
31062 this.split.el.addClass("x-layout-split-v");
31064 var size = config.initialSize || config.height;
31065 if(typeof size != "undefined"){
31066 this.el.setHeight(size);
31069 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31070 orientation: Roo.SplitBar.VERTICAL,
31071 getBox : function(){
31072 if(this.collapsed){
31073 return this.collapsedEl.getBox();
31075 var box = this.el.getBox();
31077 var sh = this.split.el.getHeight();
31084 updateBox : function(box){
31085 if(this.split && !this.collapsed){
31086 var sh = this.split.el.getHeight();
31089 this.split.el.setLeft(box.x);
31090 this.split.el.setTop(box.y-sh);
31091 this.split.el.setWidth(box.width);
31093 if(this.collapsed){
31094 this.updateBody(box.width, null);
31096 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31100 Roo.EastLayoutRegion = function(mgr, config){
31101 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31103 this.split.placement = Roo.SplitBar.RIGHT;
31104 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31105 this.split.el.addClass("x-layout-split-h");
31107 var size = config.initialSize || config.width;
31108 if(typeof size != "undefined"){
31109 this.el.setWidth(size);
31112 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31113 orientation: Roo.SplitBar.HORIZONTAL,
31114 getBox : function(){
31115 if(this.collapsed){
31116 return this.collapsedEl.getBox();
31118 var box = this.el.getBox();
31120 var sw = this.split.el.getWidth();
31127 updateBox : function(box){
31128 if(this.split && !this.collapsed){
31129 var sw = this.split.el.getWidth();
31131 this.split.el.setLeft(box.x);
31132 this.split.el.setTop(box.y);
31133 this.split.el.setHeight(box.height);
31136 if(this.collapsed){
31137 this.updateBody(null, box.height);
31139 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31143 Roo.WestLayoutRegion = function(mgr, config){
31144 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31146 this.split.placement = Roo.SplitBar.LEFT;
31147 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31148 this.split.el.addClass("x-layout-split-h");
31150 var size = config.initialSize || config.width;
31151 if(typeof size != "undefined"){
31152 this.el.setWidth(size);
31155 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31156 orientation: Roo.SplitBar.HORIZONTAL,
31157 getBox : function(){
31158 if(this.collapsed){
31159 return this.collapsedEl.getBox();
31161 var box = this.el.getBox();
31163 box.width += this.split.el.getWidth();
31168 updateBox : function(box){
31169 if(this.split && !this.collapsed){
31170 var sw = this.split.el.getWidth();
31172 this.split.el.setLeft(box.x+box.width);
31173 this.split.el.setTop(box.y);
31174 this.split.el.setHeight(box.height);
31176 if(this.collapsed){
31177 this.updateBody(null, box.height);
31179 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31184 * Ext JS Library 1.1.1
31185 * Copyright(c) 2006-2007, Ext JS, LLC.
31187 * Originally Released Under LGPL - original licence link has changed is not relivant.
31190 * <script type="text/javascript">
31195 * Private internal class for reading and applying state
31197 Roo.LayoutStateManager = function(layout){
31198 // default empty state
31207 Roo.LayoutStateManager.prototype = {
31208 init : function(layout, provider){
31209 this.provider = provider;
31210 var state = provider.get(layout.id+"-layout-state");
31212 var wasUpdating = layout.isUpdating();
31214 layout.beginUpdate();
31216 for(var key in state){
31217 if(typeof state[key] != "function"){
31218 var rstate = state[key];
31219 var r = layout.getRegion(key);
31222 r.resizeTo(rstate.size);
31224 if(rstate.collapsed == true){
31227 r.expand(null, true);
31233 layout.endUpdate();
31235 this.state = state;
31237 this.layout = layout;
31238 layout.on("regionresized", this.onRegionResized, this);
31239 layout.on("regioncollapsed", this.onRegionCollapsed, this);
31240 layout.on("regionexpanded", this.onRegionExpanded, this);
31243 storeState : function(){
31244 this.provider.set(this.layout.id+"-layout-state", this.state);
31247 onRegionResized : function(region, newSize){
31248 this.state[region.getPosition()].size = newSize;
31252 onRegionCollapsed : function(region){
31253 this.state[region.getPosition()].collapsed = true;
31257 onRegionExpanded : function(region){
31258 this.state[region.getPosition()].collapsed = false;
31263 * Ext JS Library 1.1.1
31264 * Copyright(c) 2006-2007, Ext JS, LLC.
31266 * Originally Released Under LGPL - original licence link has changed is not relivant.
31269 * <script type="text/javascript">
31272 * @class Roo.ContentPanel
31273 * @extends Roo.util.Observable
31274 * @children Roo.form.Form Roo.JsonView Roo.View
31276 * A basic ContentPanel element.
31277 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
31278 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
31279 * @cfg {Boolean|Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
31280 * @cfg {Boolean} closable True if the panel can be closed/removed
31281 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31282 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31283 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
31284 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31285 * @cfg {String} title The title for this panel
31286 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31287 * @cfg {String} url Calls {@link #setUrl} with this value
31288 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31289 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31290 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31291 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
31292 * @cfg {String} style Extra style to add to the content panel
31293 * @cfg {Roo.menu.Menu} menu popup menu
31296 * Create a new ContentPanel.
31297 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31298 * @param {String/Object} config A string to set only the title or a config object
31299 * @param {String} content (optional) Set the HTML content for this panel
31300 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31302 Roo.ContentPanel = function(el, config, content){
31306 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31310 if (config && config.parentLayout) {
31311 el = config.parentLayout.el.createChild();
31314 if(el.autoCreate){ // xtype is available if this is called from factory
31318 this.el = Roo.get(el);
31319 if(!this.el && config && config.autoCreate){
31320 if(typeof config.autoCreate == "object"){
31321 if(!config.autoCreate.id){
31322 config.autoCreate.id = config.id||el;
31324 this.el = Roo.DomHelper.append(document.body,
31325 config.autoCreate, true);
31327 this.el = Roo.DomHelper.append(document.body,
31328 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31333 this.closable = false;
31334 this.loaded = false;
31335 this.active = false;
31336 if(typeof config == "string"){
31337 this.title = config;
31339 Roo.apply(this, config);
31342 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31343 this.wrapEl = this.el.wrap();
31344 this.toolbar.container = this.el.insertSibling(false, 'before');
31345 this.toolbar = new Roo.Toolbar(this.toolbar);
31348 // xtype created footer. - not sure if will work as we normally have to render first..
31349 if (this.footer && !this.footer.el && this.footer.xtype) {
31350 if (!this.wrapEl) {
31351 this.wrapEl = this.el.wrap();
31354 this.footer.container = this.wrapEl.createChild();
31356 this.footer = Roo.factory(this.footer, Roo);
31361 this.resizeEl = Roo.get(this.resizeEl, true);
31363 this.resizeEl = this.el;
31365 // handle view.xtype
31373 * Fires when this panel is activated.
31374 * @param {Roo.ContentPanel} this
31378 * @event deactivate
31379 * Fires when this panel is activated.
31380 * @param {Roo.ContentPanel} this
31382 "deactivate" : true,
31386 * Fires when this panel is resized if fitToFrame is true.
31387 * @param {Roo.ContentPanel} this
31388 * @param {Number} width The width after any component adjustments
31389 * @param {Number} height The height after any component adjustments
31395 * Fires when this tab is created
31396 * @param {Roo.ContentPanel} this
31406 if(this.autoScroll){
31407 this.resizeEl.setStyle("overflow", "auto");
31409 // fix randome scrolling
31410 this.el.on('scroll', function() {
31411 Roo.log('fix random scolling');
31412 this.scrollTo('top',0);
31415 content = content || this.content;
31417 this.setContent(content);
31419 if(config && config.url){
31420 this.setUrl(this.url, this.params, this.loadOnce);
31425 Roo.ContentPanel.superclass.constructor.call(this);
31427 if (this.view && typeof(this.view.xtype) != 'undefined') {
31428 this.view.el = this.el.appendChild(document.createElement("div"));
31429 this.view = Roo.factory(this.view);
31430 this.view.render && this.view.render(false, '');
31434 this.fireEvent('render', this);
31437 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31439 setRegion : function(region){
31440 this.region = region;
31442 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31444 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31449 * Returns the toolbar for this Panel if one was configured.
31450 * @return {Roo.Toolbar}
31452 getToolbar : function(){
31453 return this.toolbar;
31456 setActiveState : function(active){
31457 this.active = active;
31459 this.fireEvent("deactivate", this);
31461 this.fireEvent("activate", this);
31465 * Updates this panel's element
31466 * @param {String} content The new content
31467 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31469 setContent : function(content, loadScripts){
31470 this.el.update(content, loadScripts);
31473 ignoreResize : function(w, h){
31474 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31477 this.lastSize = {width: w, height: h};
31482 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31483 * @return {Roo.UpdateManager} The UpdateManager
31485 getUpdateManager : function(){
31486 return this.el.getUpdateManager();
31489 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31490 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
31493 url: "your-url.php",
31494 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31495 callback: yourFunction,
31496 scope: yourObject, //(optional scope)
31499 text: "Loading...",
31504 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31505 * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
31506 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
31507 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31508 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
31509 * @return {Roo.ContentPanel} this
31512 var um = this.el.getUpdateManager();
31513 um.update.apply(um, arguments);
31519 * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
31520 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31521 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
31522 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
31523 * @return {Roo.UpdateManager} The UpdateManager
31525 setUrl : function(url, params, loadOnce){
31526 if(this.refreshDelegate){
31527 this.removeListener("activate", this.refreshDelegate);
31529 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31530 this.on("activate", this.refreshDelegate);
31531 return this.el.getUpdateManager();
31534 _handleRefresh : function(url, params, loadOnce){
31535 if(!loadOnce || !this.loaded){
31536 var updater = this.el.getUpdateManager();
31537 updater.update(url, params, this._setLoaded.createDelegate(this));
31541 _setLoaded : function(){
31542 this.loaded = true;
31546 * Returns this panel's id
31549 getId : function(){
31554 * Returns this panel's element - used by regiosn to add.
31555 * @return {Roo.Element}
31557 getEl : function(){
31558 return this.wrapEl || this.el;
31561 adjustForComponents : function(width, height)
31563 //Roo.log('adjustForComponents ');
31564 if(this.resizeEl != this.el){
31565 width -= this.el.getFrameWidth('lr');
31566 height -= this.el.getFrameWidth('tb');
31569 var te = this.toolbar.getEl();
31570 height -= te.getHeight();
31571 te.setWidth(width);
31574 var te = this.footer.getEl();
31575 //Roo.log("footer:" + te.getHeight());
31577 height -= te.getHeight();
31578 te.setWidth(width);
31582 if(this.adjustments){
31583 width += this.adjustments[0];
31584 height += this.adjustments[1];
31586 return {"width": width, "height": height};
31589 setSize : function(width, height){
31590 if(this.fitToFrame && !this.ignoreResize(width, height)){
31591 if(this.fitContainer && this.resizeEl != this.el){
31592 this.el.setSize(width, height);
31594 var size = this.adjustForComponents(width, height);
31595 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31596 this.fireEvent('resize', this, size.width, size.height);
31601 * Returns this panel's title
31604 getTitle : function(){
31609 * Set this panel's title
31610 * @param {String} title
31612 setTitle : function(title){
31613 this.title = title;
31615 this.region.updatePanelTitle(this, title);
31620 * Returns true is this panel was configured to be closable
31621 * @return {Boolean}
31623 isClosable : function(){
31624 return this.closable;
31627 beforeSlide : function(){
31629 this.resizeEl.clip();
31632 afterSlide : function(){
31634 this.resizeEl.unclip();
31638 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31639 * Will fail silently if the {@link #setUrl} method has not been called.
31640 * This does not activate the panel, just updates its content.
31642 refresh : function(){
31643 if(this.refreshDelegate){
31644 this.loaded = false;
31645 this.refreshDelegate();
31650 * Destroys this panel
31652 destroy : function(){
31653 this.el.removeAllListeners();
31654 var tempEl = document.createElement("span");
31655 tempEl.appendChild(this.el.dom);
31656 tempEl.innerHTML = "";
31662 * form - if the content panel contains a form - this is a reference to it.
31663 * @type {Roo.form.Form}
31667 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31668 * This contains a reference to it.
31674 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31684 * @param {Object} cfg Xtype definition of item to add.
31687 addxtype : function(cfg) {
31689 if (cfg.xtype.match(/^Form$/)) {
31692 //if (this.footer) {
31693 // el = this.footer.container.insertSibling(false, 'before');
31695 el = this.el.createChild();
31698 this.form = new Roo.form.Form(cfg);
31701 if ( this.form.allItems.length) {
31702 this.form.render(el.dom);
31706 // should only have one of theses..
31707 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31708 // views.. should not be just added - used named prop 'view''
31710 cfg.el = this.el.appendChild(document.createElement("div"));
31713 var ret = new Roo.factory(cfg);
31715 ret.render && ret.render(false, ''); // render blank..
31724 * @class Roo.GridPanel
31725 * @extends Roo.ContentPanel
31727 * Create a new GridPanel.
31728 * @param {Roo.grid.Grid} grid The grid for this panel
31729 * @param {String/Object} config A string to set only the panel's title, or a config object
31731 Roo.GridPanel = function(grid, config){
31734 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31735 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31737 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31739 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31742 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31744 // xtype created footer. - not sure if will work as we normally have to render first..
31745 if (this.footer && !this.footer.el && this.footer.xtype) {
31747 this.footer.container = this.grid.getView().getFooterPanel(true);
31748 this.footer.dataSource = this.grid.dataSource;
31749 this.footer = Roo.factory(this.footer, Roo);
31753 grid.monitorWindowResize = false; // turn off autosizing
31754 grid.autoHeight = false;
31755 grid.autoWidth = false;
31757 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31760 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31761 getId : function(){
31762 return this.grid.id;
31766 * Returns the grid for this panel
31767 * @return {Roo.grid.Grid}
31769 getGrid : function(){
31773 setSize : function(width, height){
31774 if(!this.ignoreResize(width, height)){
31775 var grid = this.grid;
31776 var size = this.adjustForComponents(width, height);
31777 grid.getGridEl().setSize(size.width, size.height);
31782 beforeSlide : function(){
31783 this.grid.getView().scroller.clip();
31786 afterSlide : function(){
31787 this.grid.getView().scroller.unclip();
31790 destroy : function(){
31791 this.grid.destroy();
31793 Roo.GridPanel.superclass.destroy.call(this);
31799 * @class Roo.NestedLayoutPanel
31800 * @extends Roo.ContentPanel
31802 * Create a new NestedLayoutPanel.
31805 * @param {Roo.BorderLayout} layout [required] The layout for this panel
31806 * @param {String/Object} config A string to set only the title or a config object
31808 Roo.NestedLayoutPanel = function(layout, config)
31810 // construct with only one argument..
31811 /* FIXME - implement nicer consturctors
31812 if (layout.layout) {
31814 layout = config.layout;
31815 delete config.layout;
31817 if (layout.xtype && !layout.getEl) {
31818 // then layout needs constructing..
31819 layout = Roo.factory(layout, Roo);
31824 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31826 layout.monitorWindowResize = false; // turn off autosizing
31827 this.layout = layout;
31828 this.layout.getEl().addClass("x-layout-nested-layout");
31835 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31837 setSize : function(width, height){
31838 if(!this.ignoreResize(width, height)){
31839 var size = this.adjustForComponents(width, height);
31840 var el = this.layout.getEl();
31841 el.setSize(size.width, size.height);
31842 var touch = el.dom.offsetWidth;
31843 this.layout.layout();
31844 // ie requires a double layout on the first pass
31845 if(Roo.isIE && !this.initialized){
31846 this.initialized = true;
31847 this.layout.layout();
31852 // activate all subpanels if not currently active..
31854 setActiveState : function(active){
31855 this.active = active;
31857 this.fireEvent("deactivate", this);
31861 this.fireEvent("activate", this);
31862 // not sure if this should happen before or after..
31863 if (!this.layout) {
31864 return; // should not happen..
31867 for (var r in this.layout.regions) {
31868 reg = this.layout.getRegion(r);
31869 if (reg.getActivePanel()) {
31870 //reg.showPanel(reg.getActivePanel()); // force it to activate..
31871 reg.setActivePanel(reg.getActivePanel());
31874 if (!reg.panels.length) {
31877 reg.showPanel(reg.getPanel(0));
31886 * Returns the nested BorderLayout for this panel
31887 * @return {Roo.BorderLayout}
31889 getLayout : function(){
31890 return this.layout;
31894 * Adds a xtype elements to the layout of the nested panel
31898 xtype : 'ContentPanel',
31905 xtype : 'NestedLayoutPanel',
31911 items : [ ... list of content panels or nested layout panels.. ]
31915 * @param {Object} cfg Xtype definition of item to add.
31917 addxtype : function(cfg) {
31918 return this.layout.addxtype(cfg);
31923 Roo.ScrollPanel = function(el, config, content){
31924 config = config || {};
31925 config.fitToFrame = true;
31926 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31928 this.el.dom.style.overflow = "hidden";
31929 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31930 this.el.removeClass("x-layout-inactive-content");
31931 this.el.on("mousewheel", this.onWheel, this);
31933 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
31934 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
31935 up.unselectable(); down.unselectable();
31936 up.on("click", this.scrollUp, this);
31937 down.on("click", this.scrollDown, this);
31938 up.addClassOnOver("x-scroller-btn-over");
31939 down.addClassOnOver("x-scroller-btn-over");
31940 up.addClassOnClick("x-scroller-btn-click");
31941 down.addClassOnClick("x-scroller-btn-click");
31942 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31944 this.resizeEl = this.el;
31945 this.el = wrap; this.up = up; this.down = down;
31948 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31950 wheelIncrement : 5,
31951 scrollUp : function(){
31952 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31955 scrollDown : function(){
31956 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31959 afterScroll : function(){
31960 var el = this.resizeEl;
31961 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31962 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31963 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31966 setSize : function(){
31967 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31968 this.afterScroll();
31971 onWheel : function(e){
31972 var d = e.getWheelDelta();
31973 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31974 this.afterScroll();
31978 setContent : function(content, loadScripts){
31979 this.resizeEl.update(content, loadScripts);
31987 * @class Roo.TreePanel
31988 * @extends Roo.ContentPanel
31989 * Treepanel component
31992 * Create a new TreePanel. - defaults to fit/scoll contents.
31993 * @param {String/Object} config A string to set only the panel's title, or a config object
31995 Roo.TreePanel = function(config){
31996 var el = config.el;
31997 var tree = config.tree;
31998 delete config.tree;
31999 delete config.el; // hopefull!
32001 // wrapper for IE7 strict & safari scroll issue
32003 var treeEl = el.createChild();
32004 config.resizeEl = treeEl;
32008 Roo.TreePanel.superclass.constructor.call(this, el, config);
32011 this.tree = new Roo.tree.TreePanel(treeEl , tree);
32012 //console.log(tree);
32013 this.on('activate', function()
32015 if (this.tree.rendered) {
32018 //console.log('render tree');
32019 this.tree.render();
32021 // this should not be needed.. - it's actually the 'el' that resizes?
32022 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32024 //this.on('resize', function (cp, w, h) {
32025 // this.tree.innerCt.setWidth(w);
32026 // this.tree.innerCt.setHeight(h);
32027 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
32034 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
32038 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32056 * Ext JS Library 1.1.1
32057 * Copyright(c) 2006-2007, Ext JS, LLC.
32059 * Originally Released Under LGPL - original licence link has changed is not relivant.
32062 * <script type="text/javascript">
32067 * @class Roo.ReaderLayout
32068 * @extends Roo.BorderLayout
32069 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
32070 * center region containing two nested regions (a top one for a list view and one for item preview below),
32071 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32072 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32073 * expedites the setup of the overall layout and regions for this common application style.
32076 var reader = new Roo.ReaderLayout();
32077 var CP = Roo.ContentPanel; // shortcut for adding
32079 reader.beginUpdate();
32080 reader.add("north", new CP("north", "North"));
32081 reader.add("west", new CP("west", {title: "West"}));
32082 reader.add("east", new CP("east", {title: "East"}));
32084 reader.regions.listView.add(new CP("listView", "List"));
32085 reader.regions.preview.add(new CP("preview", "Preview"));
32086 reader.endUpdate();
32089 * Create a new ReaderLayout
32090 * @param {Object} config Configuration options
32091 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32092 * document.body if omitted)
32094 Roo.ReaderLayout = function(config, renderTo){
32095 var c = config || {size:{}};
32096 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32097 north: c.north !== false ? Roo.apply({
32101 }, c.north) : false,
32102 west: c.west !== false ? Roo.apply({
32110 margins:{left:5,right:0,bottom:5,top:5},
32111 cmargins:{left:5,right:5,bottom:5,top:5}
32112 }, c.west) : false,
32113 east: c.east !== false ? Roo.apply({
32121 margins:{left:0,right:5,bottom:5,top:5},
32122 cmargins:{left:5,right:5,bottom:5,top:5}
32123 }, c.east) : false,
32124 center: Roo.apply({
32125 tabPosition: 'top',
32129 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32133 this.el.addClass('x-reader');
32135 this.beginUpdate();
32137 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32138 south: c.preview !== false ? Roo.apply({
32145 cmargins:{top:5,left:0, right:0, bottom:0}
32146 }, c.preview) : false,
32147 center: Roo.apply({
32153 this.add('center', new Roo.NestedLayoutPanel(inner,
32154 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32158 this.regions.preview = inner.getRegion('south');
32159 this.regions.listView = inner.getRegion('center');
32162 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32164 * Ext JS Library 1.1.1
32165 * Copyright(c) 2006-2007, Ext JS, LLC.
32167 * Originally Released Under LGPL - original licence link has changed is not relivant.
32170 * <script type="text/javascript">
32174 * @class Roo.grid.Grid
32175 * @extends Roo.util.Observable
32176 * This class represents the primary interface of a component based grid control.
32177 * <br><br>Usage:<pre><code>
32178 var grid = new Roo.grid.Grid("my-container-id", {
32181 selModel: mySelectionModel,
32182 autoSizeColumns: true,
32183 monitorWindowResize: false,
32184 trackMouseOver: true
32189 * <b>Common Problems:</b><br/>
32190 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32191 * element will correct this<br/>
32192 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32193 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32194 * are unpredictable.<br/>
32195 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32196 * grid to calculate dimensions/offsets.<br/>
32198 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32199 * The container MUST have some type of size defined for the grid to fill. The container will be
32200 * automatically set to position relative if it isn't already.
32201 * @param {Object} config A config object that sets properties on this grid.
32203 Roo.grid.Grid = function(container, config){
32204 // initialize the container
32205 this.container = Roo.get(container);
32206 this.container.update("");
32207 this.container.setStyle("overflow", "hidden");
32208 this.container.addClass('x-grid-container');
32210 this.id = this.container.id;
32212 Roo.apply(this, config);
32213 // check and correct shorthanded configs
32215 this.dataSource = this.ds;
32219 this.colModel = this.cm;
32223 this.selModel = this.sm;
32227 if (this.selModel) {
32228 this.selModel = Roo.factory(this.selModel, Roo.grid);
32229 this.sm = this.selModel;
32230 this.sm.xmodule = this.xmodule || false;
32232 if (typeof(this.colModel.config) == 'undefined') {
32233 this.colModel = new Roo.grid.ColumnModel(this.colModel);
32234 this.cm = this.colModel;
32235 this.cm.xmodule = this.xmodule || false;
32237 if (this.dataSource) {
32238 this.dataSource= Roo.factory(this.dataSource, Roo.data);
32239 this.ds = this.dataSource;
32240 this.ds.xmodule = this.xmodule || false;
32247 this.container.setWidth(this.width);
32251 this.container.setHeight(this.height);
32258 * The raw click event for the entire grid.
32259 * @param {Roo.EventObject} e
32264 * The raw dblclick event for the entire grid.
32265 * @param {Roo.EventObject} e
32269 * @event contextmenu
32270 * The raw contextmenu event for the entire grid.
32271 * @param {Roo.EventObject} e
32273 "contextmenu" : true,
32276 * The raw mousedown event for the entire grid.
32277 * @param {Roo.EventObject} e
32279 "mousedown" : true,
32282 * The raw mouseup event for the entire grid.
32283 * @param {Roo.EventObject} e
32288 * The raw mouseover event for the entire grid.
32289 * @param {Roo.EventObject} e
32291 "mouseover" : true,
32294 * The raw mouseout event for the entire grid.
32295 * @param {Roo.EventObject} e
32300 * The raw keypress event for the entire grid.
32301 * @param {Roo.EventObject} e
32306 * The raw keydown event for the entire grid.
32307 * @param {Roo.EventObject} e
32315 * Fires when a cell is clicked
32316 * @param {Grid} this
32317 * @param {Number} rowIndex
32318 * @param {Number} columnIndex
32319 * @param {Roo.EventObject} e
32321 "cellclick" : true,
32323 * @event celldblclick
32324 * Fires when a cell is double clicked
32325 * @param {Grid} this
32326 * @param {Number} rowIndex
32327 * @param {Number} columnIndex
32328 * @param {Roo.EventObject} e
32330 "celldblclick" : true,
32333 * Fires when a row is clicked
32334 * @param {Grid} this
32335 * @param {Number} rowIndex
32336 * @param {Roo.EventObject} e
32340 * @event rowdblclick
32341 * Fires when a row is double clicked
32342 * @param {Grid} this
32343 * @param {Number} rowIndex
32344 * @param {Roo.EventObject} e
32346 "rowdblclick" : true,
32348 * @event headerclick
32349 * Fires when a header is clicked
32350 * @param {Grid} this
32351 * @param {Number} columnIndex
32352 * @param {Roo.EventObject} e
32354 "headerclick" : true,
32356 * @event headerdblclick
32357 * Fires when a header cell is double clicked
32358 * @param {Grid} this
32359 * @param {Number} columnIndex
32360 * @param {Roo.EventObject} e
32362 "headerdblclick" : true,
32364 * @event rowcontextmenu
32365 * Fires when a row is right clicked
32366 * @param {Grid} this
32367 * @param {Number} rowIndex
32368 * @param {Roo.EventObject} e
32370 "rowcontextmenu" : true,
32372 * @event cellcontextmenu
32373 * Fires when a cell is right clicked
32374 * @param {Grid} this
32375 * @param {Number} rowIndex
32376 * @param {Number} cellIndex
32377 * @param {Roo.EventObject} e
32379 "cellcontextmenu" : true,
32381 * @event headercontextmenu
32382 * Fires when a header is right clicked
32383 * @param {Grid} this
32384 * @param {Number} columnIndex
32385 * @param {Roo.EventObject} e
32387 "headercontextmenu" : true,
32389 * @event bodyscroll
32390 * Fires when the body element is scrolled
32391 * @param {Number} scrollLeft
32392 * @param {Number} scrollTop
32394 "bodyscroll" : true,
32396 * @event columnresize
32397 * Fires when the user resizes a column
32398 * @param {Number} columnIndex
32399 * @param {Number} newSize
32401 "columnresize" : true,
32403 * @event columnmove
32404 * Fires when the user moves a column
32405 * @param {Number} oldIndex
32406 * @param {Number} newIndex
32408 "columnmove" : true,
32411 * Fires when row(s) start being dragged
32412 * @param {Grid} this
32413 * @param {Roo.GridDD} dd The drag drop object
32414 * @param {event} e The raw browser event
32416 "startdrag" : true,
32419 * Fires when a drag operation is complete
32420 * @param {Grid} this
32421 * @param {Roo.GridDD} dd The drag drop object
32422 * @param {event} e The raw browser event
32427 * Fires when dragged row(s) are dropped on a valid DD target
32428 * @param {Grid} this
32429 * @param {Roo.GridDD} dd The drag drop object
32430 * @param {String} targetId The target drag drop object
32431 * @param {event} e The raw browser event
32436 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32437 * @param {Grid} this
32438 * @param {Roo.GridDD} dd The drag drop object
32439 * @param {String} targetId The target drag drop object
32440 * @param {event} e The raw browser event
32445 * Fires when the dragged row(s) first cross another DD target while being dragged
32446 * @param {Grid} this
32447 * @param {Roo.GridDD} dd The drag drop object
32448 * @param {String} targetId The target drag drop object
32449 * @param {event} e The raw browser event
32451 "dragenter" : true,
32454 * Fires when the dragged row(s) leave another DD target while being dragged
32455 * @param {Grid} this
32456 * @param {Roo.GridDD} dd The drag drop object
32457 * @param {String} targetId The target drag drop object
32458 * @param {event} e The raw browser event
32463 * Fires when a row is rendered, so you can change add a style to it.
32464 * @param {GridView} gridview The grid view
32465 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32471 * Fires when the grid is rendered
32472 * @param {Grid} grid
32477 Roo.grid.Grid.superclass.constructor.call(this);
32479 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32482 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32485 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
32488 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32491 * @cfg {Roo.grid.Store} ds The data store for the grid
32494 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32497 * @cfg {String} ddGroup - drag drop group.
32500 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32504 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32506 minColumnWidth : 25,
32509 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32510 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32511 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32513 autoSizeColumns : false,
32516 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32518 autoSizeHeaders : true,
32521 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32523 monitorWindowResize : true,
32526 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32527 * rows measured to get a columns size. Default is 0 (all rows).
32529 maxRowsToMeasure : 0,
32532 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32534 trackMouseOver : true,
32537 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32540 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
32544 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32546 enableDragDrop : false,
32549 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32551 enableColumnMove : true,
32554 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32556 enableColumnHide : true,
32559 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32561 enableRowHeightSync : false,
32564 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32569 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32571 autoHeight : false,
32574 * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
32576 autoExpandColumn : false,
32579 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32582 autoExpandMin : 50,
32585 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32587 autoExpandMax : 1000,
32590 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32595 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32599 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32609 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32610 * of a fixed width. Default is false.
32613 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32618 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32619 * %0 is replaced with the number of selected rows.
32621 ddText : "{0} selected row{1}",
32625 * Called once after all setup has been completed and the grid is ready to be rendered.
32626 * @return {Roo.grid.Grid} this
32628 render : function()
32630 var c = this.container;
32631 // try to detect autoHeight/width mode
32632 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32633 this.autoHeight = true;
32635 var view = this.getView();
32638 c.on("click", this.onClick, this);
32639 c.on("dblclick", this.onDblClick, this);
32640 c.on("contextmenu", this.onContextMenu, this);
32641 c.on("keydown", this.onKeyDown, this);
32643 c.on("touchstart", this.onTouchStart, this);
32646 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32648 this.getSelectionModel().init(this);
32653 this.loadMask = new Roo.LoadMask(this.container,
32654 Roo.apply({store:this.dataSource}, this.loadMask));
32658 if (this.toolbar && this.toolbar.xtype) {
32659 this.toolbar.container = this.getView().getHeaderPanel(true);
32660 this.toolbar = new Roo.Toolbar(this.toolbar);
32662 if (this.footer && this.footer.xtype) {
32663 this.footer.dataSource = this.getDataSource();
32664 this.footer.container = this.getView().getFooterPanel(true);
32665 this.footer = Roo.factory(this.footer, Roo);
32667 if (this.dropTarget && this.dropTarget.xtype) {
32668 delete this.dropTarget.xtype;
32669 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32673 this.rendered = true;
32674 this.fireEvent('render', this);
32679 * Reconfigures the grid to use a different Store and Column Model.
32680 * The View will be bound to the new objects and refreshed.
32681 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32682 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32684 reconfigure : function(dataSource, colModel){
32686 this.loadMask.destroy();
32687 this.loadMask = new Roo.LoadMask(this.container,
32688 Roo.apply({store:dataSource}, this.loadMask));
32690 this.view.bind(dataSource, colModel);
32691 this.dataSource = dataSource;
32692 this.colModel = colModel;
32693 this.view.refresh(true);
32697 * Add's a column, default at the end..
32699 * @param {int} position to add (default end)
32700 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
32702 addColumns : function(pos, ar)
32705 for (var i =0;i< ar.length;i++) {
32707 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32708 this.cm.lookup[cfg.id] = cfg;
32712 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32713 pos = this.cm.config.length; //this.cm.config.push(cfg);
32715 pos = Math.max(0,pos);
32718 this.cm.config.splice.apply(this.cm.config, ar);
32722 this.view.generateRules(this.cm);
32723 this.view.refresh(true);
32731 onKeyDown : function(e){
32732 this.fireEvent("keydown", e);
32736 * Destroy this grid.
32737 * @param {Boolean} removeEl True to remove the element
32739 destroy : function(removeEl, keepListeners){
32741 this.loadMask.destroy();
32743 var c = this.container;
32744 c.removeAllListeners();
32745 this.view.destroy();
32746 this.colModel.purgeListeners();
32747 if(!keepListeners){
32748 this.purgeListeners();
32751 if(removeEl === true){
32757 processEvent : function(name, e){
32758 // does this fire select???
32759 //Roo.log('grid:processEvent ' + name);
32761 if (name != 'touchstart' ) {
32762 this.fireEvent(name, e);
32765 var t = e.getTarget();
32767 var header = v.findHeaderIndex(t);
32768 if(header !== false){
32769 var ename = name == 'touchstart' ? 'click' : name;
32771 this.fireEvent("header" + ename, this, header, e);
32773 var row = v.findRowIndex(t);
32774 var cell = v.findCellIndex(t);
32775 if (name == 'touchstart') {
32776 // first touch is always a click.
32777 // hopefull this happens after selection is updated.?
32780 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32781 var cs = this.selModel.getSelectedCell();
32782 if (row == cs[0] && cell == cs[1]){
32786 if (typeof(this.selModel.getSelections) != 'undefined') {
32787 var cs = this.selModel.getSelections();
32788 var ds = this.dataSource;
32789 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32800 this.fireEvent("row" + name, this, row, e);
32801 if(cell !== false){
32802 this.fireEvent("cell" + name, this, row, cell, e);
32809 onClick : function(e){
32810 this.processEvent("click", e);
32813 onTouchStart : function(e){
32814 this.processEvent("touchstart", e);
32818 onContextMenu : function(e, t){
32819 this.processEvent("contextmenu", e);
32823 onDblClick : function(e){
32824 this.processEvent("dblclick", e);
32828 walkCells : function(row, col, step, fn, scope){
32829 var cm = this.colModel, clen = cm.getColumnCount();
32830 var ds = this.dataSource, rlen = ds.getCount(), first = true;
32842 if(fn.call(scope || this, row, col, cm) === true){
32860 if(fn.call(scope || this, row, col, cm) === true){
32872 getSelections : function(){
32873 return this.selModel.getSelections();
32877 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32878 * but if manual update is required this method will initiate it.
32880 autoSize : function(){
32882 this.view.layout();
32883 if(this.view.adjustForScroll){
32884 this.view.adjustForScroll();
32890 * Returns the grid's underlying element.
32891 * @return {Element} The element
32893 getGridEl : function(){
32894 return this.container;
32897 // private for compatibility, overridden by editor grid
32898 stopEditing : function(){},
32901 * Returns the grid's SelectionModel.
32902 * @return {SelectionModel}
32904 getSelectionModel : function(){
32905 if(!this.selModel){
32906 this.selModel = new Roo.grid.RowSelectionModel();
32908 return this.selModel;
32912 * Returns the grid's DataSource.
32913 * @return {DataSource}
32915 getDataSource : function(){
32916 return this.dataSource;
32920 * Returns the grid's ColumnModel.
32921 * @return {ColumnModel}
32923 getColumnModel : function(){
32924 return this.colModel;
32928 * Returns the grid's GridView object.
32929 * @return {GridView}
32931 getView : function(){
32933 this.view = new Roo.grid.GridView(this.viewConfig);
32934 this.relayEvents(this.view, [
32935 "beforerowremoved", "beforerowsinserted",
32936 "beforerefresh", "rowremoved",
32937 "rowsinserted", "rowupdated" ,"refresh"
32943 * Called to get grid's drag proxy text, by default returns this.ddText.
32944 * Override this to put something different in the dragged text.
32947 getDragDropText : function(){
32948 var count = this.selModel.getCount();
32949 return String.format(this.ddText, count, count == 1 ? '' : 's');
32954 * Ext JS Library 1.1.1
32955 * Copyright(c) 2006-2007, Ext JS, LLC.
32957 * Originally Released Under LGPL - original licence link has changed is not relivant.
32960 * <script type="text/javascript">
32963 * @class Roo.grid.AbstractGridView
32964 * @extends Roo.util.Observable
32966 * Abstract base class for grid Views
32969 Roo.grid.AbstractGridView = function(){
32973 "beforerowremoved" : true,
32974 "beforerowsinserted" : true,
32975 "beforerefresh" : true,
32976 "rowremoved" : true,
32977 "rowsinserted" : true,
32978 "rowupdated" : true,
32981 Roo.grid.AbstractGridView.superclass.constructor.call(this);
32984 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32985 rowClass : "x-grid-row",
32986 cellClass : "x-grid-cell",
32987 tdClass : "x-grid-td",
32988 hdClass : "x-grid-hd",
32989 splitClass : "x-grid-hd-split",
32991 init: function(grid){
32993 var cid = this.grid.getGridEl().id;
32994 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32995 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32996 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32997 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33000 getColumnRenderers : function(){
33001 var renderers = [];
33002 var cm = this.grid.colModel;
33003 var colCount = cm.getColumnCount();
33004 for(var i = 0; i < colCount; i++){
33005 renderers[i] = cm.getRenderer(i);
33010 getColumnIds : function(){
33012 var cm = this.grid.colModel;
33013 var colCount = cm.getColumnCount();
33014 for(var i = 0; i < colCount; i++){
33015 ids[i] = cm.getColumnId(i);
33020 getDataIndexes : function(){
33021 if(!this.indexMap){
33022 this.indexMap = this.buildIndexMap();
33024 return this.indexMap.colToData;
33027 getColumnIndexByDataIndex : function(dataIndex){
33028 if(!this.indexMap){
33029 this.indexMap = this.buildIndexMap();
33031 return this.indexMap.dataToCol[dataIndex];
33035 * Set a css style for a column dynamically.
33036 * @param {Number} colIndex The index of the column
33037 * @param {String} name The css property name
33038 * @param {String} value The css value
33040 setCSSStyle : function(colIndex, name, value){
33041 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33042 Roo.util.CSS.updateRule(selector, name, value);
33045 generateRules : function(cm){
33046 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33047 Roo.util.CSS.removeStyleSheet(rulesId);
33048 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33049 var cid = cm.getColumnId(i);
33050 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33051 this.tdSelector, cid, " {\n}\n",
33052 this.hdSelector, cid, " {\n}\n",
33053 this.splitSelector, cid, " {\n}\n");
33055 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33059 * Ext JS Library 1.1.1
33060 * Copyright(c) 2006-2007, Ext JS, LLC.
33062 * Originally Released Under LGPL - original licence link has changed is not relivant.
33065 * <script type="text/javascript">
33069 // This is a support class used internally by the Grid components
33070 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33072 this.view = grid.getView();
33073 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33074 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33076 this.setHandleElId(Roo.id(hd));
33077 this.setOuterHandleElId(Roo.id(hd2));
33079 this.scroll = false;
33081 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33083 getDragData : function(e){
33084 var t = Roo.lib.Event.getTarget(e);
33085 var h = this.view.findHeaderCell(t);
33087 return {ddel: h.firstChild, header:h};
33092 onInitDrag : function(e){
33093 this.view.headersDisabled = true;
33094 var clone = this.dragData.ddel.cloneNode(true);
33095 clone.id = Roo.id();
33096 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33097 this.proxy.update(clone);
33101 afterValidDrop : function(){
33103 setTimeout(function(){
33104 v.headersDisabled = false;
33108 afterInvalidDrop : function(){
33110 setTimeout(function(){
33111 v.headersDisabled = false;
33117 * Ext JS Library 1.1.1
33118 * Copyright(c) 2006-2007, Ext JS, LLC.
33120 * Originally Released Under LGPL - original licence link has changed is not relivant.
33123 * <script type="text/javascript">
33126 // This is a support class used internally by the Grid components
33127 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33129 this.view = grid.getView();
33130 // split the proxies so they don't interfere with mouse events
33131 this.proxyTop = Roo.DomHelper.append(document.body, {
33132 cls:"col-move-top", html:" "
33134 this.proxyBottom = Roo.DomHelper.append(document.body, {
33135 cls:"col-move-bottom", html:" "
33137 this.proxyTop.hide = this.proxyBottom.hide = function(){
33138 this.setLeftTop(-100,-100);
33139 this.setStyle("visibility", "hidden");
33141 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33142 // temporarily disabled
33143 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33144 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33146 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33147 proxyOffsets : [-4, -9],
33148 fly: Roo.Element.fly,
33150 getTargetFromEvent : function(e){
33151 var t = Roo.lib.Event.getTarget(e);
33152 var cindex = this.view.findCellIndex(t);
33153 if(cindex !== false){
33154 return this.view.getHeaderCell(cindex);
33159 nextVisible : function(h){
33160 var v = this.view, cm = this.grid.colModel;
33163 if(!cm.isHidden(v.getCellIndex(h))){
33171 prevVisible : function(h){
33172 var v = this.view, cm = this.grid.colModel;
33175 if(!cm.isHidden(v.getCellIndex(h))){
33183 positionIndicator : function(h, n, e){
33184 var x = Roo.lib.Event.getPageX(e);
33185 var r = Roo.lib.Dom.getRegion(n.firstChild);
33186 var px, pt, py = r.top + this.proxyOffsets[1];
33187 if((r.right - x) <= (r.right-r.left)/2){
33188 px = r.right+this.view.borderWidth;
33194 var oldIndex = this.view.getCellIndex(h);
33195 var newIndex = this.view.getCellIndex(n);
33197 if(this.grid.colModel.isFixed(newIndex)){
33201 var locked = this.grid.colModel.isLocked(newIndex);
33206 if(oldIndex < newIndex){
33209 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33212 px += this.proxyOffsets[0];
33213 this.proxyTop.setLeftTop(px, py);
33214 this.proxyTop.show();
33215 if(!this.bottomOffset){
33216 this.bottomOffset = this.view.mainHd.getHeight();
33218 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33219 this.proxyBottom.show();
33223 onNodeEnter : function(n, dd, e, data){
33224 if(data.header != n){
33225 this.positionIndicator(data.header, n, e);
33229 onNodeOver : function(n, dd, e, data){
33230 var result = false;
33231 if(data.header != n){
33232 result = this.positionIndicator(data.header, n, e);
33235 this.proxyTop.hide();
33236 this.proxyBottom.hide();
33238 return result ? this.dropAllowed : this.dropNotAllowed;
33241 onNodeOut : function(n, dd, e, data){
33242 this.proxyTop.hide();
33243 this.proxyBottom.hide();
33246 onNodeDrop : function(n, dd, e, data){
33247 var h = data.header;
33249 var cm = this.grid.colModel;
33250 var x = Roo.lib.Event.getPageX(e);
33251 var r = Roo.lib.Dom.getRegion(n.firstChild);
33252 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33253 var oldIndex = this.view.getCellIndex(h);
33254 var newIndex = this.view.getCellIndex(n);
33255 var locked = cm.isLocked(newIndex);
33259 if(oldIndex < newIndex){
33262 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33265 cm.setLocked(oldIndex, locked, true);
33266 cm.moveColumn(oldIndex, newIndex);
33267 this.grid.fireEvent("columnmove", oldIndex, newIndex);
33275 * Ext JS Library 1.1.1
33276 * Copyright(c) 2006-2007, Ext JS, LLC.
33278 * Originally Released Under LGPL - original licence link has changed is not relivant.
33281 * <script type="text/javascript">
33285 * @class Roo.grid.GridView
33286 * @extends Roo.util.Observable
33289 * @param {Object} config
33291 Roo.grid.GridView = function(config){
33292 Roo.grid.GridView.superclass.constructor.call(this);
33295 Roo.apply(this, config);
33298 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33300 unselectable : 'unselectable="on"',
33301 unselectableCls : 'x-unselectable',
33304 rowClass : "x-grid-row",
33306 cellClass : "x-grid-col",
33308 tdClass : "x-grid-td",
33310 hdClass : "x-grid-hd",
33312 splitClass : "x-grid-split",
33314 sortClasses : ["sort-asc", "sort-desc"],
33316 enableMoveAnim : false,
33320 dh : Roo.DomHelper,
33322 fly : Roo.Element.fly,
33324 css : Roo.util.CSS,
33330 scrollIncrement : 22,
33332 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33334 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33336 bind : function(ds, cm){
33338 this.ds.un("load", this.onLoad, this);
33339 this.ds.un("datachanged", this.onDataChange, this);
33340 this.ds.un("add", this.onAdd, this);
33341 this.ds.un("remove", this.onRemove, this);
33342 this.ds.un("update", this.onUpdate, this);
33343 this.ds.un("clear", this.onClear, this);
33346 ds.on("load", this.onLoad, this);
33347 ds.on("datachanged", this.onDataChange, this);
33348 ds.on("add", this.onAdd, this);
33349 ds.on("remove", this.onRemove, this);
33350 ds.on("update", this.onUpdate, this);
33351 ds.on("clear", this.onClear, this);
33356 this.cm.un("widthchange", this.onColWidthChange, this);
33357 this.cm.un("headerchange", this.onHeaderChange, this);
33358 this.cm.un("hiddenchange", this.onHiddenChange, this);
33359 this.cm.un("columnmoved", this.onColumnMove, this);
33360 this.cm.un("columnlockchange", this.onColumnLock, this);
33363 this.generateRules(cm);
33364 cm.on("widthchange", this.onColWidthChange, this);
33365 cm.on("headerchange", this.onHeaderChange, this);
33366 cm.on("hiddenchange", this.onHiddenChange, this);
33367 cm.on("columnmoved", this.onColumnMove, this);
33368 cm.on("columnlockchange", this.onColumnLock, this);
33373 init: function(grid){
33374 Roo.grid.GridView.superclass.init.call(this, grid);
33376 this.bind(grid.dataSource, grid.colModel);
33378 grid.on("headerclick", this.handleHeaderClick, this);
33380 if(grid.trackMouseOver){
33381 grid.on("mouseover", this.onRowOver, this);
33382 grid.on("mouseout", this.onRowOut, this);
33384 grid.cancelTextSelection = function(){};
33385 this.gridId = grid.id;
33387 var tpls = this.templates || {};
33390 tpls.master = new Roo.Template(
33391 '<div class="x-grid" hidefocus="true">',
33392 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33393 '<div class="x-grid-topbar"></div>',
33394 '<div class="x-grid-scroller"><div></div></div>',
33395 '<div class="x-grid-locked">',
33396 '<div class="x-grid-header">{lockedHeader}</div>',
33397 '<div class="x-grid-body">{lockedBody}</div>',
33399 '<div class="x-grid-viewport">',
33400 '<div class="x-grid-header">{header}</div>',
33401 '<div class="x-grid-body">{body}</div>',
33403 '<div class="x-grid-bottombar"></div>',
33405 '<div class="x-grid-resize-proxy"> </div>',
33408 tpls.master.disableformats = true;
33412 tpls.header = new Roo.Template(
33413 '<table border="0" cellspacing="0" cellpadding="0">',
33414 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33417 tpls.header.disableformats = true;
33419 tpls.header.compile();
33422 tpls.hcell = new Roo.Template(
33423 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33424 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33427 tpls.hcell.disableFormats = true;
33429 tpls.hcell.compile();
33432 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33433 this.unselectableCls + '" ' + this.unselectable +'> </div>');
33434 tpls.hsplit.disableFormats = true;
33436 tpls.hsplit.compile();
33439 tpls.body = new Roo.Template(
33440 '<table border="0" cellspacing="0" cellpadding="0">',
33441 "<tbody>{rows}</tbody>",
33444 tpls.body.disableFormats = true;
33446 tpls.body.compile();
33449 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33450 tpls.row.disableFormats = true;
33452 tpls.row.compile();
33455 tpls.cell = new Roo.Template(
33456 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33457 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33458 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33461 tpls.cell.disableFormats = true;
33463 tpls.cell.compile();
33465 this.templates = tpls;
33468 // remap these for backwards compat
33469 onColWidthChange : function(){
33470 this.updateColumns.apply(this, arguments);
33472 onHeaderChange : function(){
33473 this.updateHeaders.apply(this, arguments);
33475 onHiddenChange : function(){
33476 this.handleHiddenChange.apply(this, arguments);
33478 onColumnMove : function(){
33479 this.handleColumnMove.apply(this, arguments);
33481 onColumnLock : function(){
33482 this.handleLockChange.apply(this, arguments);
33485 onDataChange : function(){
33487 this.updateHeaderSortState();
33490 onClear : function(){
33494 onUpdate : function(ds, record){
33495 this.refreshRow(record);
33498 refreshRow : function(record){
33499 var ds = this.ds, index;
33500 if(typeof record == 'number'){
33502 record = ds.getAt(index);
33504 index = ds.indexOf(record);
33506 this.insertRows(ds, index, index, true);
33507 this.onRemove(ds, record, index+1, true);
33508 this.syncRowHeights(index, index);
33510 this.fireEvent("rowupdated", this, index, record);
33513 onAdd : function(ds, records, index){
33514 this.insertRows(ds, index, index + (records.length-1));
33517 onRemove : function(ds, record, index, isUpdate){
33518 if(isUpdate !== true){
33519 this.fireEvent("beforerowremoved", this, index, record);
33521 var bt = this.getBodyTable(), lt = this.getLockedTable();
33522 if(bt.rows[index]){
33523 bt.firstChild.removeChild(bt.rows[index]);
33525 if(lt.rows[index]){
33526 lt.firstChild.removeChild(lt.rows[index]);
33528 if(isUpdate !== true){
33529 this.stripeRows(index);
33530 this.syncRowHeights(index, index);
33532 this.fireEvent("rowremoved", this, index, record);
33536 onLoad : function(){
33537 this.scrollToTop();
33541 * Scrolls the grid to the top
33543 scrollToTop : function(){
33545 this.scroller.dom.scrollTop = 0;
33551 * Gets a panel in the header of the grid that can be used for toolbars etc.
33552 * After modifying the contents of this panel a call to grid.autoSize() may be
33553 * required to register any changes in size.
33554 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33555 * @return Roo.Element
33557 getHeaderPanel : function(doShow){
33559 this.headerPanel.show();
33561 return this.headerPanel;
33565 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33566 * After modifying the contents of this panel a call to grid.autoSize() may be
33567 * required to register any changes in size.
33568 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33569 * @return Roo.Element
33571 getFooterPanel : function(doShow){
33573 this.footerPanel.show();
33575 return this.footerPanel;
33578 initElements : function(){
33579 var E = Roo.Element;
33580 var el = this.grid.getGridEl().dom.firstChild;
33581 var cs = el.childNodes;
33583 this.el = new E(el);
33585 this.focusEl = new E(el.firstChild);
33586 this.focusEl.swallowEvent("click", true);
33588 this.headerPanel = new E(cs[1]);
33589 this.headerPanel.enableDisplayMode("block");
33591 this.scroller = new E(cs[2]);
33592 this.scrollSizer = new E(this.scroller.dom.firstChild);
33594 this.lockedWrap = new E(cs[3]);
33595 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33596 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33598 this.mainWrap = new E(cs[4]);
33599 this.mainHd = new E(this.mainWrap.dom.firstChild);
33600 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33602 this.footerPanel = new E(cs[5]);
33603 this.footerPanel.enableDisplayMode("block");
33605 this.resizeProxy = new E(cs[6]);
33607 this.headerSelector = String.format(
33608 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33609 this.lockedHd.id, this.mainHd.id
33612 this.splitterSelector = String.format(
33613 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33614 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33617 idToCssName : function(s)
33619 return s.replace(/[^a-z0-9]+/ig, '-');
33622 getHeaderCell : function(index){
33623 return Roo.DomQuery.select(this.headerSelector)[index];
33626 getHeaderCellMeasure : function(index){
33627 return this.getHeaderCell(index).firstChild;
33630 getHeaderCellText : function(index){
33631 return this.getHeaderCell(index).firstChild.firstChild;
33634 getLockedTable : function(){
33635 return this.lockedBody.dom.firstChild;
33638 getBodyTable : function(){
33639 return this.mainBody.dom.firstChild;
33642 getLockedRow : function(index){
33643 return this.getLockedTable().rows[index];
33646 getRow : function(index){
33647 return this.getBodyTable().rows[index];
33650 getRowComposite : function(index){
33652 this.rowEl = new Roo.CompositeElementLite();
33654 var els = [], lrow, mrow;
33655 if(lrow = this.getLockedRow(index)){
33658 if(mrow = this.getRow(index)){
33661 this.rowEl.elements = els;
33665 * Gets the 'td' of the cell
33667 * @param {Integer} rowIndex row to select
33668 * @param {Integer} colIndex column to select
33672 getCell : function(rowIndex, colIndex){
33673 var locked = this.cm.getLockedCount();
33675 if(colIndex < locked){
33676 source = this.lockedBody.dom.firstChild;
33678 source = this.mainBody.dom.firstChild;
33679 colIndex -= locked;
33681 return source.rows[rowIndex].childNodes[colIndex];
33684 getCellText : function(rowIndex, colIndex){
33685 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33688 getCellBox : function(cell){
33689 var b = this.fly(cell).getBox();
33690 if(Roo.isOpera){ // opera fails to report the Y
33691 b.y = cell.offsetTop + this.mainBody.getY();
33696 getCellIndex : function(cell){
33697 var id = String(cell.className).match(this.cellRE);
33699 return parseInt(id[1], 10);
33704 findHeaderIndex : function(n){
33705 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33706 return r ? this.getCellIndex(r) : false;
33709 findHeaderCell : function(n){
33710 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33711 return r ? r : false;
33714 findRowIndex : function(n){
33718 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33719 return r ? r.rowIndex : false;
33722 findCellIndex : function(node){
33723 var stop = this.el.dom;
33724 while(node && node != stop){
33725 if(this.findRE.test(node.className)){
33726 return this.getCellIndex(node);
33728 node = node.parentNode;
33733 getColumnId : function(index){
33734 return this.cm.getColumnId(index);
33737 getSplitters : function()
33739 if(this.splitterSelector){
33740 return Roo.DomQuery.select(this.splitterSelector);
33746 getSplitter : function(index){
33747 return this.getSplitters()[index];
33750 onRowOver : function(e, t){
33752 if((row = this.findRowIndex(t)) !== false){
33753 this.getRowComposite(row).addClass("x-grid-row-over");
33757 onRowOut : function(e, t){
33759 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33760 this.getRowComposite(row).removeClass("x-grid-row-over");
33764 renderHeaders : function(){
33766 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33767 var cb = [], lb = [], sb = [], lsb = [], p = {};
33768 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33769 p.cellId = "x-grid-hd-0-" + i;
33770 p.splitId = "x-grid-csplit-0-" + i;
33771 p.id = cm.getColumnId(i);
33772 p.value = cm.getColumnHeader(i) || "";
33773 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33774 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33775 if(!cm.isLocked(i)){
33776 cb[cb.length] = ct.apply(p);
33777 sb[sb.length] = st.apply(p);
33779 lb[lb.length] = ct.apply(p);
33780 lsb[lsb.length] = st.apply(p);
33783 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33784 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33787 updateHeaders : function(){
33788 var html = this.renderHeaders();
33789 this.lockedHd.update(html[0]);
33790 this.mainHd.update(html[1]);
33794 * Focuses the specified row.
33795 * @param {Number} row The row index
33797 focusRow : function(row)
33799 //Roo.log('GridView.focusRow');
33800 var x = this.scroller.dom.scrollLeft;
33801 this.focusCell(row, 0, false);
33802 this.scroller.dom.scrollLeft = x;
33806 * Focuses the specified cell.
33807 * @param {Number} row The row index
33808 * @param {Number} col The column index
33809 * @param {Boolean} hscroll false to disable horizontal scrolling
33811 focusCell : function(row, col, hscroll)
33813 //Roo.log('GridView.focusCell');
33814 var el = this.ensureVisible(row, col, hscroll);
33815 this.focusEl.alignTo(el, "tl-tl");
33817 this.focusEl.focus();
33819 this.focusEl.focus.defer(1, this.focusEl);
33824 * Scrolls the specified cell into view
33825 * @param {Number} row The row index
33826 * @param {Number} col The column index
33827 * @param {Boolean} hscroll false to disable horizontal scrolling
33829 ensureVisible : function(row, col, hscroll)
33831 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33832 //return null; //disable for testing.
33833 if(typeof row != "number"){
33834 row = row.rowIndex;
33836 if(row < 0 && row >= this.ds.getCount()){
33839 col = (col !== undefined ? col : 0);
33840 var cm = this.grid.colModel;
33841 while(cm.isHidden(col)){
33845 var el = this.getCell(row, col);
33849 var c = this.scroller.dom;
33851 var ctop = parseInt(el.offsetTop, 10);
33852 var cleft = parseInt(el.offsetLeft, 10);
33853 var cbot = ctop + el.offsetHeight;
33854 var cright = cleft + el.offsetWidth;
33856 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33857 var stop = parseInt(c.scrollTop, 10);
33858 var sleft = parseInt(c.scrollLeft, 10);
33859 var sbot = stop + ch;
33860 var sright = sleft + c.clientWidth;
33862 Roo.log('GridView.ensureVisible:' +
33864 ' c.clientHeight:' + c.clientHeight +
33865 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33873 c.scrollTop = ctop;
33874 //Roo.log("set scrolltop to ctop DISABLE?");
33875 }else if(cbot > sbot){
33876 //Roo.log("set scrolltop to cbot-ch");
33877 c.scrollTop = cbot-ch;
33880 if(hscroll !== false){
33882 c.scrollLeft = cleft;
33883 }else if(cright > sright){
33884 c.scrollLeft = cright-c.clientWidth;
33891 updateColumns : function(){
33892 this.grid.stopEditing();
33893 var cm = this.grid.colModel, colIds = this.getColumnIds();
33894 //var totalWidth = cm.getTotalWidth();
33896 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33897 //if(cm.isHidden(i)) continue;
33898 var w = cm.getColumnWidth(i);
33899 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33900 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33902 this.updateSplitters();
33905 generateRules : function(cm){
33906 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33907 Roo.util.CSS.removeStyleSheet(rulesId);
33908 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33909 var cid = cm.getColumnId(i);
33911 if(cm.config[i].align){
33912 align = 'text-align:'+cm.config[i].align+';';
33915 if(cm.isHidden(i)){
33916 hidden = 'display:none;';
33918 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33920 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33921 this.hdSelector, cid, " {\n", align, width, "}\n",
33922 this.tdSelector, cid, " {\n",hidden,"\n}\n",
33923 this.splitSelector, cid, " {\n", hidden , "\n}\n");
33925 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33928 updateSplitters : function(){
33929 var cm = this.cm, s = this.getSplitters();
33930 if(s){ // splitters not created yet
33931 var pos = 0, locked = true;
33932 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33933 if(cm.isHidden(i)) {
33936 var w = cm.getColumnWidth(i); // make sure it's a number
33937 if(!cm.isLocked(i) && locked){
33942 s[i].style.left = (pos-this.splitOffset) + "px";
33947 handleHiddenChange : function(colModel, colIndex, hidden){
33949 this.hideColumn(colIndex);
33951 this.unhideColumn(colIndex);
33955 hideColumn : function(colIndex){
33956 var cid = this.getColumnId(colIndex);
33957 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33958 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33960 this.updateHeaders();
33962 this.updateSplitters();
33966 unhideColumn : function(colIndex){
33967 var cid = this.getColumnId(colIndex);
33968 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33969 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33972 this.updateHeaders();
33974 this.updateSplitters();
33978 insertRows : function(dm, firstRow, lastRow, isUpdate){
33979 if(firstRow == 0 && lastRow == dm.getCount()-1){
33983 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33985 var s = this.getScrollState();
33986 var markup = this.renderRows(firstRow, lastRow);
33987 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33988 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33989 this.restoreScroll(s);
33991 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33992 this.syncRowHeights(firstRow, lastRow);
33993 this.stripeRows(firstRow);
33999 bufferRows : function(markup, target, index){
34000 var before = null, trows = target.rows, tbody = target.tBodies[0];
34001 if(index < trows.length){
34002 before = trows[index];
34004 var b = document.createElement("div");
34005 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34006 var rows = b.firstChild.rows;
34007 for(var i = 0, len = rows.length; i < len; i++){
34009 tbody.insertBefore(rows[0], before);
34011 tbody.appendChild(rows[0]);
34018 deleteRows : function(dm, firstRow, lastRow){
34019 if(dm.getRowCount()<1){
34020 this.fireEvent("beforerefresh", this);
34021 this.mainBody.update("");
34022 this.lockedBody.update("");
34023 this.fireEvent("refresh", this);
34025 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34026 var bt = this.getBodyTable();
34027 var tbody = bt.firstChild;
34028 var rows = bt.rows;
34029 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34030 tbody.removeChild(rows[firstRow]);
34032 this.stripeRows(firstRow);
34033 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34037 updateRows : function(dataSource, firstRow, lastRow){
34038 var s = this.getScrollState();
34040 this.restoreScroll(s);
34043 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34047 this.updateHeaderSortState();
34050 getScrollState : function(){
34052 var sb = this.scroller.dom;
34053 return {left: sb.scrollLeft, top: sb.scrollTop};
34056 stripeRows : function(startRow){
34057 if(!this.grid.stripeRows || this.ds.getCount() < 1){
34060 startRow = startRow || 0;
34061 var rows = this.getBodyTable().rows;
34062 var lrows = this.getLockedTable().rows;
34063 var cls = ' x-grid-row-alt ';
34064 for(var i = startRow, len = rows.length; i < len; i++){
34065 var row = rows[i], lrow = lrows[i];
34066 var isAlt = ((i+1) % 2 == 0);
34067 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34068 if(isAlt == hasAlt){
34072 row.className += " x-grid-row-alt";
34074 row.className = row.className.replace("x-grid-row-alt", "");
34077 lrow.className = row.className;
34082 restoreScroll : function(state){
34083 //Roo.log('GridView.restoreScroll');
34084 var sb = this.scroller.dom;
34085 sb.scrollLeft = state.left;
34086 sb.scrollTop = state.top;
34090 syncScroll : function(){
34091 //Roo.log('GridView.syncScroll');
34092 var sb = this.scroller.dom;
34093 var sh = this.mainHd.dom;
34094 var bs = this.mainBody.dom;
34095 var lv = this.lockedBody.dom;
34096 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34097 lv.scrollTop = bs.scrollTop = sb.scrollTop;
34100 handleScroll : function(e){
34102 var sb = this.scroller.dom;
34103 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34107 handleWheel : function(e){
34108 var d = e.getWheelDelta();
34109 this.scroller.dom.scrollTop -= d*22;
34110 // set this here to prevent jumpy scrolling on large tables
34111 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34115 renderRows : function(startRow, endRow){
34116 // pull in all the crap needed to render rows
34117 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34118 var colCount = cm.getColumnCount();
34120 if(ds.getCount() < 1){
34124 // build a map for all the columns
34126 for(var i = 0; i < colCount; i++){
34127 var name = cm.getDataIndex(i);
34129 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34130 renderer : cm.getRenderer(i),
34131 id : cm.getColumnId(i),
34132 locked : cm.isLocked(i),
34133 has_editor : cm.isCellEditable(i)
34137 startRow = startRow || 0;
34138 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34140 // records to render
34141 var rs = ds.getRange(startRow, endRow);
34143 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34146 // As much as I hate to duplicate code, this was branched because FireFox really hates
34147 // [].join("") on strings. The performance difference was substantial enough to
34148 // branch this function
34149 doRender : Roo.isGecko ?
34150 function(cs, rs, ds, startRow, colCount, stripe){
34151 var ts = this.templates, ct = ts.cell, rt = ts.row;
34153 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34155 var hasListener = this.grid.hasListener('rowclass');
34157 for(var j = 0, len = rs.length; j < len; j++){
34158 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34159 for(var i = 0; i < colCount; i++){
34161 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34163 p.css = p.attr = "";
34164 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34165 if(p.value == undefined || p.value === "") {
34166 p.value = " ";
34169 p.css += ' x-grid-editable-cell';
34171 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34172 p.css += ' x-grid-dirty-cell';
34174 var markup = ct.apply(p);
34182 if(stripe && ((rowIndex+1) % 2 == 0)){
34183 alt.push("x-grid-row-alt")
34186 alt.push( " x-grid-dirty-row");
34189 if(this.getRowClass){
34190 alt.push(this.getRowClass(r, rowIndex));
34196 rowIndex : rowIndex,
34199 this.grid.fireEvent('rowclass', this, rowcfg);
34200 alt.push(rowcfg.rowClass);
34202 rp.alt = alt.join(" ");
34203 lbuf+= rt.apply(rp);
34205 buf+= rt.apply(rp);
34207 return [lbuf, buf];
34209 function(cs, rs, ds, startRow, colCount, stripe){
34210 var ts = this.templates, ct = ts.cell, rt = ts.row;
34212 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34213 var hasListener = this.grid.hasListener('rowclass');
34216 for(var j = 0, len = rs.length; j < len; j++){
34217 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34218 for(var i = 0; i < colCount; i++){
34220 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34222 p.css = p.attr = "";
34223 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34224 if(p.value == undefined || p.value === "") {
34225 p.value = " ";
34229 p.css += ' x-grid-editable-cell';
34231 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34232 p.css += ' x-grid-dirty-cell'
34235 var markup = ct.apply(p);
34237 cb[cb.length] = markup;
34239 lcb[lcb.length] = markup;
34243 if(stripe && ((rowIndex+1) % 2 == 0)){
34244 alt.push( "x-grid-row-alt");
34247 alt.push(" x-grid-dirty-row");
34250 if(this.getRowClass){
34251 alt.push( this.getRowClass(r, rowIndex));
34257 rowIndex : rowIndex,
34260 this.grid.fireEvent('rowclass', this, rowcfg);
34261 alt.push(rowcfg.rowClass);
34264 rp.alt = alt.join(" ");
34265 rp.cells = lcb.join("");
34266 lbuf[lbuf.length] = rt.apply(rp);
34267 rp.cells = cb.join("");
34268 buf[buf.length] = rt.apply(rp);
34270 return [lbuf.join(""), buf.join("")];
34273 renderBody : function(){
34274 var markup = this.renderRows();
34275 var bt = this.templates.body;
34276 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34280 * Refreshes the grid
34281 * @param {Boolean} headersToo
34283 refresh : function(headersToo){
34284 this.fireEvent("beforerefresh", this);
34285 this.grid.stopEditing();
34286 var result = this.renderBody();
34287 this.lockedBody.update(result[0]);
34288 this.mainBody.update(result[1]);
34289 if(headersToo === true){
34290 this.updateHeaders();
34291 this.updateColumns();
34292 this.updateSplitters();
34293 this.updateHeaderSortState();
34295 this.syncRowHeights();
34297 this.fireEvent("refresh", this);
34300 handleColumnMove : function(cm, oldIndex, newIndex){
34301 this.indexMap = null;
34302 var s = this.getScrollState();
34303 this.refresh(true);
34304 this.restoreScroll(s);
34305 this.afterMove(newIndex);
34308 afterMove : function(colIndex){
34309 if(this.enableMoveAnim && Roo.enableFx){
34310 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34312 // if multisort - fix sortOrder, and reload..
34313 if (this.grid.dataSource.multiSort) {
34314 // the we can call sort again..
34315 var dm = this.grid.dataSource;
34316 var cm = this.grid.colModel;
34318 for(var i = 0; i < cm.config.length; i++ ) {
34320 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34321 continue; // dont' bother, it's not in sort list or being set.
34324 so.push(cm.config[i].dataIndex);
34327 dm.load(dm.lastOptions);
34334 updateCell : function(dm, rowIndex, dataIndex){
34335 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34336 if(typeof colIndex == "undefined"){ // not present in grid
34339 var cm = this.grid.colModel;
34340 var cell = this.getCell(rowIndex, colIndex);
34341 var cellText = this.getCellText(rowIndex, colIndex);
34344 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34345 id : cm.getColumnId(colIndex),
34346 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34348 var renderer = cm.getRenderer(colIndex);
34349 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34350 if(typeof val == "undefined" || val === "") {
34353 cellText.innerHTML = val;
34354 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34355 this.syncRowHeights(rowIndex, rowIndex);
34358 calcColumnWidth : function(colIndex, maxRowsToMeasure){
34360 if(this.grid.autoSizeHeaders){
34361 var h = this.getHeaderCellMeasure(colIndex);
34362 maxWidth = Math.max(maxWidth, h.scrollWidth);
34365 if(this.cm.isLocked(colIndex)){
34366 tb = this.getLockedTable();
34369 tb = this.getBodyTable();
34370 index = colIndex - this.cm.getLockedCount();
34373 var rows = tb.rows;
34374 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34375 for(var i = 0; i < stopIndex; i++){
34376 var cell = rows[i].childNodes[index].firstChild;
34377 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34380 return maxWidth + /*margin for error in IE*/ 5;
34383 * Autofit a column to its content.
34384 * @param {Number} colIndex
34385 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34387 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34388 if(this.cm.isHidden(colIndex)){
34389 return; // can't calc a hidden column
34392 var cid = this.cm.getColumnId(colIndex);
34393 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34394 if(this.grid.autoSizeHeaders){
34395 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34398 var newWidth = this.calcColumnWidth(colIndex);
34399 this.cm.setColumnWidth(colIndex,
34400 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34401 if(!suppressEvent){
34402 this.grid.fireEvent("columnresize", colIndex, newWidth);
34407 * Autofits all columns to their content and then expands to fit any extra space in the grid
34409 autoSizeColumns : function(){
34410 var cm = this.grid.colModel;
34411 var colCount = cm.getColumnCount();
34412 for(var i = 0; i < colCount; i++){
34413 this.autoSizeColumn(i, true, true);
34415 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34418 this.updateColumns();
34424 * Autofits all columns to the grid's width proportionate with their current size
34425 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34427 fitColumns : function(reserveScrollSpace){
34428 var cm = this.grid.colModel;
34429 var colCount = cm.getColumnCount();
34433 for (i = 0; i < colCount; i++){
34434 if(!cm.isHidden(i) && !cm.isFixed(i)){
34435 w = cm.getColumnWidth(i);
34441 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34442 if(reserveScrollSpace){
34445 var frac = (avail - cm.getTotalWidth())/width;
34446 while (cols.length){
34449 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34451 this.updateColumns();
34455 onRowSelect : function(rowIndex){
34456 var row = this.getRowComposite(rowIndex);
34457 row.addClass("x-grid-row-selected");
34460 onRowDeselect : function(rowIndex){
34461 var row = this.getRowComposite(rowIndex);
34462 row.removeClass("x-grid-row-selected");
34465 onCellSelect : function(row, col){
34466 var cell = this.getCell(row, col);
34468 Roo.fly(cell).addClass("x-grid-cell-selected");
34472 onCellDeselect : function(row, col){
34473 var cell = this.getCell(row, col);
34475 Roo.fly(cell).removeClass("x-grid-cell-selected");
34479 updateHeaderSortState : function(){
34481 // sort state can be single { field: xxx, direction : yyy}
34482 // or { xxx=>ASC , yyy : DESC ..... }
34485 if (!this.ds.multiSort) {
34486 var state = this.ds.getSortState();
34490 mstate[state.field] = state.direction;
34491 // FIXME... - this is not used here.. but might be elsewhere..
34492 this.sortState = state;
34495 mstate = this.ds.sortToggle;
34497 //remove existing sort classes..
34499 var sc = this.sortClasses;
34500 var hds = this.el.select(this.headerSelector).removeClass(sc);
34502 for(var f in mstate) {
34504 var sortColumn = this.cm.findColumnIndex(f);
34506 if(sortColumn != -1){
34507 var sortDir = mstate[f];
34508 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34517 handleHeaderClick : function(g, index,e){
34519 Roo.log("header click");
34522 // touch events on header are handled by context
34523 this.handleHdCtx(g,index,e);
34528 if(this.headersDisabled){
34531 var dm = g.dataSource, cm = g.colModel;
34532 if(!cm.isSortable(index)){
34537 if (dm.multiSort) {
34538 // update the sortOrder
34540 for(var i = 0; i < cm.config.length; i++ ) {
34542 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34543 continue; // dont' bother, it's not in sort list or being set.
34546 so.push(cm.config[i].dataIndex);
34552 dm.sort(cm.getDataIndex(index));
34556 destroy : function(){
34558 this.colMenu.removeAll();
34559 Roo.menu.MenuMgr.unregister(this.colMenu);
34560 this.colMenu.getEl().remove();
34561 delete this.colMenu;
34564 this.hmenu.removeAll();
34565 Roo.menu.MenuMgr.unregister(this.hmenu);
34566 this.hmenu.getEl().remove();
34569 if(this.grid.enableColumnMove){
34570 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34572 for(var dd in dds){
34573 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34574 var elid = dds[dd].dragElId;
34576 Roo.get(elid).remove();
34577 } else if(dds[dd].config.isTarget){
34578 dds[dd].proxyTop.remove();
34579 dds[dd].proxyBottom.remove();
34582 if(Roo.dd.DDM.locationCache[dd]){
34583 delete Roo.dd.DDM.locationCache[dd];
34586 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34589 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34590 this.bind(null, null);
34591 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34594 handleLockChange : function(){
34595 this.refresh(true);
34598 onDenyColumnLock : function(){
34602 onDenyColumnHide : function(){
34606 handleHdMenuClick : function(item){
34607 var index = this.hdCtxIndex;
34608 var cm = this.cm, ds = this.ds;
34611 ds.sort(cm.getDataIndex(index), "ASC");
34614 ds.sort(cm.getDataIndex(index), "DESC");
34617 var lc = cm.getLockedCount();
34618 if(cm.getColumnCount(true) <= lc+1){
34619 this.onDenyColumnLock();
34623 cm.setLocked(index, true, true);
34624 cm.moveColumn(index, lc);
34625 this.grid.fireEvent("columnmove", index, lc);
34627 cm.setLocked(index, true);
34631 var lc = cm.getLockedCount();
34632 if((lc-1) != index){
34633 cm.setLocked(index, false, true);
34634 cm.moveColumn(index, lc-1);
34635 this.grid.fireEvent("columnmove", index, lc-1);
34637 cm.setLocked(index, false);
34640 case 'wider': // used to expand cols on touch..
34642 var cw = cm.getColumnWidth(index);
34643 cw += (item.id == 'wider' ? 1 : -1) * 50;
34644 cw = Math.max(0, cw);
34645 cw = Math.min(cw,4000);
34646 cm.setColumnWidth(index, cw);
34650 index = cm.getIndexById(item.id.substr(4));
34652 if(item.checked && cm.getColumnCount(true) <= 1){
34653 this.onDenyColumnHide();
34656 cm.setHidden(index, item.checked);
34662 beforeColMenuShow : function(){
34663 var cm = this.cm, colCount = cm.getColumnCount();
34664 this.colMenu.removeAll();
34665 for(var i = 0; i < colCount; i++){
34666 this.colMenu.add(new Roo.menu.CheckItem({
34667 id: "col-"+cm.getColumnId(i),
34668 text: cm.getColumnHeader(i),
34669 checked: !cm.isHidden(i),
34675 handleHdCtx : function(g, index, e){
34677 var hd = this.getHeaderCell(index);
34678 this.hdCtxIndex = index;
34679 var ms = this.hmenu.items, cm = this.cm;
34680 ms.get("asc").setDisabled(!cm.isSortable(index));
34681 ms.get("desc").setDisabled(!cm.isSortable(index));
34682 if(this.grid.enableColLock !== false){
34683 ms.get("lock").setDisabled(cm.isLocked(index));
34684 ms.get("unlock").setDisabled(!cm.isLocked(index));
34686 this.hmenu.show(hd, "tl-bl");
34689 handleHdOver : function(e){
34690 var hd = this.findHeaderCell(e.getTarget());
34691 if(hd && !this.headersDisabled){
34692 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34693 this.fly(hd).addClass("x-grid-hd-over");
34698 handleHdOut : function(e){
34699 var hd = this.findHeaderCell(e.getTarget());
34701 this.fly(hd).removeClass("x-grid-hd-over");
34705 handleSplitDblClick : function(e, t){
34706 var i = this.getCellIndex(t);
34707 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34708 this.autoSizeColumn(i, true);
34713 render : function(){
34716 var colCount = cm.getColumnCount();
34718 if(this.grid.monitorWindowResize === true){
34719 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34721 var header = this.renderHeaders();
34722 var body = this.templates.body.apply({rows:""});
34723 var html = this.templates.master.apply({
34726 lockedHeader: header[0],
34730 //this.updateColumns();
34732 this.grid.getGridEl().dom.innerHTML = html;
34734 this.initElements();
34736 // a kludge to fix the random scolling effect in webkit
34737 this.el.on("scroll", function() {
34738 this.el.dom.scrollTop=0; // hopefully not recursive..
34741 this.scroller.on("scroll", this.handleScroll, this);
34742 this.lockedBody.on("mousewheel", this.handleWheel, this);
34743 this.mainBody.on("mousewheel", this.handleWheel, this);
34745 this.mainHd.on("mouseover", this.handleHdOver, this);
34746 this.mainHd.on("mouseout", this.handleHdOut, this);
34747 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34748 {delegate: "."+this.splitClass});
34750 this.lockedHd.on("mouseover", this.handleHdOver, this);
34751 this.lockedHd.on("mouseout", this.handleHdOut, this);
34752 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34753 {delegate: "."+this.splitClass});
34755 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34756 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34759 this.updateSplitters();
34761 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34762 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34763 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34766 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34767 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34769 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34770 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34772 if(this.grid.enableColLock !== false){
34773 this.hmenu.add('-',
34774 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34775 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34779 this.hmenu.add('-',
34780 {id:"wider", text: this.columnsWiderText},
34781 {id:"narrow", text: this.columnsNarrowText }
34787 if(this.grid.enableColumnHide !== false){
34789 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34790 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34791 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34793 this.hmenu.add('-',
34794 {id:"columns", text: this.columnsText, menu: this.colMenu}
34797 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34799 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34802 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34803 this.dd = new Roo.grid.GridDragZone(this.grid, {
34804 ddGroup : this.grid.ddGroup || 'GridDD'
34810 for(var i = 0; i < colCount; i++){
34811 if(cm.isHidden(i)){
34812 this.hideColumn(i);
34814 if(cm.config[i].align){
34815 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34816 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34820 this.updateHeaderSortState();
34822 this.beforeInitialResize();
34825 // two part rendering gives faster view to the user
34826 this.renderPhase2.defer(1, this);
34829 renderPhase2 : function(){
34830 // render the rows now
34832 if(this.grid.autoSizeColumns){
34833 this.autoSizeColumns();
34837 beforeInitialResize : function(){
34841 onColumnSplitterMoved : function(i, w){
34842 this.userResized = true;
34843 var cm = this.grid.colModel;
34844 cm.setColumnWidth(i, w, true);
34845 var cid = cm.getColumnId(i);
34846 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34847 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34848 this.updateSplitters();
34850 this.grid.fireEvent("columnresize", i, w);
34853 syncRowHeights : function(startIndex, endIndex){
34854 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34855 startIndex = startIndex || 0;
34856 var mrows = this.getBodyTable().rows;
34857 var lrows = this.getLockedTable().rows;
34858 var len = mrows.length-1;
34859 endIndex = Math.min(endIndex || len, len);
34860 for(var i = startIndex; i <= endIndex; i++){
34861 var m = mrows[i], l = lrows[i];
34862 var h = Math.max(m.offsetHeight, l.offsetHeight);
34863 m.style.height = l.style.height = h + "px";
34868 layout : function(initialRender, is2ndPass)
34871 var auto = g.autoHeight;
34872 var scrollOffset = 16;
34873 var c = g.getGridEl(), cm = this.cm,
34874 expandCol = g.autoExpandColumn,
34876 //c.beginMeasure();
34878 if(!c.dom.offsetWidth){ // display:none?
34880 this.lockedWrap.show();
34881 this.mainWrap.show();
34886 var hasLock = this.cm.isLocked(0);
34888 var tbh = this.headerPanel.getHeight();
34889 var bbh = this.footerPanel.getHeight();
34892 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34893 var newHeight = ch + c.getBorderWidth("tb");
34895 newHeight = Math.min(g.maxHeight, newHeight);
34897 c.setHeight(newHeight);
34901 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34904 var s = this.scroller;
34906 var csize = c.getSize(true);
34908 this.el.setSize(csize.width, csize.height);
34910 this.headerPanel.setWidth(csize.width);
34911 this.footerPanel.setWidth(csize.width);
34913 var hdHeight = this.mainHd.getHeight();
34914 var vw = csize.width;
34915 var vh = csize.height - (tbh + bbh);
34919 var bt = this.getBodyTable();
34921 if(cm.getLockedCount() == cm.config.length){
34922 bt = this.getLockedTable();
34925 var ltWidth = hasLock ?
34926 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34928 var scrollHeight = bt.offsetHeight;
34929 var scrollWidth = ltWidth + bt.offsetWidth;
34930 var vscroll = false, hscroll = false;
34932 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34934 var lw = this.lockedWrap, mw = this.mainWrap;
34935 var lb = this.lockedBody, mb = this.mainBody;
34937 setTimeout(function(){
34938 var t = s.dom.offsetTop;
34939 var w = s.dom.clientWidth,
34940 h = s.dom.clientHeight;
34943 lw.setSize(ltWidth, h);
34945 mw.setLeftTop(ltWidth, t);
34946 mw.setSize(w-ltWidth, h);
34948 lb.setHeight(h-hdHeight);
34949 mb.setHeight(h-hdHeight);
34951 if(is2ndPass !== true && !gv.userResized && expandCol){
34952 // high speed resize without full column calculation
34954 var ci = cm.getIndexById(expandCol);
34956 ci = cm.findColumnIndex(expandCol);
34958 ci = Math.max(0, ci); // make sure it's got at least the first col.
34959 var expandId = cm.getColumnId(ci);
34960 var tw = cm.getTotalWidth(false);
34961 var currentWidth = cm.getColumnWidth(ci);
34962 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34963 if(currentWidth != cw){
34964 cm.setColumnWidth(ci, cw, true);
34965 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34966 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34967 gv.updateSplitters();
34968 gv.layout(false, true);
34980 onWindowResize : function(){
34981 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34987 appendFooter : function(parentEl){
34991 sortAscText : "Sort Ascending",
34992 sortDescText : "Sort Descending",
34993 lockText : "Lock Column",
34994 unlockText : "Unlock Column",
34995 columnsText : "Columns",
34997 columnsWiderText : "Wider",
34998 columnsNarrowText : "Thinner"
35002 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35003 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35004 this.proxy.el.addClass('x-grid3-col-dd');
35007 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35008 handleMouseDown : function(e){
35012 callHandleMouseDown : function(e){
35013 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35018 * Ext JS Library 1.1.1
35019 * Copyright(c) 2006-2007, Ext JS, LLC.
35021 * Originally Released Under LGPL - original licence link has changed is not relivant.
35024 * <script type="text/javascript">
35027 * @extends Roo.dd.DDProxy
35028 * @class Roo.grid.SplitDragZone
35029 * Support for Column Header resizing
35031 * @param {Object} config
35034 // This is a support class used internally by the Grid components
35035 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35037 this.view = grid.getView();
35038 this.proxy = this.view.resizeProxy;
35039 Roo.grid.SplitDragZone.superclass.constructor.call(
35042 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35044 dragElId : Roo.id(this.proxy.dom),
35049 this.setHandleElId(Roo.id(hd));
35050 if (hd2 !== false) {
35051 this.setOuterHandleElId(Roo.id(hd2));
35054 this.scroll = false;
35056 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35057 fly: Roo.Element.fly,
35059 b4StartDrag : function(x, y){
35060 this.view.headersDisabled = true;
35061 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35062 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35064 this.proxy.setHeight(h);
35066 // for old system colWidth really stored the actual width?
35067 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35068 // which in reality did not work.. - it worked only for fixed sizes
35069 // for resizable we need to use actual sizes.
35070 var w = this.cm.getColumnWidth(this.cellIndex);
35071 if (!this.view.mainWrap) {
35073 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35078 // this was w-this.grid.minColumnWidth;
35079 // doesnt really make sense? - w = thie curren width or the rendered one?
35080 var minw = Math.max(w-this.grid.minColumnWidth, 0);
35081 this.resetConstraints();
35082 this.setXConstraint(minw, 1000);
35083 this.setYConstraint(0, 0);
35084 this.minX = x - minw;
35085 this.maxX = x + 1000;
35087 if (!this.view.mainWrap) { // this is Bootstrap code..
35088 this.getDragEl().style.display='block';
35091 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35095 handleMouseDown : function(e){
35096 ev = Roo.EventObject.setEvent(e);
35097 var t = this.fly(ev.getTarget());
35098 if(t.hasClass("x-grid-split")){
35099 this.cellIndex = this.view.getCellIndex(t.dom);
35100 this.split = t.dom;
35101 this.cm = this.grid.colModel;
35102 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35103 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35108 endDrag : function(e){
35109 this.view.headersDisabled = false;
35110 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35111 var diff = endX - this.startPos;
35113 var w = this.cm.getColumnWidth(this.cellIndex);
35114 if (!this.view.mainWrap) {
35117 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35120 autoOffset : function(){
35121 this.setDelta(0,0);
35125 * Ext JS Library 1.1.1
35126 * Copyright(c) 2006-2007, Ext JS, LLC.
35128 * Originally Released Under LGPL - original licence link has changed is not relivant.
35131 * <script type="text/javascript">
35135 // This is a support class used internally by the Grid components
35136 Roo.grid.GridDragZone = function(grid, config){
35137 this.view = grid.getView();
35138 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35139 if(this.view.lockedBody){
35140 this.setHandleElId(Roo.id(this.view.mainBody.dom));
35141 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35143 this.scroll = false;
35145 this.ddel = document.createElement('div');
35146 this.ddel.className = 'x-grid-dd-wrap';
35149 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35150 ddGroup : "GridDD",
35152 getDragData : function(e){
35153 var t = Roo.lib.Event.getTarget(e);
35154 var rowIndex = this.view.findRowIndex(t);
35155 var sm = this.grid.selModel;
35157 //Roo.log(rowIndex);
35159 if (sm.getSelectedCell) {
35160 // cell selection..
35161 if (!sm.getSelectedCell()) {
35164 if (rowIndex != sm.getSelectedCell()[0]) {
35169 if (sm.getSelections && sm.getSelections().length < 1) {
35174 // before it used to all dragging of unseleted... - now we dont do that.
35175 if(rowIndex !== false){
35180 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35182 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35185 if (e.hasModifier()){
35186 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35189 Roo.log("getDragData");
35194 rowIndex: rowIndex,
35195 selections: sm.getSelections ? sm.getSelections() : (
35196 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35203 onInitDrag : function(e){
35204 var data = this.dragData;
35205 this.ddel.innerHTML = this.grid.getDragDropText();
35206 this.proxy.update(this.ddel);
35207 // fire start drag?
35210 afterRepair : function(){
35211 this.dragging = false;
35214 getRepairXY : function(e, data){
35218 onEndDrag : function(data, e){
35222 onValidDrop : function(dd, e, id){
35227 beforeInvalidDrop : function(e, id){
35232 * Ext JS Library 1.1.1
35233 * Copyright(c) 2006-2007, Ext JS, LLC.
35235 * Originally Released Under LGPL - original licence link has changed is not relivant.
35238 * <script type="text/javascript">
35243 * @class Roo.grid.ColumnModel
35244 * @extends Roo.util.Observable
35245 * This is the default implementation of a ColumnModel used by the Grid. It defines
35246 * the columns in the grid.
35249 var colModel = new Roo.grid.ColumnModel([
35250 {header: "Ticker", width: 60, sortable: true, locked: true},
35251 {header: "Company Name", width: 150, sortable: true},
35252 {header: "Market Cap.", width: 100, sortable: true},
35253 {header: "$ Sales", width: 100, sortable: true, renderer: money},
35254 {header: "Employees", width: 100, sortable: true, resizable: false}
35259 * The config options listed for this class are options which may appear in each
35260 * individual column definition.
35261 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35263 * @param {Object} config An Array of column config objects. See this class's
35264 * config objects for details.
35266 Roo.grid.ColumnModel = function(config){
35268 * The config passed into the constructor
35270 this.config = []; //config;
35273 // if no id, create one
35274 // if the column does not have a dataIndex mapping,
35275 // map it to the order it is in the config
35276 for(var i = 0, len = config.length; i < len; i++){
35277 this.addColumn(config[i]);
35282 * The width of columns which have no width specified (defaults to 100)
35285 this.defaultWidth = 100;
35288 * Default sortable of columns which have no sortable specified (defaults to false)
35291 this.defaultSortable = false;
35295 * @event widthchange
35296 * Fires when the width of a column changes.
35297 * @param {ColumnModel} this
35298 * @param {Number} columnIndex The column index
35299 * @param {Number} newWidth The new width
35301 "widthchange": true,
35303 * @event headerchange
35304 * Fires when the text of a header changes.
35305 * @param {ColumnModel} this
35306 * @param {Number} columnIndex The column index
35307 * @param {Number} newText The new header text
35309 "headerchange": true,
35311 * @event hiddenchange
35312 * Fires when a column is hidden or "unhidden".
35313 * @param {ColumnModel} this
35314 * @param {Number} columnIndex The column index
35315 * @param {Boolean} hidden true if hidden, false otherwise
35317 "hiddenchange": true,
35319 * @event columnmoved
35320 * Fires when a column is moved.
35321 * @param {ColumnModel} this
35322 * @param {Number} oldIndex
35323 * @param {Number} newIndex
35325 "columnmoved" : true,
35327 * @event columlockchange
35328 * Fires when a column's locked state is changed
35329 * @param {ColumnModel} this
35330 * @param {Number} colIndex
35331 * @param {Boolean} locked true if locked
35333 "columnlockchange" : true
35335 Roo.grid.ColumnModel.superclass.constructor.call(this);
35337 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35339 * @cfg {String} header The header text to display in the Grid view.
35342 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35345 * @cfg {String} smHeader Header at Bootsrap Small width
35348 * @cfg {String} mdHeader Header at Bootsrap Medium width
35351 * @cfg {String} lgHeader Header at Bootsrap Large width
35354 * @cfg {String} xlHeader Header at Bootsrap extra Large width
35357 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35358 * {@link Roo.data.Record} definition from which to draw the column's value. If not
35359 * specified, the column's index is used as an index into the Record's data Array.
35362 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35363 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35366 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35367 * Defaults to the value of the {@link #defaultSortable} property.
35368 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35371 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
35374 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
35377 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35380 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35383 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35384 * given the cell's data value. See {@link #setRenderer}. If not specified, the
35385 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35386 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35389 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
35392 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
35395 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
35398 * @cfg {String} cursor (Optional)
35401 * @cfg {String} tooltip (Optional)
35404 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35407 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35410 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35413 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35416 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35419 * Returns the id of the column at the specified index.
35420 * @param {Number} index The column index
35421 * @return {String} the id
35423 getColumnId : function(index){
35424 return this.config[index].id;
35428 * Returns the column for a specified id.
35429 * @param {String} id The column id
35430 * @return {Object} the column
35432 getColumnById : function(id){
35433 return this.lookup[id];
35438 * Returns the column Object for a specified dataIndex.
35439 * @param {String} dataIndex The column dataIndex
35440 * @return {Object|Boolean} the column or false if not found
35442 getColumnByDataIndex: function(dataIndex){
35443 var index = this.findColumnIndex(dataIndex);
35444 return index > -1 ? this.config[index] : false;
35448 * Returns the index for a specified column id.
35449 * @param {String} id The column id
35450 * @return {Number} the index, or -1 if not found
35452 getIndexById : function(id){
35453 for(var i = 0, len = this.config.length; i < len; i++){
35454 if(this.config[i].id == id){
35462 * Returns the index for a specified column dataIndex.
35463 * @param {String} dataIndex The column dataIndex
35464 * @return {Number} the index, or -1 if not found
35467 findColumnIndex : function(dataIndex){
35468 for(var i = 0, len = this.config.length; i < len; i++){
35469 if(this.config[i].dataIndex == dataIndex){
35477 moveColumn : function(oldIndex, newIndex){
35478 var c = this.config[oldIndex];
35479 this.config.splice(oldIndex, 1);
35480 this.config.splice(newIndex, 0, c);
35481 this.dataMap = null;
35482 this.fireEvent("columnmoved", this, oldIndex, newIndex);
35485 isLocked : function(colIndex){
35486 return this.config[colIndex].locked === true;
35489 setLocked : function(colIndex, value, suppressEvent){
35490 if(this.isLocked(colIndex) == value){
35493 this.config[colIndex].locked = value;
35494 if(!suppressEvent){
35495 this.fireEvent("columnlockchange", this, colIndex, value);
35499 getTotalLockedWidth : function(){
35500 var totalWidth = 0;
35501 for(var i = 0; i < this.config.length; i++){
35502 if(this.isLocked(i) && !this.isHidden(i)){
35503 this.totalWidth += this.getColumnWidth(i);
35509 getLockedCount : function(){
35510 for(var i = 0, len = this.config.length; i < len; i++){
35511 if(!this.isLocked(i)){
35516 return this.config.length;
35520 * Returns the number of columns.
35523 getColumnCount : function(visibleOnly){
35524 if(visibleOnly === true){
35526 for(var i = 0, len = this.config.length; i < len; i++){
35527 if(!this.isHidden(i)){
35533 return this.config.length;
35537 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35538 * @param {Function} fn
35539 * @param {Object} scope (optional)
35540 * @return {Array} result
35542 getColumnsBy : function(fn, scope){
35544 for(var i = 0, len = this.config.length; i < len; i++){
35545 var c = this.config[i];
35546 if(fn.call(scope||this, c, i) === true){
35554 * Returns true if the specified column is sortable.
35555 * @param {Number} col The column index
35556 * @return {Boolean}
35558 isSortable : function(col){
35559 if(typeof this.config[col].sortable == "undefined"){
35560 return this.defaultSortable;
35562 return this.config[col].sortable;
35566 * Returns the rendering (formatting) function defined for the column.
35567 * @param {Number} col The column index.
35568 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35570 getRenderer : function(col){
35571 if(!this.config[col].renderer){
35572 return Roo.grid.ColumnModel.defaultRenderer;
35574 return this.config[col].renderer;
35578 * Sets the rendering (formatting) function for a column.
35579 * @param {Number} col The column index
35580 * @param {Function} fn The function to use to process the cell's raw data
35581 * to return HTML markup for the grid view. The render function is called with
35582 * the following parameters:<ul>
35583 * <li>Data value.</li>
35584 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35585 * <li>css A CSS style string to apply to the table cell.</li>
35586 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35587 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35588 * <li>Row index</li>
35589 * <li>Column index</li>
35590 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35592 setRenderer : function(col, fn){
35593 this.config[col].renderer = fn;
35597 * Returns the width for the specified column.
35598 * @param {Number} col The column index
35599 * @param (optional) {String} gridSize bootstrap width size.
35602 getColumnWidth : function(col, gridSize)
35604 var cfg = this.config[col];
35606 if (typeof(gridSize) == 'undefined') {
35607 return cfg.width * 1 || this.defaultWidth;
35609 if (gridSize === false) { // if we set it..
35610 return cfg.width || false;
35612 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35614 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35615 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35618 return cfg[ sizes[i] ];
35625 * Sets the width for a column.
35626 * @param {Number} col The column index
35627 * @param {Number} width The new width
35629 setColumnWidth : function(col, width, suppressEvent){
35630 this.config[col].width = width;
35631 this.totalWidth = null;
35632 if(!suppressEvent){
35633 this.fireEvent("widthchange", this, col, width);
35638 * Returns the total width of all columns.
35639 * @param {Boolean} includeHidden True to include hidden column widths
35642 getTotalWidth : function(includeHidden){
35643 if(!this.totalWidth){
35644 this.totalWidth = 0;
35645 for(var i = 0, len = this.config.length; i < len; i++){
35646 if(includeHidden || !this.isHidden(i)){
35647 this.totalWidth += this.getColumnWidth(i);
35651 return this.totalWidth;
35655 * Returns the header for the specified column.
35656 * @param {Number} col The column index
35659 getColumnHeader : function(col){
35660 return this.config[col].header;
35664 * Sets the header for a column.
35665 * @param {Number} col The column index
35666 * @param {String} header The new header
35668 setColumnHeader : function(col, header){
35669 this.config[col].header = header;
35670 this.fireEvent("headerchange", this, col, header);
35674 * Returns the tooltip for the specified column.
35675 * @param {Number} col The column index
35678 getColumnTooltip : function(col){
35679 return this.config[col].tooltip;
35682 * Sets the tooltip for a column.
35683 * @param {Number} col The column index
35684 * @param {String} tooltip The new tooltip
35686 setColumnTooltip : function(col, tooltip){
35687 this.config[col].tooltip = tooltip;
35691 * Returns the dataIndex for the specified column.
35692 * @param {Number} col The column index
35695 getDataIndex : function(col){
35696 return this.config[col].dataIndex;
35700 * Sets the dataIndex for a column.
35701 * @param {Number} col The column index
35702 * @param {Number} dataIndex The new dataIndex
35704 setDataIndex : function(col, dataIndex){
35705 this.config[col].dataIndex = dataIndex;
35711 * Returns true if the cell is editable.
35712 * @param {Number} colIndex The column index
35713 * @param {Number} rowIndex The row index - this is nto actually used..?
35714 * @return {Boolean}
35716 isCellEditable : function(colIndex, rowIndex){
35717 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35721 * Returns the editor defined for the cell/column.
35722 * return false or null to disable editing.
35723 * @param {Number} colIndex The column index
35724 * @param {Number} rowIndex The row index
35727 getCellEditor : function(colIndex, rowIndex){
35728 return this.config[colIndex].editor;
35732 * Sets if a column is editable.
35733 * @param {Number} col The column index
35734 * @param {Boolean} editable True if the column is editable
35736 setEditable : function(col, editable){
35737 this.config[col].editable = editable;
35742 * Returns true if the column is hidden.
35743 * @param {Number} colIndex The column index
35744 * @return {Boolean}
35746 isHidden : function(colIndex){
35747 return this.config[colIndex].hidden;
35752 * Returns true if the column width cannot be changed
35754 isFixed : function(colIndex){
35755 return this.config[colIndex].fixed;
35759 * Returns true if the column can be resized
35760 * @return {Boolean}
35762 isResizable : function(colIndex){
35763 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35766 * Sets if a column is hidden.
35767 * @param {Number} colIndex The column index
35768 * @param {Boolean} hidden True if the column is hidden
35770 setHidden : function(colIndex, hidden){
35771 this.config[colIndex].hidden = hidden;
35772 this.totalWidth = null;
35773 this.fireEvent("hiddenchange", this, colIndex, hidden);
35777 * Sets the editor for a column.
35778 * @param {Number} col The column index
35779 * @param {Object} editor The editor object
35781 setEditor : function(col, editor){
35782 this.config[col].editor = editor;
35785 * Add a column (experimental...) - defaults to adding to the end..
35786 * @param {Object} config
35788 addColumn : function(c)
35791 var i = this.config.length;
35792 this.config[i] = c;
35794 if(typeof c.dataIndex == "undefined"){
35797 if(typeof c.renderer == "string"){
35798 c.renderer = Roo.util.Format[c.renderer];
35800 if(typeof c.id == "undefined"){
35803 if(c.editor && c.editor.xtype){
35804 c.editor = Roo.factory(c.editor, Roo.grid);
35806 if(c.editor && c.editor.isFormField){
35807 c.editor = new Roo.grid.GridEditor(c.editor);
35809 this.lookup[c.id] = c;
35814 Roo.grid.ColumnModel.defaultRenderer = function(value)
35816 if(typeof value == "object") {
35819 if(typeof value == "string" && value.length < 1){
35823 return String.format("{0}", value);
35826 // Alias for backwards compatibility
35827 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35830 * Ext JS Library 1.1.1
35831 * Copyright(c) 2006-2007, Ext JS, LLC.
35833 * Originally Released Under LGPL - original licence link has changed is not relivant.
35836 * <script type="text/javascript">
35840 * @class Roo.grid.AbstractSelectionModel
35841 * @extends Roo.util.Observable
35843 * Abstract base class for grid SelectionModels. It provides the interface that should be
35844 * implemented by descendant classes. This class should not be directly instantiated.
35847 Roo.grid.AbstractSelectionModel = function(){
35848 this.locked = false;
35849 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35852 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35853 /** @ignore Called by the grid automatically. Do not call directly. */
35854 init : function(grid){
35860 * Locks the selections.
35863 this.locked = true;
35867 * Unlocks the selections.
35869 unlock : function(){
35870 this.locked = false;
35874 * Returns true if the selections are locked.
35875 * @return {Boolean}
35877 isLocked : function(){
35878 return this.locked;
35882 * Ext JS Library 1.1.1
35883 * Copyright(c) 2006-2007, Ext JS, LLC.
35885 * Originally Released Under LGPL - original licence link has changed is not relivant.
35888 * <script type="text/javascript">
35891 * @extends Roo.grid.AbstractSelectionModel
35892 * @class Roo.grid.RowSelectionModel
35893 * The default SelectionModel used by {@link Roo.grid.Grid}.
35894 * It supports multiple selections and keyboard selection/navigation.
35896 * @param {Object} config
35898 Roo.grid.RowSelectionModel = function(config){
35899 Roo.apply(this, config);
35900 this.selections = new Roo.util.MixedCollection(false, function(o){
35905 this.lastActive = false;
35909 * @event selectionchange
35910 * Fires when the selection changes
35911 * @param {SelectionModel} this
35913 "selectionchange" : true,
35915 * @event afterselectionchange
35916 * Fires after the selection changes (eg. by key press or clicking)
35917 * @param {SelectionModel} this
35919 "afterselectionchange" : true,
35921 * @event beforerowselect
35922 * Fires when a row is selected being selected, return false to cancel.
35923 * @param {SelectionModel} this
35924 * @param {Number} rowIndex The selected index
35925 * @param {Boolean} keepExisting False if other selections will be cleared
35927 "beforerowselect" : true,
35930 * Fires when a row is selected.
35931 * @param {SelectionModel} this
35932 * @param {Number} rowIndex The selected index
35933 * @param {Roo.data.Record} r The record
35935 "rowselect" : true,
35937 * @event rowdeselect
35938 * Fires when a row is deselected.
35939 * @param {SelectionModel} this
35940 * @param {Number} rowIndex The selected index
35942 "rowdeselect" : true
35944 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35945 this.locked = false;
35948 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
35950 * @cfg {Boolean} singleSelect
35951 * True to allow selection of only one row at a time (defaults to false)
35953 singleSelect : false,
35956 initEvents : function(){
35958 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35959 this.grid.on("mousedown", this.handleMouseDown, this);
35960 }else{ // allow click to work like normal
35961 this.grid.on("rowclick", this.handleDragableRowClick, this);
35963 // bootstrap does not have a view..
35964 var view = this.grid.view ? this.grid.view : this.grid;
35965 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35966 "up" : function(e){
35968 this.selectPrevious(e.shiftKey);
35969 }else if(this.last !== false && this.lastActive !== false){
35970 var last = this.last;
35971 this.selectRange(this.last, this.lastActive-1);
35972 view.focusRow(this.lastActive);
35973 if(last !== false){
35977 this.selectFirstRow();
35979 this.fireEvent("afterselectionchange", this);
35981 "down" : function(e){
35983 this.selectNext(e.shiftKey);
35984 }else if(this.last !== false && this.lastActive !== false){
35985 var last = this.last;
35986 this.selectRange(this.last, this.lastActive+1);
35987 view.focusRow(this.lastActive);
35988 if(last !== false){
35992 this.selectFirstRow();
35994 this.fireEvent("afterselectionchange", this);
36000 view.on("refresh", this.onRefresh, this);
36001 view.on("rowupdated", this.onRowUpdated, this);
36002 view.on("rowremoved", this.onRemove, this);
36006 onRefresh : function(){
36007 var ds = this.grid.ds, i, v = this.grid.view;
36008 var s = this.selections;
36009 s.each(function(r){
36010 if((i = ds.indexOfId(r.id)) != -1){
36012 s.add(ds.getAt(i)); // updating the selection relate data
36020 onRemove : function(v, index, r){
36021 this.selections.remove(r);
36025 onRowUpdated : function(v, index, r){
36026 if(this.isSelected(r)){
36027 v.onRowSelect(index);
36033 * @param {Array} records The records to select
36034 * @param {Boolean} keepExisting (optional) True to keep existing selections
36036 selectRecords : function(records, keepExisting){
36038 this.clearSelections();
36040 var ds = this.grid.ds;
36041 for(var i = 0, len = records.length; i < len; i++){
36042 this.selectRow(ds.indexOf(records[i]), true);
36047 * Gets the number of selected rows.
36050 getCount : function(){
36051 return this.selections.length;
36055 * Selects the first row in the grid.
36057 selectFirstRow : function(){
36062 * Select the last row.
36063 * @param {Boolean} keepExisting (optional) True to keep existing selections
36065 selectLastRow : function(keepExisting){
36066 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36070 * Selects the row immediately following the last selected row.
36071 * @param {Boolean} keepExisting (optional) True to keep existing selections
36073 selectNext : function(keepExisting){
36074 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36075 this.selectRow(this.last+1, keepExisting);
36076 var view = this.grid.view ? this.grid.view : this.grid;
36077 view.focusRow(this.last);
36082 * Selects the row that precedes the last selected row.
36083 * @param {Boolean} keepExisting (optional) True to keep existing selections
36085 selectPrevious : function(keepExisting){
36087 this.selectRow(this.last-1, keepExisting);
36088 var view = this.grid.view ? this.grid.view : this.grid;
36089 view.focusRow(this.last);
36094 * Returns the selected records
36095 * @return {Array} Array of selected records
36097 getSelections : function(){
36098 return [].concat(this.selections.items);
36102 * Returns the first selected record.
36105 getSelected : function(){
36106 return this.selections.itemAt(0);
36111 * Clears all selections.
36113 clearSelections : function(fast){
36118 var ds = this.grid.ds;
36119 var s = this.selections;
36120 s.each(function(r){
36121 this.deselectRow(ds.indexOfId(r.id));
36125 this.selections.clear();
36132 * Selects all rows.
36134 selectAll : function(){
36138 this.selections.clear();
36139 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36140 this.selectRow(i, true);
36145 * Returns True if there is a selection.
36146 * @return {Boolean}
36148 hasSelection : function(){
36149 return this.selections.length > 0;
36153 * Returns True if the specified row is selected.
36154 * @param {Number/Record} record The record or index of the record to check
36155 * @return {Boolean}
36157 isSelected : function(index){
36158 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36159 return (r && this.selections.key(r.id) ? true : false);
36163 * Returns True if the specified record id is selected.
36164 * @param {String} id The id of record to check
36165 * @return {Boolean}
36167 isIdSelected : function(id){
36168 return (this.selections.key(id) ? true : false);
36172 handleMouseDown : function(e, t)
36174 var view = this.grid.view ? this.grid.view : this.grid;
36176 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36179 if(e.shiftKey && this.last !== false){
36180 var last = this.last;
36181 this.selectRange(last, rowIndex, e.ctrlKey);
36182 this.last = last; // reset the last
36183 view.focusRow(rowIndex);
36185 var isSelected = this.isSelected(rowIndex);
36186 if(e.button !== 0 && isSelected){
36187 view.focusRow(rowIndex);
36188 }else if(e.ctrlKey && isSelected){
36189 this.deselectRow(rowIndex);
36190 }else if(!isSelected){
36191 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36192 view.focusRow(rowIndex);
36195 this.fireEvent("afterselectionchange", this);
36198 handleDragableRowClick : function(grid, rowIndex, e)
36200 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36201 this.selectRow(rowIndex, false);
36202 var view = this.grid.view ? this.grid.view : this.grid;
36203 view.focusRow(rowIndex);
36204 this.fireEvent("afterselectionchange", this);
36209 * Selects multiple rows.
36210 * @param {Array} rows Array of the indexes of the row to select
36211 * @param {Boolean} keepExisting (optional) True to keep existing selections
36213 selectRows : function(rows, keepExisting){
36215 this.clearSelections();
36217 for(var i = 0, len = rows.length; i < len; i++){
36218 this.selectRow(rows[i], true);
36223 * Selects a range of rows. All rows in between startRow and endRow are also selected.
36224 * @param {Number} startRow The index of the first row in the range
36225 * @param {Number} endRow The index of the last row in the range
36226 * @param {Boolean} keepExisting (optional) True to retain existing selections
36228 selectRange : function(startRow, endRow, keepExisting){
36233 this.clearSelections();
36235 if(startRow <= endRow){
36236 for(var i = startRow; i <= endRow; i++){
36237 this.selectRow(i, true);
36240 for(var i = startRow; i >= endRow; i--){
36241 this.selectRow(i, true);
36247 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36248 * @param {Number} startRow The index of the first row in the range
36249 * @param {Number} endRow The index of the last row in the range
36251 deselectRange : function(startRow, endRow, preventViewNotify){
36255 for(var i = startRow; i <= endRow; i++){
36256 this.deselectRow(i, preventViewNotify);
36262 * @param {Number} row The index of the row to select
36263 * @param {Boolean} keepExisting (optional) True to keep existing selections
36265 selectRow : function(index, keepExisting, preventViewNotify){
36266 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36269 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36270 if(!keepExisting || this.singleSelect){
36271 this.clearSelections();
36273 var r = this.grid.ds.getAt(index);
36274 this.selections.add(r);
36275 this.last = this.lastActive = index;
36276 if(!preventViewNotify){
36277 var view = this.grid.view ? this.grid.view : this.grid;
36278 view.onRowSelect(index);
36280 this.fireEvent("rowselect", this, index, r);
36281 this.fireEvent("selectionchange", this);
36287 * @param {Number} row The index of the row to deselect
36289 deselectRow : function(index, preventViewNotify){
36293 if(this.last == index){
36296 if(this.lastActive == index){
36297 this.lastActive = false;
36299 var r = this.grid.ds.getAt(index);
36300 this.selections.remove(r);
36301 if(!preventViewNotify){
36302 var view = this.grid.view ? this.grid.view : this.grid;
36303 view.onRowDeselect(index);
36305 this.fireEvent("rowdeselect", this, index);
36306 this.fireEvent("selectionchange", this);
36310 restoreLast : function(){
36312 this.last = this._last;
36317 acceptsNav : function(row, col, cm){
36318 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36322 onEditorKey : function(field, e){
36323 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36328 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36330 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36332 }else if(k == e.ENTER && !e.ctrlKey){
36336 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36338 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36340 }else if(k == e.ESC){
36344 g.startEditing(newCell[0], newCell[1]);
36349 * Ext JS Library 1.1.1
36350 * Copyright(c) 2006-2007, Ext JS, LLC.
36352 * Originally Released Under LGPL - original licence link has changed is not relivant.
36355 * <script type="text/javascript">
36358 * @class Roo.grid.CellSelectionModel
36359 * @extends Roo.grid.AbstractSelectionModel
36360 * This class provides the basic implementation for cell selection in a grid.
36362 * @param {Object} config The object containing the configuration of this model.
36363 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36365 Roo.grid.CellSelectionModel = function(config){
36366 Roo.apply(this, config);
36368 this.selection = null;
36372 * @event beforerowselect
36373 * Fires before a cell is selected.
36374 * @param {SelectionModel} this
36375 * @param {Number} rowIndex The selected row index
36376 * @param {Number} colIndex The selected cell index
36378 "beforecellselect" : true,
36380 * @event cellselect
36381 * Fires when a cell is selected.
36382 * @param {SelectionModel} this
36383 * @param {Number} rowIndex The selected row index
36384 * @param {Number} colIndex The selected cell index
36386 "cellselect" : true,
36388 * @event selectionchange
36389 * Fires when the active selection changes.
36390 * @param {SelectionModel} this
36391 * @param {Object} selection null for no selection or an object (o) with two properties
36393 <li>o.record: the record object for the row the selection is in</li>
36394 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36397 "selectionchange" : true,
36400 * Fires when the tab (or enter) was pressed on the last editable cell
36401 * You can use this to trigger add new row.
36402 * @param {SelectionModel} this
36406 * @event beforeeditnext
36407 * Fires before the next editable sell is made active
36408 * You can use this to skip to another cell or fire the tabend
36409 * if you set cell to false
36410 * @param {Object} eventdata object : { cell : [ row, col ] }
36412 "beforeeditnext" : true
36414 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36417 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
36419 enter_is_tab: false,
36422 initEvents : function(){
36423 this.grid.on("mousedown", this.handleMouseDown, this);
36424 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36425 var view = this.grid.view;
36426 view.on("refresh", this.onViewChange, this);
36427 view.on("rowupdated", this.onRowUpdated, this);
36428 view.on("beforerowremoved", this.clearSelections, this);
36429 view.on("beforerowsinserted", this.clearSelections, this);
36430 if(this.grid.isEditor){
36431 this.grid.on("beforeedit", this.beforeEdit, this);
36436 beforeEdit : function(e){
36437 this.select(e.row, e.column, false, true, e.record);
36441 onRowUpdated : function(v, index, r){
36442 if(this.selection && this.selection.record == r){
36443 v.onCellSelect(index, this.selection.cell[1]);
36448 onViewChange : function(){
36449 this.clearSelections(true);
36453 * Returns the currently selected cell,.
36454 * @return {Array} The selected cell (row, column) or null if none selected.
36456 getSelectedCell : function(){
36457 return this.selection ? this.selection.cell : null;
36461 * Clears all selections.
36462 * @param {Boolean} true to prevent the gridview from being notified about the change.
36464 clearSelections : function(preventNotify){
36465 var s = this.selection;
36467 if(preventNotify !== true){
36468 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36470 this.selection = null;
36471 this.fireEvent("selectionchange", this, null);
36476 * Returns true if there is a selection.
36477 * @return {Boolean}
36479 hasSelection : function(){
36480 return this.selection ? true : false;
36484 handleMouseDown : function(e, t){
36485 var v = this.grid.getView();
36486 if(this.isLocked()){
36489 var row = v.findRowIndex(t);
36490 var cell = v.findCellIndex(t);
36491 if(row !== false && cell !== false){
36492 this.select(row, cell);
36498 * @param {Number} rowIndex
36499 * @param {Number} collIndex
36501 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36502 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36503 this.clearSelections();
36504 r = r || this.grid.dataSource.getAt(rowIndex);
36507 cell : [rowIndex, colIndex]
36509 if(!preventViewNotify){
36510 var v = this.grid.getView();
36511 v.onCellSelect(rowIndex, colIndex);
36512 if(preventFocus !== true){
36513 v.focusCell(rowIndex, colIndex);
36516 this.fireEvent("cellselect", this, rowIndex, colIndex);
36517 this.fireEvent("selectionchange", this, this.selection);
36522 isSelectable : function(rowIndex, colIndex, cm){
36523 return !cm.isHidden(colIndex);
36527 handleKeyDown : function(e){
36528 //Roo.log('Cell Sel Model handleKeyDown');
36529 if(!e.isNavKeyPress()){
36532 var g = this.grid, s = this.selection;
36535 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
36537 this.select(cell[0], cell[1]);
36542 var walk = function(row, col, step){
36543 return g.walkCells(row, col, step, sm.isSelectable, sm);
36545 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36552 // handled by onEditorKey
36553 if (g.isEditor && g.editing) {
36557 newCell = walk(r, c-1, -1);
36559 newCell = walk(r, c+1, 1);
36564 newCell = walk(r+1, c, 1);
36568 newCell = walk(r-1, c, -1);
36572 newCell = walk(r, c+1, 1);
36576 newCell = walk(r, c-1, -1);
36581 if(g.isEditor && !g.editing){
36582 g.startEditing(r, c);
36591 this.select(newCell[0], newCell[1]);
36597 acceptsNav : function(row, col, cm){
36598 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36602 * @param {Number} field (not used) - as it's normally used as a listener
36603 * @param {Number} e - event - fake it by using
36605 * var e = Roo.EventObjectImpl.prototype;
36606 * e.keyCode = e.TAB
36610 onEditorKey : function(field, e){
36612 var k = e.getKey(),
36615 ed = g.activeEditor,
36617 ///Roo.log('onEditorKey' + k);
36620 if (this.enter_is_tab && k == e.ENTER) {
36626 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36628 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36634 } else if(k == e.ENTER && !e.ctrlKey){
36637 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36639 } else if(k == e.ESC){
36644 var ecall = { cell : newCell, forward : forward };
36645 this.fireEvent('beforeeditnext', ecall );
36646 newCell = ecall.cell;
36647 forward = ecall.forward;
36651 //Roo.log('next cell after edit');
36652 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36653 } else if (forward) {
36654 // tabbed past last
36655 this.fireEvent.defer(100, this, ['tabend',this]);
36660 * Ext JS Library 1.1.1
36661 * Copyright(c) 2006-2007, Ext JS, LLC.
36663 * Originally Released Under LGPL - original licence link has changed is not relivant.
36666 * <script type="text/javascript">
36670 * @class Roo.grid.EditorGrid
36671 * @extends Roo.grid.Grid
36672 * Class for creating and editable grid.
36673 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36674 * The container MUST have some type of size defined for the grid to fill. The container will be
36675 * automatically set to position relative if it isn't already.
36676 * @param {Object} dataSource The data model to bind to
36677 * @param {Object} colModel The column model with info about this grid's columns
36679 Roo.grid.EditorGrid = function(container, config){
36680 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36681 this.getGridEl().addClass("xedit-grid");
36683 if(!this.selModel){
36684 this.selModel = new Roo.grid.CellSelectionModel();
36687 this.activeEditor = null;
36691 * @event beforeedit
36692 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36693 * <ul style="padding:5px;padding-left:16px;">
36694 * <li>grid - This grid</li>
36695 * <li>record - The record being edited</li>
36696 * <li>field - The field name being edited</li>
36697 * <li>value - The value for the field being edited.</li>
36698 * <li>row - The grid row index</li>
36699 * <li>column - The grid column index</li>
36700 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36702 * @param {Object} e An edit event (see above for description)
36704 "beforeedit" : true,
36707 * Fires after a cell is edited. <br />
36708 * <ul style="padding:5px;padding-left:16px;">
36709 * <li>grid - This grid</li>
36710 * <li>record - The record being edited</li>
36711 * <li>field - The field name being edited</li>
36712 * <li>value - The value being set</li>
36713 * <li>originalValue - The original value for the field, before the edit.</li>
36714 * <li>row - The grid row index</li>
36715 * <li>column - The grid column index</li>
36717 * @param {Object} e An edit event (see above for description)
36719 "afteredit" : true,
36721 * @event validateedit
36722 * Fires after a cell is edited, but before the value is set in the record.
36723 * You can use this to modify the value being set in the field, Return false
36724 * to cancel the change. The edit event object has the following properties <br />
36725 * <ul style="padding:5px;padding-left:16px;">
36726 * <li>editor - This editor</li>
36727 * <li>grid - This grid</li>
36728 * <li>record - The record being edited</li>
36729 * <li>field - The field name being edited</li>
36730 * <li>value - The value being set</li>
36731 * <li>originalValue - The original value for the field, before the edit.</li>
36732 * <li>row - The grid row index</li>
36733 * <li>column - The grid column index</li>
36734 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36736 * @param {Object} e An edit event (see above for description)
36738 "validateedit" : true
36740 this.on("bodyscroll", this.stopEditing, this);
36741 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36744 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36746 * @cfg {Number} clicksToEdit
36747 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36754 trackMouseOver: false, // causes very odd FF errors
36756 onCellDblClick : function(g, row, col){
36757 this.startEditing(row, col);
36760 onEditComplete : function(ed, value, startValue){
36761 this.editing = false;
36762 this.activeEditor = null;
36763 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36765 var field = this.colModel.getDataIndex(ed.col);
36770 originalValue: startValue,
36777 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36780 if(String(value) !== String(startValue)){
36782 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36783 r.set(field, e.value);
36784 // if we are dealing with a combo box..
36785 // then we also set the 'name' colum to be the displayField
36786 if (ed.field.displayField && ed.field.name) {
36787 r.set(ed.field.name, ed.field.el.dom.value);
36790 delete e.cancel; //?? why!!!
36791 this.fireEvent("afteredit", e);
36794 this.fireEvent("afteredit", e); // always fire it!
36796 this.view.focusCell(ed.row, ed.col);
36800 * Starts editing the specified for the specified row/column
36801 * @param {Number} rowIndex
36802 * @param {Number} colIndex
36804 startEditing : function(row, col){
36805 this.stopEditing();
36806 if(this.colModel.isCellEditable(col, row)){
36807 this.view.ensureVisible(row, col, true);
36809 var r = this.dataSource.getAt(row);
36810 var field = this.colModel.getDataIndex(col);
36811 var cell = Roo.get(this.view.getCell(row,col));
36816 value: r.data[field],
36821 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36822 this.editing = true;
36823 var ed = this.colModel.getCellEditor(col, row);
36829 ed.render(ed.parentEl || document.body);
36835 (function(){ // complex but required for focus issues in safari, ie and opera
36839 ed.on("complete", this.onEditComplete, this, {single: true});
36840 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36841 this.activeEditor = ed;
36842 var v = r.data[field];
36843 ed.startEdit(this.view.getCell(row, col), v);
36844 // combo's with 'displayField and name set
36845 if (ed.field.displayField && ed.field.name) {
36846 ed.field.el.dom.value = r.data[ed.field.name];
36850 }).defer(50, this);
36856 * Stops any active editing
36858 stopEditing : function(){
36859 if(this.activeEditor){
36860 this.activeEditor.completeEdit();
36862 this.activeEditor = null;
36866 * Called to get grid's drag proxy text, by default returns this.ddText.
36869 getDragDropText : function(){
36870 var count = this.selModel.getSelectedCell() ? 1 : 0;
36871 return String.format(this.ddText, count, count == 1 ? '' : 's');
36876 * Ext JS Library 1.1.1
36877 * Copyright(c) 2006-2007, Ext JS, LLC.
36879 * Originally Released Under LGPL - original licence link has changed is not relivant.
36882 * <script type="text/javascript">
36885 // private - not really -- you end up using it !
36886 // This is a support class used internally by the Grid components
36889 * @class Roo.grid.GridEditor
36890 * @extends Roo.Editor
36891 * Class for creating and editable grid elements.
36892 * @param {Object} config any settings (must include field)
36894 Roo.grid.GridEditor = function(field, config){
36895 if (!config && field.field) {
36897 field = Roo.factory(config.field, Roo.form);
36899 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36900 field.monitorTab = false;
36903 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36906 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36909 alignment: "tl-tl",
36912 cls: "x-small-editor x-grid-editor",
36917 * Ext JS Library 1.1.1
36918 * Copyright(c) 2006-2007, Ext JS, LLC.
36920 * Originally Released Under LGPL - original licence link has changed is not relivant.
36923 * <script type="text/javascript">
36928 Roo.grid.PropertyRecord = Roo.data.Record.create([
36929 {name:'name',type:'string'}, 'value'
36933 Roo.grid.PropertyStore = function(grid, source){
36935 this.store = new Roo.data.Store({
36936 recordType : Roo.grid.PropertyRecord
36938 this.store.on('update', this.onUpdate, this);
36940 this.setSource(source);
36942 Roo.grid.PropertyStore.superclass.constructor.call(this);
36947 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36948 setSource : function(o){
36950 this.store.removeAll();
36953 if(this.isEditableValue(o[k])){
36954 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36957 this.store.loadRecords({records: data}, {}, true);
36960 onUpdate : function(ds, record, type){
36961 if(type == Roo.data.Record.EDIT){
36962 var v = record.data['value'];
36963 var oldValue = record.modified['value'];
36964 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36965 this.source[record.id] = v;
36967 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36974 getProperty : function(row){
36975 return this.store.getAt(row);
36978 isEditableValue: function(val){
36979 if(val && val instanceof Date){
36981 }else if(typeof val == 'object' || typeof val == 'function'){
36987 setValue : function(prop, value){
36988 this.source[prop] = value;
36989 this.store.getById(prop).set('value', value);
36992 getSource : function(){
36993 return this.source;
36997 Roo.grid.PropertyColumnModel = function(grid, store){
37000 g.PropertyColumnModel.superclass.constructor.call(this, [
37001 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37002 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37004 this.store = store;
37005 this.bselect = Roo.DomHelper.append(document.body, {
37006 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37007 {tag: 'option', value: 'true', html: 'true'},
37008 {tag: 'option', value: 'false', html: 'false'}
37011 Roo.id(this.bselect);
37014 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37015 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37016 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37017 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37018 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37020 this.renderCellDelegate = this.renderCell.createDelegate(this);
37021 this.renderPropDelegate = this.renderProp.createDelegate(this);
37024 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37028 valueText : 'Value',
37030 dateFormat : 'm/j/Y',
37033 renderDate : function(dateVal){
37034 return dateVal.dateFormat(this.dateFormat);
37037 renderBool : function(bVal){
37038 return bVal ? 'true' : 'false';
37041 isCellEditable : function(colIndex, rowIndex){
37042 return colIndex == 1;
37045 getRenderer : function(col){
37047 this.renderCellDelegate : this.renderPropDelegate;
37050 renderProp : function(v){
37051 return this.getPropertyName(v);
37054 renderCell : function(val){
37056 if(val instanceof Date){
37057 rv = this.renderDate(val);
37058 }else if(typeof val == 'boolean'){
37059 rv = this.renderBool(val);
37061 return Roo.util.Format.htmlEncode(rv);
37064 getPropertyName : function(name){
37065 var pn = this.grid.propertyNames;
37066 return pn && pn[name] ? pn[name] : name;
37069 getCellEditor : function(colIndex, rowIndex){
37070 var p = this.store.getProperty(rowIndex);
37071 var n = p.data['name'], val = p.data['value'];
37073 if(typeof(this.grid.customEditors[n]) == 'string'){
37074 return this.editors[this.grid.customEditors[n]];
37076 if(typeof(this.grid.customEditors[n]) != 'undefined'){
37077 return this.grid.customEditors[n];
37079 if(val instanceof Date){
37080 return this.editors['date'];
37081 }else if(typeof val == 'number'){
37082 return this.editors['number'];
37083 }else if(typeof val == 'boolean'){
37084 return this.editors['boolean'];
37086 return this.editors['string'];
37092 * @class Roo.grid.PropertyGrid
37093 * @extends Roo.grid.EditorGrid
37094 * This class represents the interface of a component based property grid control.
37095 * <br><br>Usage:<pre><code>
37096 var grid = new Roo.grid.PropertyGrid("my-container-id", {
37104 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37105 * The container MUST have some type of size defined for the grid to fill. The container will be
37106 * automatically set to position relative if it isn't already.
37107 * @param {Object} config A config object that sets properties on this grid.
37109 Roo.grid.PropertyGrid = function(container, config){
37110 config = config || {};
37111 var store = new Roo.grid.PropertyStore(this);
37112 this.store = store;
37113 var cm = new Roo.grid.PropertyColumnModel(this, store);
37114 store.store.sort('name', 'ASC');
37115 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37118 enableColLock:false,
37119 enableColumnMove:false,
37121 trackMouseOver: false,
37124 this.getGridEl().addClass('x-props-grid');
37125 this.lastEditRow = null;
37126 this.on('columnresize', this.onColumnResize, this);
37129 * @event beforepropertychange
37130 * Fires before a property changes (return false to stop?)
37131 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37132 * @param {String} id Record Id
37133 * @param {String} newval New Value
37134 * @param {String} oldval Old Value
37136 "beforepropertychange": true,
37138 * @event propertychange
37139 * Fires after a property changes
37140 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37141 * @param {String} id Record Id
37142 * @param {String} newval New Value
37143 * @param {String} oldval Old Value
37145 "propertychange": true
37147 this.customEditors = this.customEditors || {};
37149 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37152 * @cfg {Object} customEditors map of colnames=> custom editors.
37153 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37154 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37155 * false disables editing of the field.
37159 * @cfg {Object} propertyNames map of property Names to their displayed value
37162 render : function(){
37163 Roo.grid.PropertyGrid.superclass.render.call(this);
37164 this.autoSize.defer(100, this);
37167 autoSize : function(){
37168 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37170 this.view.fitColumns();
37174 onColumnResize : function(){
37175 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37179 * Sets the data for the Grid
37180 * accepts a Key => Value object of all the elements avaiable.
37181 * @param {Object} data to appear in grid.
37183 setSource : function(source){
37184 this.store.setSource(source);
37188 * Gets all the data from the grid.
37189 * @return {Object} data data stored in grid
37191 getSource : function(){
37192 return this.store.getSource();
37201 * @class Roo.grid.Calendar
37202 * @extends Roo.util.Grid
37203 * This class extends the Grid to provide a calendar widget
37204 * <br><br>Usage:<pre><code>
37205 var grid = new Roo.grid.Calendar("my-container-id", {
37208 selModel: mySelectionModel,
37209 autoSizeColumns: true,
37210 monitorWindowResize: false,
37211 trackMouseOver: true
37212 eventstore : real data store..
37218 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37219 * The container MUST have some type of size defined for the grid to fill. The container will be
37220 * automatically set to position relative if it isn't already.
37221 * @param {Object} config A config object that sets properties on this grid.
37223 Roo.grid.Calendar = function(container, config){
37224 // initialize the container
37225 this.container = Roo.get(container);
37226 this.container.update("");
37227 this.container.setStyle("overflow", "hidden");
37228 this.container.addClass('x-grid-container');
37230 this.id = this.container.id;
37232 Roo.apply(this, config);
37233 // check and correct shorthanded configs
37237 for (var r = 0;r < 6;r++) {
37240 for (var c =0;c < 7;c++) {
37244 if (this.eventStore) {
37245 this.eventStore= Roo.factory(this.eventStore, Roo.data);
37246 this.eventStore.on('load',this.onLoad, this);
37247 this.eventStore.on('beforeload',this.clearEvents, this);
37251 this.dataSource = new Roo.data.Store({
37252 proxy: new Roo.data.MemoryProxy(rows),
37253 reader: new Roo.data.ArrayReader({}, [
37254 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37257 this.dataSource.load();
37258 this.ds = this.dataSource;
37259 this.ds.xmodule = this.xmodule || false;
37262 var cellRender = function(v,x,r)
37264 return String.format(
37265 '<div class="fc-day fc-widget-content"><div>' +
37266 '<div class="fc-event-container"></div>' +
37267 '<div class="fc-day-number">{0}</div>'+
37269 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37270 '</div></div>', v);
37275 this.colModel = new Roo.grid.ColumnModel( [
37277 xtype: 'ColumnModel',
37279 dataIndex : 'weekday0',
37281 renderer : cellRender
37284 xtype: 'ColumnModel',
37286 dataIndex : 'weekday1',
37288 renderer : cellRender
37291 xtype: 'ColumnModel',
37293 dataIndex : 'weekday2',
37294 header : 'Tuesday',
37295 renderer : cellRender
37298 xtype: 'ColumnModel',
37300 dataIndex : 'weekday3',
37301 header : 'Wednesday',
37302 renderer : cellRender
37305 xtype: 'ColumnModel',
37307 dataIndex : 'weekday4',
37308 header : 'Thursday',
37309 renderer : cellRender
37312 xtype: 'ColumnModel',
37314 dataIndex : 'weekday5',
37316 renderer : cellRender
37319 xtype: 'ColumnModel',
37321 dataIndex : 'weekday6',
37322 header : 'Saturday',
37323 renderer : cellRender
37326 this.cm = this.colModel;
37327 this.cm.xmodule = this.xmodule || false;
37331 //this.selModel = new Roo.grid.CellSelectionModel();
37332 //this.sm = this.selModel;
37333 //this.selModel.init(this);
37337 this.container.setWidth(this.width);
37341 this.container.setHeight(this.height);
37348 * The raw click event for the entire grid.
37349 * @param {Roo.EventObject} e
37354 * The raw dblclick event for the entire grid.
37355 * @param {Roo.EventObject} e
37359 * @event contextmenu
37360 * The raw contextmenu event for the entire grid.
37361 * @param {Roo.EventObject} e
37363 "contextmenu" : true,
37366 * The raw mousedown event for the entire grid.
37367 * @param {Roo.EventObject} e
37369 "mousedown" : true,
37372 * The raw mouseup event for the entire grid.
37373 * @param {Roo.EventObject} e
37378 * The raw mouseover event for the entire grid.
37379 * @param {Roo.EventObject} e
37381 "mouseover" : true,
37384 * The raw mouseout event for the entire grid.
37385 * @param {Roo.EventObject} e
37390 * The raw keypress event for the entire grid.
37391 * @param {Roo.EventObject} e
37396 * The raw keydown event for the entire grid.
37397 * @param {Roo.EventObject} e
37405 * Fires when a cell is clicked
37406 * @param {Grid} this
37407 * @param {Number} rowIndex
37408 * @param {Number} columnIndex
37409 * @param {Roo.EventObject} e
37411 "cellclick" : true,
37413 * @event celldblclick
37414 * Fires when a cell is double clicked
37415 * @param {Grid} this
37416 * @param {Number} rowIndex
37417 * @param {Number} columnIndex
37418 * @param {Roo.EventObject} e
37420 "celldblclick" : true,
37423 * Fires when a row is clicked
37424 * @param {Grid} this
37425 * @param {Number} rowIndex
37426 * @param {Roo.EventObject} e
37430 * @event rowdblclick
37431 * Fires when a row is double clicked
37432 * @param {Grid} this
37433 * @param {Number} rowIndex
37434 * @param {Roo.EventObject} e
37436 "rowdblclick" : true,
37438 * @event headerclick
37439 * Fires when a header is clicked
37440 * @param {Grid} this
37441 * @param {Number} columnIndex
37442 * @param {Roo.EventObject} e
37444 "headerclick" : true,
37446 * @event headerdblclick
37447 * Fires when a header cell is double clicked
37448 * @param {Grid} this
37449 * @param {Number} columnIndex
37450 * @param {Roo.EventObject} e
37452 "headerdblclick" : true,
37454 * @event rowcontextmenu
37455 * Fires when a row is right clicked
37456 * @param {Grid} this
37457 * @param {Number} rowIndex
37458 * @param {Roo.EventObject} e
37460 "rowcontextmenu" : true,
37462 * @event cellcontextmenu
37463 * Fires when a cell is right clicked
37464 * @param {Grid} this
37465 * @param {Number} rowIndex
37466 * @param {Number} cellIndex
37467 * @param {Roo.EventObject} e
37469 "cellcontextmenu" : true,
37471 * @event headercontextmenu
37472 * Fires when a header is right clicked
37473 * @param {Grid} this
37474 * @param {Number} columnIndex
37475 * @param {Roo.EventObject} e
37477 "headercontextmenu" : true,
37479 * @event bodyscroll
37480 * Fires when the body element is scrolled
37481 * @param {Number} scrollLeft
37482 * @param {Number} scrollTop
37484 "bodyscroll" : true,
37486 * @event columnresize
37487 * Fires when the user resizes a column
37488 * @param {Number} columnIndex
37489 * @param {Number} newSize
37491 "columnresize" : true,
37493 * @event columnmove
37494 * Fires when the user moves a column
37495 * @param {Number} oldIndex
37496 * @param {Number} newIndex
37498 "columnmove" : true,
37501 * Fires when row(s) start being dragged
37502 * @param {Grid} this
37503 * @param {Roo.GridDD} dd The drag drop object
37504 * @param {event} e The raw browser event
37506 "startdrag" : true,
37509 * Fires when a drag operation is complete
37510 * @param {Grid} this
37511 * @param {Roo.GridDD} dd The drag drop object
37512 * @param {event} e The raw browser event
37517 * Fires when dragged row(s) are dropped on a valid DD target
37518 * @param {Grid} this
37519 * @param {Roo.GridDD} dd The drag drop object
37520 * @param {String} targetId The target drag drop object
37521 * @param {event} e The raw browser event
37526 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37527 * @param {Grid} this
37528 * @param {Roo.GridDD} dd The drag drop object
37529 * @param {String} targetId The target drag drop object
37530 * @param {event} e The raw browser event
37535 * Fires when the dragged row(s) first cross another DD target while being dragged
37536 * @param {Grid} this
37537 * @param {Roo.GridDD} dd The drag drop object
37538 * @param {String} targetId The target drag drop object
37539 * @param {event} e The raw browser event
37541 "dragenter" : true,
37544 * Fires when the dragged row(s) leave another DD target while being dragged
37545 * @param {Grid} this
37546 * @param {Roo.GridDD} dd The drag drop object
37547 * @param {String} targetId The target drag drop object
37548 * @param {event} e The raw browser event
37553 * Fires when a row is rendered, so you can change add a style to it.
37554 * @param {GridView} gridview The grid view
37555 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37561 * Fires when the grid is rendered
37562 * @param {Grid} grid
37567 * Fires when a date is selected
37568 * @param {DatePicker} this
37569 * @param {Date} date The selected date
37573 * @event monthchange
37574 * Fires when the displayed month changes
37575 * @param {DatePicker} this
37576 * @param {Date} date The selected month
37578 'monthchange': true,
37580 * @event evententer
37581 * Fires when mouse over an event
37582 * @param {Calendar} this
37583 * @param {event} Event
37585 'evententer': true,
37587 * @event eventleave
37588 * Fires when the mouse leaves an
37589 * @param {Calendar} this
37592 'eventleave': true,
37594 * @event eventclick
37595 * Fires when the mouse click an
37596 * @param {Calendar} this
37599 'eventclick': true,
37601 * @event eventrender
37602 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37603 * @param {Calendar} this
37604 * @param {data} data to be modified
37606 'eventrender': true
37610 Roo.grid.Grid.superclass.constructor.call(this);
37611 this.on('render', function() {
37612 this.view.el.addClass('x-grid-cal');
37614 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37618 if (!Roo.grid.Calendar.style) {
37619 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37622 '.x-grid-cal .x-grid-col' : {
37623 height: 'auto !important',
37624 'vertical-align': 'top'
37626 '.x-grid-cal .fc-event-hori' : {
37637 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37639 * @cfg {Store} eventStore The store that loads events.
37644 activeDate : false,
37647 monitorWindowResize : false,
37650 resizeColumns : function() {
37651 var col = (this.view.el.getWidth() / 7) - 3;
37652 // loop through cols, and setWidth
37653 for(var i =0 ; i < 7 ; i++){
37654 this.cm.setColumnWidth(i, col);
37657 setDate :function(date) {
37659 Roo.log('setDate?');
37661 this.resizeColumns();
37662 var vd = this.activeDate;
37663 this.activeDate = date;
37664 // if(vd && this.el){
37665 // var t = date.getTime();
37666 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37667 // Roo.log('using add remove');
37669 // this.fireEvent('monthchange', this, date);
37671 // this.cells.removeClass("fc-state-highlight");
37672 // this.cells.each(function(c){
37673 // if(c.dateValue == t){
37674 // c.addClass("fc-state-highlight");
37675 // setTimeout(function(){
37676 // try{c.dom.firstChild.focus();}catch(e){}
37686 var days = date.getDaysInMonth();
37688 var firstOfMonth = date.getFirstDateOfMonth();
37689 var startingPos = firstOfMonth.getDay()-this.startDay;
37691 if(startingPos < this.startDay){
37695 var pm = date.add(Date.MONTH, -1);
37696 var prevStart = pm.getDaysInMonth()-startingPos;
37700 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37702 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37703 //this.cells.addClassOnOver('fc-state-hover');
37705 var cells = this.cells.elements;
37706 var textEls = this.textNodes;
37708 //Roo.each(cells, function(cell){
37709 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37712 days += startingPos;
37714 // convert everything to numbers so it's fast
37715 var day = 86400000;
37716 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37719 //Roo.log(prevStart);
37721 var today = new Date().clearTime().getTime();
37722 var sel = date.clearTime().getTime();
37723 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37724 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37725 var ddMatch = this.disabledDatesRE;
37726 var ddText = this.disabledDatesText;
37727 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37728 var ddaysText = this.disabledDaysText;
37729 var format = this.format;
37731 var setCellClass = function(cal, cell){
37733 //Roo.log('set Cell Class');
37735 var t = d.getTime();
37740 cell.dateValue = t;
37742 cell.className += " fc-today";
37743 cell.className += " fc-state-highlight";
37744 cell.title = cal.todayText;
37747 // disable highlight in other month..
37748 cell.className += " fc-state-highlight";
37753 //cell.className = " fc-state-disabled";
37754 cell.title = cal.minText;
37758 //cell.className = " fc-state-disabled";
37759 cell.title = cal.maxText;
37763 if(ddays.indexOf(d.getDay()) != -1){
37764 // cell.title = ddaysText;
37765 // cell.className = " fc-state-disabled";
37768 if(ddMatch && format){
37769 var fvalue = d.dateFormat(format);
37770 if(ddMatch.test(fvalue)){
37771 cell.title = ddText.replace("%0", fvalue);
37772 cell.className = " fc-state-disabled";
37776 if (!cell.initialClassName) {
37777 cell.initialClassName = cell.dom.className;
37780 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37785 for(; i < startingPos; i++) {
37786 cells[i].dayName = (++prevStart);
37787 Roo.log(textEls[i]);
37788 d.setDate(d.getDate()+1);
37790 //cells[i].className = "fc-past fc-other-month";
37791 setCellClass(this, cells[i]);
37796 for(; i < days; i++){
37797 intDay = i - startingPos + 1;
37798 cells[i].dayName = (intDay);
37799 d.setDate(d.getDate()+1);
37801 cells[i].className = ''; // "x-date-active";
37802 setCellClass(this, cells[i]);
37806 for(; i < 42; i++) {
37807 //textEls[i].innerHTML = (++extraDays);
37809 d.setDate(d.getDate()+1);
37810 cells[i].dayName = (++extraDays);
37811 cells[i].className = "fc-future fc-other-month";
37812 setCellClass(this, cells[i]);
37815 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37817 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37819 // this will cause all the cells to mis
37822 for (var r = 0;r < 6;r++) {
37823 for (var c =0;c < 7;c++) {
37824 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37828 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37829 for(i=0;i<cells.length;i++) {
37831 this.cells.elements[i].dayName = cells[i].dayName ;
37832 this.cells.elements[i].className = cells[i].className;
37833 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37834 this.cells.elements[i].title = cells[i].title ;
37835 this.cells.elements[i].dateValue = cells[i].dateValue ;
37841 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37842 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37844 ////if(totalRows != 6){
37845 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37846 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37849 this.fireEvent('monthchange', this, date);
37854 * Returns the grid's SelectionModel.
37855 * @return {SelectionModel}
37857 getSelectionModel : function(){
37858 if(!this.selModel){
37859 this.selModel = new Roo.grid.CellSelectionModel();
37861 return this.selModel;
37865 this.eventStore.load()
37871 findCell : function(dt) {
37872 dt = dt.clearTime().getTime();
37874 this.cells.each(function(c){
37875 //Roo.log("check " +c.dateValue + '?=' + dt);
37876 if(c.dateValue == dt){
37886 findCells : function(rec) {
37887 var s = rec.data.start_dt.clone().clearTime().getTime();
37889 var e= rec.data.end_dt.clone().clearTime().getTime();
37892 this.cells.each(function(c){
37893 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37895 if(c.dateValue > e){
37898 if(c.dateValue < s){
37907 findBestRow: function(cells)
37911 for (var i =0 ; i < cells.length;i++) {
37912 ret = Math.max(cells[i].rows || 0,ret);
37919 addItem : function(rec)
37921 // look for vertical location slot in
37922 var cells = this.findCells(rec);
37924 rec.row = this.findBestRow(cells);
37926 // work out the location.
37930 for(var i =0; i < cells.length; i++) {
37938 if (crow.start.getY() == cells[i].getY()) {
37940 crow.end = cells[i];
37956 for (var i = 0; i < cells.length;i++) {
37957 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37964 clearEvents: function() {
37966 if (!this.eventStore.getCount()) {
37969 // reset number of rows in cells.
37970 Roo.each(this.cells.elements, function(c){
37974 this.eventStore.each(function(e) {
37975 this.clearEvent(e);
37980 clearEvent : function(ev)
37983 Roo.each(ev.els, function(el) {
37984 el.un('mouseenter' ,this.onEventEnter, this);
37985 el.un('mouseleave' ,this.onEventLeave, this);
37993 renderEvent : function(ev,ctr) {
37995 ctr = this.view.el.select('.fc-event-container',true).first();
37999 this.clearEvent(ev);
38005 var cells = ev.cells;
38006 var rows = ev.rows;
38007 this.fireEvent('eventrender', this, ev);
38009 for(var i =0; i < rows.length; i++) {
38013 cls += ' fc-event-start';
38015 if ((i+1) == rows.length) {
38016 cls += ' fc-event-end';
38019 //Roo.log(ev.data);
38020 // how many rows should it span..
38021 var cg = this.eventTmpl.append(ctr,Roo.apply({
38024 }, ev.data) , true);
38027 cg.on('mouseenter' ,this.onEventEnter, this, ev);
38028 cg.on('mouseleave' ,this.onEventLeave, this, ev);
38029 cg.on('click', this.onEventClick, this, ev);
38033 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38034 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38037 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
38038 cg.setWidth(ebox.right - sbox.x -2);
38042 renderEvents: function()
38044 // first make sure there is enough space..
38046 if (!this.eventTmpl) {
38047 this.eventTmpl = new Roo.Template(
38048 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
38049 '<div class="fc-event-inner">' +
38050 '<span class="fc-event-time">{time}</span>' +
38051 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38053 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
38061 this.cells.each(function(c) {
38062 //Roo.log(c.select('.fc-day-content div',true).first());
38063 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38066 var ctr = this.view.el.select('.fc-event-container',true).first();
38069 this.eventStore.each(function(ev){
38071 this.renderEvent(ev);
38075 this.view.layout();
38079 onEventEnter: function (e, el,event,d) {
38080 this.fireEvent('evententer', this, el, event);
38083 onEventLeave: function (e, el,event,d) {
38084 this.fireEvent('eventleave', this, el, event);
38087 onEventClick: function (e, el,event,d) {
38088 this.fireEvent('eventclick', this, el, event);
38091 onMonthChange: function () {
38095 onLoad: function () {
38097 //Roo.log('calendar onload');
38099 if(this.eventStore.getCount() > 0){
38103 this.eventStore.each(function(d){
38108 if (typeof(add.end_dt) == 'undefined') {
38109 Roo.log("Missing End time in calendar data: ");
38113 if (typeof(add.start_dt) == 'undefined') {
38114 Roo.log("Missing Start time in calendar data: ");
38118 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38119 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38120 add.id = add.id || d.id;
38121 add.title = add.title || '??';
38129 this.renderEvents();
38139 render : function ()
38143 if (!this.view.el.hasClass('course-timesheet')) {
38144 this.view.el.addClass('course-timesheet');
38146 if (this.tsStyle) {
38151 Roo.log(_this.grid.view.el.getWidth());
38154 this.tsStyle = Roo.util.CSS.createStyleSheet({
38155 '.course-timesheet .x-grid-row' : {
38158 '.x-grid-row td' : {
38159 'vertical-align' : 0
38161 '.course-edit-link' : {
38163 'text-overflow' : 'ellipsis',
38164 'overflow' : 'hidden',
38165 'white-space' : 'nowrap',
38166 'cursor' : 'pointer'
38171 '.de-act-sup-link' : {
38172 'color' : 'purple',
38173 'text-decoration' : 'line-through'
38177 'text-decoration' : 'line-through'
38179 '.course-timesheet .course-highlight' : {
38180 'border-top-style': 'dashed !important',
38181 'border-bottom-bottom': 'dashed !important'
38183 '.course-timesheet .course-item' : {
38184 'font-family' : 'tahoma, arial, helvetica',
38185 'font-size' : '11px',
38186 'overflow' : 'hidden',
38187 'padding-left' : '10px',
38188 'padding-right' : '10px',
38189 'padding-top' : '10px'
38197 monitorWindowResize : false,
38198 cellrenderer : function(v,x,r)
38203 xtype: 'CellSelectionModel',
38210 beforeload : function (_self, options)
38212 options.params = options.params || {};
38213 options.params._month = _this.monthField.getValue();
38214 options.params.limit = 9999;
38215 options.params['sort'] = 'when_dt';
38216 options.params['dir'] = 'ASC';
38217 this.proxy.loadResponse = this.loadResponse;
38219 //this.addColumns();
38221 load : function (_self, records, options)
38223 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38224 // if you click on the translation.. you can edit it...
38225 var el = Roo.get(this);
38226 var id = el.dom.getAttribute('data-id');
38227 var d = el.dom.getAttribute('data-date');
38228 var t = el.dom.getAttribute('data-time');
38229 //var id = this.child('span').dom.textContent;
38232 Pman.Dialog.CourseCalendar.show({
38236 productitem_active : id ? 1 : 0
38238 _this.grid.ds.load({});
38243 _this.panel.fireEvent('resize', [ '', '' ]);
38246 loadResponse : function(o, success, response){
38247 // this is overridden on before load..
38249 Roo.log("our code?");
38250 //Roo.log(success);
38251 //Roo.log(response)
38252 delete this.activeRequest;
38254 this.fireEvent("loadexception", this, o, response);
38255 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38260 result = o.reader.read(response);
38262 Roo.log("load exception?");
38263 this.fireEvent("loadexception", this, o, response, e);
38264 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38267 Roo.log("ready...");
38268 // loop through result.records;
38269 // and set this.tdate[date] = [] << array of records..
38271 Roo.each(result.records, function(r){
38273 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38274 _this.tdata[r.data.when_dt.format('j')] = [];
38276 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38279 //Roo.log(_this.tdata);
38281 result.records = [];
38282 result.totalRecords = 6;
38284 // let's generate some duumy records for the rows.
38285 //var st = _this.dateField.getValue();
38287 // work out monday..
38288 //st = st.add(Date.DAY, -1 * st.format('w'));
38290 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38292 var firstOfMonth = date.getFirstDayOfMonth();
38293 var days = date.getDaysInMonth();
38295 var firstAdded = false;
38296 for (var i = 0; i < result.totalRecords ; i++) {
38297 //var d= st.add(Date.DAY, i);
38300 for(var w = 0 ; w < 7 ; w++){
38301 if(!firstAdded && firstOfMonth != w){
38308 var dd = (d > 0 && d < 10) ? "0"+d : d;
38309 row['weekday'+w] = String.format(
38310 '<span style="font-size: 16px;"><b>{0}</b></span>'+
38311 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38313 date.format('Y-m-')+dd
38316 if(typeof(_this.tdata[d]) != 'undefined'){
38317 Roo.each(_this.tdata[d], function(r){
38321 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38322 if(r.parent_id*1>0){
38323 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38326 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38327 deactive = 'de-act-link';
38330 row['weekday'+w] += String.format(
38331 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38333 r.product_id_name, //1
38334 r.when_dt.format('h:ia'), //2
38344 // only do this if something added..
38346 result.records.push(_this.grid.dataSource.reader.newRow(row));
38350 // push it twice. (second one with an hour..
38354 this.fireEvent("load", this, o, o.request.arg);
38355 o.request.callback.call(o.request.scope, result, o.request.arg, true);
38357 sortInfo : {field: 'when_dt', direction : 'ASC' },
38359 xtype: 'HttpProxy',
38362 url : baseURL + '/Roo/Shop_course.php'
38365 xtype: 'JsonReader',
38382 'name': 'parent_id',
38386 'name': 'product_id',
38390 'name': 'productitem_id',
38408 click : function (_self, e)
38410 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38411 sd.setMonth(sd.getMonth()-1);
38412 _this.monthField.setValue(sd.format('Y-m-d'));
38413 _this.grid.ds.load({});
38419 xtype: 'Separator',
38423 xtype: 'MonthField',
38426 render : function (_self)
38428 _this.monthField = _self;
38429 // _this.monthField.set today
38431 select : function (combo, date)
38433 _this.grid.ds.load({});
38436 value : (function() { return new Date(); })()
38439 xtype: 'Separator',
38445 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38455 click : function (_self, e)
38457 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38458 sd.setMonth(sd.getMonth()+1);
38459 _this.monthField.setValue(sd.format('Y-m-d'));
38460 _this.grid.ds.load({});
38473 * Ext JS Library 1.1.1
38474 * Copyright(c) 2006-2007, Ext JS, LLC.
38476 * Originally Released Under LGPL - original licence link has changed is not relivant.
38479 * <script type="text/javascript">
38483 * @class Roo.LoadMask
38484 * A simple utility class for generically masking elements while loading data. If the element being masked has
38485 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38486 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38487 * element's UpdateManager load indicator and will be destroyed after the initial load.
38489 * Create a new LoadMask
38490 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38491 * @param {Object} config The config object
38493 Roo.LoadMask = function(el, config){
38494 this.el = Roo.get(el);
38495 Roo.apply(this, config);
38497 this.store.on('beforeload', this.onBeforeLoad, this);
38498 this.store.on('load', this.onLoad, this);
38499 this.store.on('loadexception', this.onLoadException, this);
38500 this.removeMask = false;
38502 var um = this.el.getUpdateManager();
38503 um.showLoadIndicator = false; // disable the default indicator
38504 um.on('beforeupdate', this.onBeforeLoad, this);
38505 um.on('update', this.onLoad, this);
38506 um.on('failure', this.onLoad, this);
38507 this.removeMask = true;
38511 Roo.LoadMask.prototype = {
38513 * @cfg {Boolean} removeMask
38514 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38515 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38517 removeMask : false,
38519 * @cfg {String} msg
38520 * The text to display in a centered loading message box (defaults to 'Loading...')
38522 msg : 'Loading...',
38524 * @cfg {String} msgCls
38525 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38527 msgCls : 'x-mask-loading',
38530 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38536 * Disables the mask to prevent it from being displayed
38538 disable : function(){
38539 this.disabled = true;
38543 * Enables the mask so that it can be displayed
38545 enable : function(){
38546 this.disabled = false;
38549 onLoadException : function()
38551 Roo.log(arguments);
38553 if (typeof(arguments[3]) != 'undefined') {
38554 Roo.MessageBox.alert("Error loading",arguments[3]);
38558 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38559 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38566 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38569 onLoad : function()
38571 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38575 onBeforeLoad : function(){
38576 if(!this.disabled){
38577 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38582 destroy : function(){
38584 this.store.un('beforeload', this.onBeforeLoad, this);
38585 this.store.un('load', this.onLoad, this);
38586 this.store.un('loadexception', this.onLoadException, this);
38588 var um = this.el.getUpdateManager();
38589 um.un('beforeupdate', this.onBeforeLoad, this);
38590 um.un('update', this.onLoad, this);
38591 um.un('failure', this.onLoad, this);
38596 * Ext JS Library 1.1.1
38597 * Copyright(c) 2006-2007, Ext JS, LLC.
38599 * Originally Released Under LGPL - original licence link has changed is not relivant.
38602 * <script type="text/javascript">
38607 * @class Roo.XTemplate
38608 * @extends Roo.Template
38609 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38611 var t = new Roo.XTemplate(
38612 '<select name="{name}">',
38613 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38617 // then append, applying the master template values
38620 * Supported features:
38625 {a_variable} - output encoded.
38626 {a_variable.format:("Y-m-d")} - call a method on the variable
38627 {a_variable:raw} - unencoded output
38628 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38629 {a_variable:this.method_on_template(...)} - call a method on the template object.
38634 <tpl for="a_variable or condition.."></tpl>
38635 <tpl if="a_variable or condition"></tpl>
38636 <tpl exec="some javascript"></tpl>
38637 <tpl name="named_template"></tpl> (experimental)
38639 <tpl for="."></tpl> - just iterate the property..
38640 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38644 Roo.XTemplate = function()
38646 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38653 Roo.extend(Roo.XTemplate, Roo.Template, {
38656 * The various sub templates
38661 * basic tag replacing syntax
38664 * // you can fake an object call by doing this
38668 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38671 * compile the template
38673 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38676 compile: function()
38680 s = ['<tpl>', s, '</tpl>'].join('');
38682 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38683 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38684 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38685 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38686 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38691 while(true == !!(m = s.match(re))){
38692 var forMatch = m[0].match(nameRe),
38693 ifMatch = m[0].match(ifRe),
38694 execMatch = m[0].match(execRe),
38695 namedMatch = m[0].match(namedRe),
38700 name = forMatch && forMatch[1] ? forMatch[1] : '';
38703 // if - puts fn into test..
38704 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38706 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38711 // exec - calls a function... returns empty if true is returned.
38712 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38714 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38722 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38723 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38724 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38727 var uid = namedMatch ? namedMatch[1] : id;
38731 id: namedMatch ? namedMatch[1] : id,
38738 s = s.replace(m[0], '');
38740 s = s.replace(m[0], '{xtpl'+ id + '}');
38745 for(var i = tpls.length-1; i >= 0; --i){
38746 this.compileTpl(tpls[i]);
38747 this.tpls[tpls[i].id] = tpls[i];
38749 this.master = tpls[tpls.length-1];
38753 * same as applyTemplate, except it's done to one of the subTemplates
38754 * when using named templates, you can do:
38756 * var str = pl.applySubTemplate('your-name', values);
38759 * @param {Number} id of the template
38760 * @param {Object} values to apply to template
38761 * @param {Object} parent (normaly the instance of this object)
38763 applySubTemplate : function(id, values, parent)
38767 var t = this.tpls[id];
38771 if(t.test && !t.test.call(this, values, parent)){
38775 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38776 Roo.log(e.toString());
38782 if(t.exec && t.exec.call(this, values, parent)){
38786 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38787 Roo.log(e.toString());
38792 var vs = t.target ? t.target.call(this, values, parent) : values;
38793 parent = t.target ? values : parent;
38794 if(t.target && vs instanceof Array){
38796 for(var i = 0, len = vs.length; i < len; i++){
38797 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38799 return buf.join('');
38801 return t.compiled.call(this, vs, parent);
38803 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38804 Roo.log(e.toString());
38805 Roo.log(t.compiled);
38810 compileTpl : function(tpl)
38812 var fm = Roo.util.Format;
38813 var useF = this.disableFormats !== true;
38814 var sep = Roo.isGecko ? "+" : ",";
38815 var undef = function(str) {
38816 Roo.log("Property not found :" + str);
38820 var fn = function(m, name, format, args)
38822 //Roo.log(arguments);
38823 args = args ? args.replace(/\\'/g,"'") : args;
38824 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38825 if (typeof(format) == 'undefined') {
38826 format= 'htmlEncode';
38828 if (format == 'raw' ) {
38832 if(name.substr(0, 4) == 'xtpl'){
38833 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38836 // build an array of options to determine if value is undefined..
38838 // basically get 'xxxx.yyyy' then do
38839 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38840 // (function () { Roo.log("Property not found"); return ''; })() :
38845 Roo.each(name.split('.'), function(st) {
38846 lookfor += (lookfor.length ? '.': '') + st;
38847 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38850 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38853 if(format && useF){
38855 args = args ? ',' + args : "";
38857 if(format.substr(0, 5) != "this."){
38858 format = "fm." + format + '(';
38860 format = 'this.call("'+ format.substr(5) + '", ';
38864 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38868 // called with xxyx.yuu:(test,test)
38870 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38872 // raw.. - :raw modifier..
38873 return "'"+ sep + udef_st + name + ")"+sep+"'";
38877 // branched to use + in gecko and [].join() in others
38879 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38880 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38883 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38884 body.push(tpl.body.replace(/(\r\n|\n)/g,
38885 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38886 body.push("'].join('');};};");
38887 body = body.join('');
38890 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38892 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38898 applyTemplate : function(values){
38899 return this.master.compiled.call(this, values, {});
38900 //var s = this.subs;
38903 apply : function(){
38904 return this.applyTemplate.apply(this, arguments);
38909 Roo.XTemplate.from = function(el){
38910 el = Roo.getDom(el);
38911 return new Roo.XTemplate(el.value || el.innerHTML);