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.Reader} 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
1276 * Base class for reading structured data from a data source. This class is intended to be
1277 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1280 Roo.data.DataReader = function(meta, recordType){
1284 this.recordType = recordType instanceof Array ?
1285 Roo.data.Record.create(recordType) : recordType;
1288 Roo.data.DataReader.prototype = {
1291 readerType : 'Data',
1293 * Create an empty record
1294 * @param {Object} data (optional) - overlay some values
1295 * @return {Roo.data.Record} record created.
1297 newRow : function(d) {
1299 this.recordType.prototype.fields.each(function(c) {
1301 case 'int' : da[c.name] = 0; break;
1302 case 'date' : da[c.name] = new Date(); break;
1303 case 'float' : da[c.name] = 0.0; break;
1304 case 'boolean' : da[c.name] = false; break;
1305 default : da[c.name] = ""; break;
1309 return new this.recordType(Roo.apply(da, d));
1315 * Ext JS Library 1.1.1
1316 * Copyright(c) 2006-2007, Ext JS, LLC.
1318 * Originally Released Under LGPL - original licence link has changed is not relivant.
1321 * <script type="text/javascript">
1325 * @class Roo.data.DataProxy
1326 * @extends Roo.data.Observable
1327 * This class is an abstract base class for implementations which provide retrieval of
1328 * unformatted data objects.<br>
1330 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1331 * (of the appropriate type which knows how to parse the data object) to provide a block of
1332 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1334 * Custom implementations must implement the load method as described in
1335 * {@link Roo.data.HttpProxy#load}.
1337 Roo.data.DataProxy = function(){
1341 * Fires before a network request is made to retrieve a data object.
1342 * @param {Object} This DataProxy object.
1343 * @param {Object} params The params parameter to the load function.
1348 * Fires before the load method's callback is called.
1349 * @param {Object} This DataProxy object.
1350 * @param {Object} o The data object.
1351 * @param {Object} arg The callback argument object passed to the load function.
1355 * @event loadexception
1356 * Fires if an Exception occurs during data retrieval.
1357 * @param {Object} This DataProxy object.
1358 * @param {Object} o The data object.
1359 * @param {Object} arg The callback argument object passed to the load function.
1360 * @param {Object} e The Exception.
1362 loadexception : true
1364 Roo.data.DataProxy.superclass.constructor.call(this);
1367 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1370 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1374 * Ext JS Library 1.1.1
1375 * Copyright(c) 2006-2007, Ext JS, LLC.
1377 * Originally Released Under LGPL - original licence link has changed is not relivant.
1380 * <script type="text/javascript">
1383 * @class Roo.data.MemoryProxy
1384 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1385 * to the Reader when its load method is called.
1387 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1389 Roo.data.MemoryProxy = function(data){
1393 Roo.data.MemoryProxy.superclass.constructor.call(this);
1397 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1400 * Load data from the requested source (in this case an in-memory
1401 * data object passed to the constructor), read the data object into
1402 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1403 * process that block using the passed callback.
1404 * @param {Object} params This parameter is not used by the MemoryProxy class.
1405 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1406 * object into a block of Roo.data.Records.
1407 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1408 * The function must be passed <ul>
1409 * <li>The Record block object</li>
1410 * <li>The "arg" argument from the load function</li>
1411 * <li>A boolean success indicator</li>
1413 * @param {Object} scope The scope in which to call the callback
1414 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1416 load : function(params, reader, callback, scope, arg){
1417 params = params || {};
1420 result = reader.readRecords(params.data ? params.data :this.data);
1422 this.fireEvent("loadexception", this, arg, null, e);
1423 callback.call(scope, null, arg, false);
1426 callback.call(scope, result, arg, true);
1430 update : function(params, records){
1435 * Ext JS Library 1.1.1
1436 * Copyright(c) 2006-2007, Ext JS, LLC.
1438 * Originally Released Under LGPL - original licence link has changed is not relivant.
1441 * <script type="text/javascript">
1444 * @class Roo.data.HttpProxy
1445 * @extends Roo.data.DataProxy
1446 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1447 * configured to reference a certain URL.<br><br>
1449 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1450 * from which the running page was served.<br><br>
1452 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1454 * Be aware that to enable the browser to parse an XML document, the server must set
1455 * the Content-Type header in the HTTP response to "text/xml".
1457 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1458 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1459 * will be used to make the request.
1461 Roo.data.HttpProxy = function(conn){
1462 Roo.data.HttpProxy.superclass.constructor.call(this);
1463 // is conn a conn config or a real conn?
1465 this.useAjax = !conn || !conn.events;
1469 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1470 // thse are take from connection...
1473 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1476 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1477 * extra parameters to each request made by this object. (defaults to undefined)
1480 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1481 * to each request made by this object. (defaults to undefined)
1484 * @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)
1487 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1490 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1496 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1500 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1501 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1502 * a finer-grained basis than the DataProxy events.
1504 getConnection : function(){
1505 return this.useAjax ? Roo.Ajax : this.conn;
1509 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1510 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1511 * process that block using the passed callback.
1512 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1513 * for the request to the remote server.
1514 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1515 * object into a block of Roo.data.Records.
1516 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1517 * The function must be passed <ul>
1518 * <li>The Record block object</li>
1519 * <li>The "arg" argument from the load function</li>
1520 * <li>A boolean success indicator</li>
1522 * @param {Object} scope The scope in which to call the callback
1523 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1525 load : function(params, reader, callback, scope, arg){
1526 if(this.fireEvent("beforeload", this, params) !== false){
1528 params : params || {},
1530 callback : callback,
1535 callback : this.loadResponse,
1539 Roo.applyIf(o, this.conn);
1540 if(this.activeRequest){
1541 Roo.Ajax.abort(this.activeRequest);
1543 this.activeRequest = Roo.Ajax.request(o);
1545 this.conn.request(o);
1548 callback.call(scope||this, null, arg, false);
1553 loadResponse : function(o, success, response){
1554 delete this.activeRequest;
1556 this.fireEvent("loadexception", this, o, response);
1557 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1562 result = o.reader.read(response);
1564 this.fireEvent("loadexception", this, o, response, e);
1565 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1569 this.fireEvent("load", this, o, o.request.arg);
1570 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1574 update : function(dataSet){
1579 updateResponse : function(dataSet){
1584 * Ext JS Library 1.1.1
1585 * Copyright(c) 2006-2007, Ext JS, LLC.
1587 * Originally Released Under LGPL - original licence link has changed is not relivant.
1590 * <script type="text/javascript">
1594 * @class Roo.data.ScriptTagProxy
1595 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1596 * other than the originating domain of the running page.<br><br>
1598 * <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
1599 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1601 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1602 * source code that is used as the source inside a <script> tag.<br><br>
1604 * In order for the browser to process the returned data, the server must wrap the data object
1605 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1606 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1607 * depending on whether the callback name was passed:
1610 boolean scriptTag = false;
1611 String cb = request.getParameter("callback");
1614 response.setContentType("text/javascript");
1616 response.setContentType("application/x-json");
1618 Writer out = response.getWriter();
1620 out.write(cb + "(");
1622 out.print(dataBlock.toJsonString());
1629 * @param {Object} config A configuration object.
1631 Roo.data.ScriptTagProxy = function(config){
1632 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1633 Roo.apply(this, config);
1634 this.head = document.getElementsByTagName("head")[0];
1637 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1639 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1641 * @cfg {String} url The URL from which to request the data object.
1644 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1648 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1649 * the server the name of the callback function set up by the load call to process the returned data object.
1650 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1651 * javascript output which calls this named function passing the data object as its only parameter.
1653 callbackParam : "callback",
1655 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1656 * name to the request.
1661 * Load data from the configured URL, read the data object into
1662 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1663 * process that block using the passed callback.
1664 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1665 * for the request to the remote server.
1666 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1667 * object into a block of Roo.data.Records.
1668 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1669 * The function must be passed <ul>
1670 * <li>The Record block object</li>
1671 * <li>The "arg" argument from the load function</li>
1672 * <li>A boolean success indicator</li>
1674 * @param {Object} scope The scope in which to call the callback
1675 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1677 load : function(params, reader, callback, scope, arg){
1678 if(this.fireEvent("beforeload", this, params) !== false){
1680 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1683 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1685 url += "&_dc=" + (new Date().getTime());
1687 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1690 cb : "stcCallback"+transId,
1691 scriptId : "stcScript"+transId,
1695 callback : callback,
1701 window[trans.cb] = function(o){
1702 conn.handleResponse(o, trans);
1705 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1707 if(this.autoAbort !== false){
1711 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1713 var script = document.createElement("script");
1714 script.setAttribute("src", url);
1715 script.setAttribute("type", "text/javascript");
1716 script.setAttribute("id", trans.scriptId);
1717 this.head.appendChild(script);
1721 callback.call(scope||this, null, arg, false);
1726 isLoading : function(){
1727 return this.trans ? true : false;
1731 * Abort the current server request.
1734 if(this.isLoading()){
1735 this.destroyTrans(this.trans);
1740 destroyTrans : function(trans, isLoaded){
1741 this.head.removeChild(document.getElementById(trans.scriptId));
1742 clearTimeout(trans.timeoutId);
1744 window[trans.cb] = undefined;
1746 delete window[trans.cb];
1749 // if hasn't been loaded, wait for load to remove it to prevent script error
1750 window[trans.cb] = function(){
1751 window[trans.cb] = undefined;
1753 delete window[trans.cb];
1760 handleResponse : function(o, trans){
1762 this.destroyTrans(trans, true);
1765 result = trans.reader.readRecords(o);
1767 this.fireEvent("loadexception", this, o, trans.arg, e);
1768 trans.callback.call(trans.scope||window, null, trans.arg, false);
1771 this.fireEvent("load", this, o, trans.arg);
1772 trans.callback.call(trans.scope||window, result, trans.arg, true);
1776 handleFailure : function(trans){
1778 this.destroyTrans(trans, false);
1779 this.fireEvent("loadexception", this, null, trans.arg);
1780 trans.callback.call(trans.scope||window, null, trans.arg, false);
1784 * Ext JS Library 1.1.1
1785 * Copyright(c) 2006-2007, Ext JS, LLC.
1787 * Originally Released Under LGPL - original licence link has changed is not relivant.
1790 * <script type="text/javascript">
1794 * @class Roo.data.JsonReader
1795 * @extends Roo.data.DataReader
1796 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1797 * based on mappings in a provided Roo.data.Record constructor.
1799 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1800 * in the reply previously.
1805 var RecordDef = Roo.data.Record.create([
1806 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1807 {name: 'occupation'} // This field will use "occupation" as the mapping.
1809 var myReader = new Roo.data.JsonReader({
1810 totalProperty: "results", // The property which contains the total dataset size (optional)
1811 root: "rows", // The property which contains an Array of row objects
1812 id: "id" // The property within each row object that provides an ID for the record (optional)
1816 * This would consume a JSON file like this:
1818 { 'results': 2, 'rows': [
1819 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1820 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1823 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1824 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1825 * paged from the remote server.
1826 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1827 * @cfg {String} root name of the property which contains the Array of row objects.
1828 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1829 * @cfg {Array} fields Array of field definition objects
1831 * Create a new JsonReader
1832 * @param {Object} meta Metadata configuration options
1833 * @param {Object} recordType Either an Array of field definition objects,
1834 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1836 Roo.data.JsonReader = function(meta, recordType){
1839 // set some defaults:
1841 totalProperty: 'total',
1842 successProperty : 'success',
1847 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1849 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1851 readerType : 'Json',
1854 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1855 * Used by Store query builder to append _requestMeta to params.
1858 metaFromRemote : false,
1860 * This method is only used by a DataProxy which has retrieved data from a remote server.
1861 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1862 * @return {Object} data A data block which is used by an Roo.data.Store object as
1863 * a cache of Roo.data.Records.
1865 read : function(response){
1866 var json = response.responseText;
1868 var o = /* eval:var:o */ eval("("+json+")");
1870 throw {message: "JsonReader.read: Json object not found"};
1876 this.metaFromRemote = true;
1877 this.meta = o.metaData;
1878 this.recordType = Roo.data.Record.create(o.metaData.fields);
1879 this.onMetaChange(this.meta, this.recordType, o);
1881 return this.readRecords(o);
1884 // private function a store will implement
1885 onMetaChange : function(meta, recordType, o){
1892 simpleAccess: function(obj, subsc) {
1899 getJsonAccessor: function(){
1901 return function(expr) {
1903 return(re.test(expr))
1904 ? new Function("obj", "return obj." + expr)
1914 * Create a data block containing Roo.data.Records from an XML document.
1915 * @param {Object} o An object which contains an Array of row objects in the property specified
1916 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1917 * which contains the total size of the dataset.
1918 * @return {Object} data A data block which is used by an Roo.data.Store object as
1919 * a cache of Roo.data.Records.
1921 readRecords : function(o){
1923 * After any data loads, the raw JSON data is available for further custom processing.
1927 var s = this.meta, Record = this.recordType,
1928 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1930 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1932 if(s.totalProperty) {
1933 this.getTotal = this.getJsonAccessor(s.totalProperty);
1935 if(s.successProperty) {
1936 this.getSuccess = this.getJsonAccessor(s.successProperty);
1938 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1940 var g = this.getJsonAccessor(s.id);
1941 this.getId = function(rec) {
1943 return (r === undefined || r === "") ? null : r;
1946 this.getId = function(){return null;};
1949 for(var jj = 0; jj < fl; jj++){
1951 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1952 this.ef[jj] = this.getJsonAccessor(map);
1956 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1957 if(s.totalProperty){
1958 var vt = parseInt(this.getTotal(o), 10);
1963 if(s.successProperty){
1964 var vs = this.getSuccess(o);
1965 if(vs === false || vs === 'false'){
1970 for(var i = 0; i < c; i++){
1973 var id = this.getId(n);
1974 for(var j = 0; j < fl; j++){
1976 var v = this.ef[j](n);
1978 Roo.log('missing convert for ' + f.name);
1982 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1984 var record = new Record(values, id);
1986 records[i] = record;
1992 totalRecords : totalRecords
1995 // used when loading children.. @see loadDataFromChildren
1996 toLoadData: function(rec)
1998 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1999 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2000 return { data : data, total : data.length };
2005 * Ext JS Library 1.1.1
2006 * Copyright(c) 2006-2007, Ext JS, LLC.
2008 * Originally Released Under LGPL - original licence link has changed is not relivant.
2011 * <script type="text/javascript">
2015 * @class Roo.data.XmlReader
2016 * @extends Roo.data.DataReader
2017 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2018 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2020 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2021 * header in the HTTP response must be set to "text/xml".</em>
2025 var RecordDef = Roo.data.Record.create([
2026 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2027 {name: 'occupation'} // This field will use "occupation" as the mapping.
2029 var myReader = new Roo.data.XmlReader({
2030 totalRecords: "results", // The element which contains the total dataset size (optional)
2031 record: "row", // The repeated element which contains row information
2032 id: "id" // The element within the row that provides an ID for the record (optional)
2036 * This would consume an XML file like this:
2040 <results>2</results>
2043 <name>Bill</name>
2044 <occupation>Gardener</occupation>
2048 <name>Ben</name>
2049 <occupation>Horticulturalist</occupation>
2053 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2054 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2055 * paged from the remote server.
2056 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2057 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2058 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2059 * a record identifier value.
2061 * Create a new XmlReader
2062 * @param {Object} meta Metadata configuration options
2063 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2064 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2065 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2067 Roo.data.XmlReader = function(meta, recordType){
2069 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2071 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2076 * This method is only used by a DataProxy which has retrieved data from a remote server.
2077 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2078 * to contain a method called 'responseXML' that returns an XML document object.
2079 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2080 * a cache of Roo.data.Records.
2082 read : function(response){
2083 var doc = response.responseXML;
2085 throw {message: "XmlReader.read: XML Document not available"};
2087 return this.readRecords(doc);
2091 * Create a data block containing Roo.data.Records from an XML document.
2092 * @param {Object} doc A parsed XML document.
2093 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2094 * a cache of Roo.data.Records.
2096 readRecords : function(doc){
2098 * After any data loads/reads, the raw XML Document is available for further custom processing.
2102 var root = doc.documentElement || doc;
2103 var q = Roo.DomQuery;
2104 var recordType = this.recordType, fields = recordType.prototype.fields;
2105 var sid = this.meta.id;
2106 var totalRecords = 0, success = true;
2107 if(this.meta.totalRecords){
2108 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2111 if(this.meta.success){
2112 var sv = q.selectValue(this.meta.success, root, true);
2113 success = sv !== false && sv !== 'false';
2116 var ns = q.select(this.meta.record, root);
2117 for(var i = 0, len = ns.length; i < len; i++) {
2120 var id = sid ? q.selectValue(sid, n) : undefined;
2121 for(var j = 0, jlen = fields.length; j < jlen; j++){
2122 var f = fields.items[j];
2123 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2127 var record = new recordType(values, id);
2129 records[records.length] = record;
2135 totalRecords : totalRecords || records.length
2140 * Ext JS Library 1.1.1
2141 * Copyright(c) 2006-2007, Ext JS, LLC.
2143 * Originally Released Under LGPL - original licence link has changed is not relivant.
2146 * <script type="text/javascript">
2150 * @class Roo.data.ArrayReader
2151 * @extends Roo.data.DataReader
2152 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2153 * Each element of that Array represents a row of data fields. The
2154 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2155 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2159 var RecordDef = Roo.data.Record.create([
2160 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2161 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2163 var myReader = new Roo.data.ArrayReader({
2164 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2168 * This would consume an Array like this:
2170 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2174 * Create a new JsonReader
2175 * @param {Object} meta Metadata configuration options.
2176 * @param {Object|Array} recordType Either an Array of field definition objects
2178 * @cfg {Array} fields Array of field definition objects
2179 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2180 * as specified to {@link Roo.data.Record#create},
2181 * or an {@link Roo.data.Record} object
2184 * created using {@link Roo.data.Record#create}.
2186 Roo.data.ArrayReader = function(meta, recordType)
2188 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2191 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2194 * Create a data block containing Roo.data.Records from an XML document.
2195 * @param {Object} o An Array of row objects which represents the dataset.
2196 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2197 * a cache of Roo.data.Records.
2199 readRecords : function(o)
2201 var sid = this.meta ? this.meta.id : null;
2202 var recordType = this.recordType, fields = recordType.prototype.fields;
2205 for(var i = 0; i < root.length; i++){
2208 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2209 for(var j = 0, jlen = fields.length; j < jlen; j++){
2210 var f = fields.items[j];
2211 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2212 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2216 var record = new recordType(values, id);
2218 records[records.length] = record;
2222 totalRecords : records.length
2225 // used when loading children.. @see loadDataFromChildren
2226 toLoadData: function(rec)
2228 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2229 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2236 * Ext JS Library 1.1.1
2237 * Copyright(c) 2006-2007, Ext JS, LLC.
2239 * Originally Released Under LGPL - original licence link has changed is not relivant.
2242 * <script type="text/javascript">
2247 * @class Roo.data.Tree
2248 * @extends Roo.util.Observable
2249 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2250 * in the tree have most standard DOM functionality.
2252 * @param {Node} root (optional) The root node
2254 Roo.data.Tree = function(root){
2257 * The root node for this tree
2262 this.setRootNode(root);
2267 * Fires when a new child node is appended to a node in this tree.
2268 * @param {Tree} tree The owner tree
2269 * @param {Node} parent The parent node
2270 * @param {Node} node The newly appended node
2271 * @param {Number} index The index of the newly appended node
2276 * Fires when a child node is removed from a node in this tree.
2277 * @param {Tree} tree The owner tree
2278 * @param {Node} parent The parent node
2279 * @param {Node} node The child node removed
2284 * Fires when a node is moved to a new location in the tree
2285 * @param {Tree} tree The owner tree
2286 * @param {Node} node The node moved
2287 * @param {Node} oldParent The old parent of this node
2288 * @param {Node} newParent The new parent of this node
2289 * @param {Number} index The index it was moved to
2294 * Fires when a new child node is inserted in a node in this tree.
2295 * @param {Tree} tree The owner tree
2296 * @param {Node} parent The parent node
2297 * @param {Node} node The child node inserted
2298 * @param {Node} refNode The child node the node was inserted before
2302 * @event beforeappend
2303 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2304 * @param {Tree} tree The owner tree
2305 * @param {Node} parent The parent node
2306 * @param {Node} node The child node to be appended
2308 "beforeappend" : true,
2310 * @event beforeremove
2311 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2312 * @param {Tree} tree The owner tree
2313 * @param {Node} parent The parent node
2314 * @param {Node} node The child node to be removed
2316 "beforeremove" : true,
2319 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2320 * @param {Tree} tree The owner tree
2321 * @param {Node} node The node being moved
2322 * @param {Node} oldParent The parent of the node
2323 * @param {Node} newParent The new parent the node is moving to
2324 * @param {Number} index The index it is being moved to
2326 "beforemove" : true,
2328 * @event beforeinsert
2329 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2330 * @param {Tree} tree The owner tree
2331 * @param {Node} parent The parent node
2332 * @param {Node} node The child node to be inserted
2333 * @param {Node} refNode The child node the node is being inserted before
2335 "beforeinsert" : true
2338 Roo.data.Tree.superclass.constructor.call(this);
2341 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2344 proxyNodeEvent : function(){
2345 return this.fireEvent.apply(this, arguments);
2349 * Returns the root node for this tree.
2352 getRootNode : function(){
2357 * Sets the root node for this tree.
2358 * @param {Node} node
2361 setRootNode : function(node){
2363 node.ownerTree = this;
2365 this.registerNode(node);
2370 * Gets a node in this tree by its id.
2371 * @param {String} id
2374 getNodeById : function(id){
2375 return this.nodeHash[id];
2378 registerNode : function(node){
2379 this.nodeHash[node.id] = node;
2382 unregisterNode : function(node){
2383 delete this.nodeHash[node.id];
2386 toString : function(){
2387 return "[Tree"+(this.id?" "+this.id:"")+"]";
2392 * @class Roo.data.Node
2393 * @extends Roo.util.Observable
2394 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2395 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2397 * @param {Object} attributes The attributes/config for the node
2399 Roo.data.Node = function(attributes){
2401 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2404 this.attributes = attributes || {};
2405 this.leaf = this.attributes.leaf;
2407 * The node id. @type String
2409 this.id = this.attributes.id;
2411 this.id = Roo.id(null, "ynode-");
2412 this.attributes.id = this.id;
2417 * All child nodes of this node. @type Array
2419 this.childNodes = [];
2420 if(!this.childNodes.indexOf){ // indexOf is a must
2421 this.childNodes.indexOf = function(o){
2422 for(var i = 0, len = this.length; i < len; i++){
2431 * The parent node for this node. @type Node
2433 this.parentNode = null;
2435 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2437 this.firstChild = null;
2439 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2441 this.lastChild = null;
2443 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2445 this.previousSibling = null;
2447 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2449 this.nextSibling = null;
2454 * Fires when a new child node is appended
2455 * @param {Tree} tree The owner tree
2456 * @param {Node} this This node
2457 * @param {Node} node The newly appended node
2458 * @param {Number} index The index of the newly appended node
2463 * Fires when a child node is removed
2464 * @param {Tree} tree The owner tree
2465 * @param {Node} this This node
2466 * @param {Node} node The removed node
2471 * Fires when this node is moved to a new location in the tree
2472 * @param {Tree} tree The owner tree
2473 * @param {Node} this This node
2474 * @param {Node} oldParent The old parent of this node
2475 * @param {Node} newParent The new parent of this node
2476 * @param {Number} index The index it was moved to
2481 * Fires when a new child node is inserted.
2482 * @param {Tree} tree The owner tree
2483 * @param {Node} this This node
2484 * @param {Node} node The child node inserted
2485 * @param {Node} refNode The child node the node was inserted before
2489 * @event beforeappend
2490 * Fires before a new child is appended, return false to cancel the append.
2491 * @param {Tree} tree The owner tree
2492 * @param {Node} this This node
2493 * @param {Node} node The child node to be appended
2495 "beforeappend" : true,
2497 * @event beforeremove
2498 * Fires before a child is removed, return false to cancel the remove.
2499 * @param {Tree} tree The owner tree
2500 * @param {Node} this This node
2501 * @param {Node} node The child node to be removed
2503 "beforeremove" : true,
2506 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2507 * @param {Tree} tree The owner tree
2508 * @param {Node} this This node
2509 * @param {Node} oldParent The parent of this node
2510 * @param {Node} newParent The new parent this node is moving to
2511 * @param {Number} index The index it is being moved to
2513 "beforemove" : true,
2515 * @event beforeinsert
2516 * Fires before a new child is inserted, return false to cancel the insert.
2517 * @param {Tree} tree The owner tree
2518 * @param {Node} this This node
2519 * @param {Node} node The child node to be inserted
2520 * @param {Node} refNode The child node the node is being inserted before
2522 "beforeinsert" : true
2524 this.listeners = this.attributes.listeners;
2525 Roo.data.Node.superclass.constructor.call(this);
2528 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2529 fireEvent : function(evtName){
2530 // first do standard event for this node
2531 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2534 // then bubble it up to the tree if the event wasn't cancelled
2535 var ot = this.getOwnerTree();
2537 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2545 * Returns true if this node is a leaf
2548 isLeaf : function(){
2549 return this.leaf === true;
2553 setFirstChild : function(node){
2554 this.firstChild = node;
2558 setLastChild : function(node){
2559 this.lastChild = node;
2564 * Returns true if this node is the last child of its parent
2567 isLast : function(){
2568 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2572 * Returns true if this node is the first child of its parent
2575 isFirst : function(){
2576 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2579 hasChildNodes : function(){
2580 return !this.isLeaf() && this.childNodes.length > 0;
2584 * Insert node(s) as the last child node of this node.
2585 * @param {Node/Array} node The node or Array of nodes to append
2586 * @return {Node} The appended node if single append, or null if an array was passed
2588 appendChild : function(node){
2590 if(node instanceof Array){
2592 }else if(arguments.length > 1){
2596 // if passed an array or multiple args do them one by one
2598 for(var i = 0, len = multi.length; i < len; i++) {
2599 this.appendChild(multi[i]);
2602 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2605 var index = this.childNodes.length;
2606 var oldParent = node.parentNode;
2607 // it's a move, make sure we move it cleanly
2609 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2612 oldParent.removeChild(node);
2615 index = this.childNodes.length;
2617 this.setFirstChild(node);
2619 this.childNodes.push(node);
2620 node.parentNode = this;
2621 var ps = this.childNodes[index-1];
2623 node.previousSibling = ps;
2624 ps.nextSibling = node;
2626 node.previousSibling = null;
2628 node.nextSibling = null;
2629 this.setLastChild(node);
2630 node.setOwnerTree(this.getOwnerTree());
2631 this.fireEvent("append", this.ownerTree, this, node, index);
2632 if(this.ownerTree) {
2633 this.ownerTree.fireEvent("appendnode", this, node, index);
2636 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2643 * Removes a child node from this node.
2644 * @param {Node} node The node to remove
2645 * @return {Node} The removed node
2647 removeChild : function(node){
2648 var index = this.childNodes.indexOf(node);
2652 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2656 // remove it from childNodes collection
2657 this.childNodes.splice(index, 1);
2660 if(node.previousSibling){
2661 node.previousSibling.nextSibling = node.nextSibling;
2663 if(node.nextSibling){
2664 node.nextSibling.previousSibling = node.previousSibling;
2667 // update child refs
2668 if(this.firstChild == node){
2669 this.setFirstChild(node.nextSibling);
2671 if(this.lastChild == node){
2672 this.setLastChild(node.previousSibling);
2675 node.setOwnerTree(null);
2676 // clear any references from the node
2677 node.parentNode = null;
2678 node.previousSibling = null;
2679 node.nextSibling = null;
2680 this.fireEvent("remove", this.ownerTree, this, node);
2685 * Inserts the first node before the second node in this nodes childNodes collection.
2686 * @param {Node} node The node to insert
2687 * @param {Node} refNode The node to insert before (if null the node is appended)
2688 * @return {Node} The inserted node
2690 insertBefore : function(node, refNode){
2691 if(!refNode){ // like standard Dom, refNode can be null for append
2692 return this.appendChild(node);
2695 if(node == refNode){
2699 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2702 var index = this.childNodes.indexOf(refNode);
2703 var oldParent = node.parentNode;
2704 var refIndex = index;
2706 // when moving internally, indexes will change after remove
2707 if(oldParent == this && this.childNodes.indexOf(node) < index){
2711 // it's a move, make sure we move it cleanly
2713 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2716 oldParent.removeChild(node);
2719 this.setFirstChild(node);
2721 this.childNodes.splice(refIndex, 0, node);
2722 node.parentNode = this;
2723 var ps = this.childNodes[refIndex-1];
2725 node.previousSibling = ps;
2726 ps.nextSibling = node;
2728 node.previousSibling = null;
2730 node.nextSibling = refNode;
2731 refNode.previousSibling = node;
2732 node.setOwnerTree(this.getOwnerTree());
2733 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2735 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2741 * Returns the child node at the specified index.
2742 * @param {Number} index
2745 item : function(index){
2746 return this.childNodes[index];
2750 * Replaces one child node in this node with another.
2751 * @param {Node} newChild The replacement node
2752 * @param {Node} oldChild The node to replace
2753 * @return {Node} The replaced node
2755 replaceChild : function(newChild, oldChild){
2756 this.insertBefore(newChild, oldChild);
2757 this.removeChild(oldChild);
2762 * Returns the index of a child node
2763 * @param {Node} node
2764 * @return {Number} The index of the node or -1 if it was not found
2766 indexOf : function(child){
2767 return this.childNodes.indexOf(child);
2771 * Returns the tree this node is in.
2774 getOwnerTree : function(){
2775 // if it doesn't have one, look for one
2776 if(!this.ownerTree){
2780 this.ownerTree = p.ownerTree;
2786 return this.ownerTree;
2790 * Returns depth of this node (the root node has a depth of 0)
2793 getDepth : function(){
2796 while(p.parentNode){
2804 setOwnerTree : function(tree){
2805 // if it's move, we need to update everyone
2806 if(tree != this.ownerTree){
2808 this.ownerTree.unregisterNode(this);
2810 this.ownerTree = tree;
2811 var cs = this.childNodes;
2812 for(var i = 0, len = cs.length; i < len; i++) {
2813 cs[i].setOwnerTree(tree);
2816 tree.registerNode(this);
2822 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2823 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2824 * @return {String} The path
2826 getPath : function(attr){
2827 attr = attr || "id";
2828 var p = this.parentNode;
2829 var b = [this.attributes[attr]];
2831 b.unshift(p.attributes[attr]);
2834 var sep = this.getOwnerTree().pathSeparator;
2835 return sep + b.join(sep);
2839 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2840 * function call will be the scope provided or the current node. The arguments to the function
2841 * will be the args provided or the current node. If the function returns false at any point,
2842 * the bubble is stopped.
2843 * @param {Function} fn The function to call
2844 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2845 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2847 bubble : function(fn, scope, args){
2850 if(fn.call(scope || p, args || p) === false){
2858 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2859 * function call will be the scope provided or the current node. The arguments to the function
2860 * will be the args provided or the current node. If the function returns false at any point,
2861 * the cascade is stopped on that branch.
2862 * @param {Function} fn The function to call
2863 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2864 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2866 cascade : function(fn, scope, args){
2867 if(fn.call(scope || this, args || this) !== false){
2868 var cs = this.childNodes;
2869 for(var i = 0, len = cs.length; i < len; i++) {
2870 cs[i].cascade(fn, scope, args);
2876 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2877 * function call will be the scope provided or the current node. The arguments to the function
2878 * will be the args provided or the current node. If the function returns false at any point,
2879 * the iteration stops.
2880 * @param {Function} fn The function to call
2881 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2882 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2884 eachChild : function(fn, scope, args){
2885 var cs = this.childNodes;
2886 for(var i = 0, len = cs.length; i < len; i++) {
2887 if(fn.call(scope || this, args || cs[i]) === false){
2894 * Finds the first child that has the attribute with the specified value.
2895 * @param {String} attribute The attribute name
2896 * @param {Mixed} value The value to search for
2897 * @return {Node} The found child or null if none was found
2899 findChild : function(attribute, value){
2900 var cs = this.childNodes;
2901 for(var i = 0, len = cs.length; i < len; i++) {
2902 if(cs[i].attributes[attribute] == value){
2910 * Finds the first child by a custom function. The child matches if the function passed
2912 * @param {Function} fn
2913 * @param {Object} scope (optional)
2914 * @return {Node} The found child or null if none was found
2916 findChildBy : function(fn, scope){
2917 var cs = this.childNodes;
2918 for(var i = 0, len = cs.length; i < len; i++) {
2919 if(fn.call(scope||cs[i], cs[i]) === true){
2927 * Sorts this nodes children using the supplied sort function
2928 * @param {Function} fn
2929 * @param {Object} scope (optional)
2931 sort : function(fn, scope){
2932 var cs = this.childNodes;
2933 var len = cs.length;
2935 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2937 for(var i = 0; i < len; i++){
2939 n.previousSibling = cs[i-1];
2940 n.nextSibling = cs[i+1];
2942 this.setFirstChild(n);
2945 this.setLastChild(n);
2952 * Returns true if this node is an ancestor (at any point) of the passed node.
2953 * @param {Node} node
2956 contains : function(node){
2957 return node.isAncestor(this);
2961 * Returns true if the passed node is an ancestor (at any point) of this node.
2962 * @param {Node} node
2965 isAncestor : function(node){
2966 var p = this.parentNode;
2976 toString : function(){
2977 return "[Node"+(this.id?" "+this.id:"")+"]";
2981 * Ext JS Library 1.1.1
2982 * Copyright(c) 2006-2007, Ext JS, LLC.
2984 * Originally Released Under LGPL - original licence link has changed is not relivant.
2987 * <script type="text/javascript">
2993 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
2994 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
2995 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2997 * Create a new Shadow
2998 * @param {Object} config The config object
3000 Roo.Shadow = function(config){
3001 Roo.apply(this, config);
3002 if(typeof this.mode != "string"){
3003 this.mode = this.defaultMode;
3005 var o = this.offset, a = {h: 0};
3006 var rad = Math.floor(this.offset/2);
3007 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3013 a.l -= this.offset + rad;
3014 a.t -= this.offset + rad;
3025 a.l -= (this.offset - rad);
3026 a.t -= this.offset + rad;
3028 a.w -= (this.offset - rad)*2;
3039 a.l -= (this.offset - rad);
3040 a.t -= (this.offset - rad);
3042 a.w -= (this.offset + rad + 1);
3043 a.h -= (this.offset + rad);
3052 Roo.Shadow.prototype = {
3054 * @cfg {String} mode
3055 * The shadow display mode. Supports the following options:<br />
3056 * sides: Shadow displays on both sides and bottom only<br />
3057 * frame: Shadow displays equally on all four sides<br />
3058 * drop: Traditional bottom-right drop shadow (default)
3062 * @cfg {String} offset
3063 * The number of pixels to offset the shadow from the element (defaults to 4)
3068 defaultMode: "drop",
3071 * Displays the shadow under the target element
3072 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3074 show : function(target){
3075 target = Roo.get(target);
3077 this.el = Roo.Shadow.Pool.pull();
3078 if(this.el.dom.nextSibling != target.dom){
3079 this.el.insertBefore(target);
3082 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3084 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3087 target.getLeft(true),
3088 target.getTop(true),
3092 this.el.dom.style.display = "block";
3096 * Returns true if the shadow is visible, else false
3098 isVisible : function(){
3099 return this.el ? true : false;
3103 * Direct alignment when values are already available. Show must be called at least once before
3104 * calling this method to ensure it is initialized.
3105 * @param {Number} left The target element left position
3106 * @param {Number} top The target element top position
3107 * @param {Number} width The target element width
3108 * @param {Number} height The target element height
3110 realign : function(l, t, w, h){
3114 var a = this.adjusts, d = this.el.dom, s = d.style;
3116 s.left = (l+a.l)+"px";
3117 s.top = (t+a.t)+"px";
3118 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3120 if(s.width != sws || s.height != shs){
3124 var cn = d.childNodes;
3125 var sww = Math.max(0, (sw-12))+"px";
3126 cn[0].childNodes[1].style.width = sww;
3127 cn[1].childNodes[1].style.width = sww;
3128 cn[2].childNodes[1].style.width = sww;
3129 cn[1].style.height = Math.max(0, (sh-12))+"px";
3139 this.el.dom.style.display = "none";
3140 Roo.Shadow.Pool.push(this.el);
3146 * Adjust the z-index of this shadow
3147 * @param {Number} zindex The new z-index
3149 setZIndex : function(z){
3152 this.el.setStyle("z-index", z);
3157 // Private utility class that manages the internal Shadow cache
3158 Roo.Shadow.Pool = function(){
3160 var markup = Roo.isIE ?
3161 '<div class="x-ie-shadow"></div>' :
3162 '<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>';
3167 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3168 sh.autoBoxAdjust = false;
3173 push : function(sh){
3179 * Ext JS Library 1.1.1
3180 * Copyright(c) 2006-2007, Ext JS, LLC.
3182 * Originally Released Under LGPL - original licence link has changed is not relivant.
3185 * <script type="text/javascript">
3190 * @class Roo.SplitBar
3191 * @extends Roo.util.Observable
3192 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3196 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3197 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3198 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3199 split.minSize = 100;
3200 split.maxSize = 600;
3201 split.animate = true;
3202 split.on('moved', splitterMoved);
3205 * Create a new SplitBar
3206 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3207 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3208 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3209 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3210 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3211 position of the SplitBar).
3213 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3216 this.el = Roo.get(dragElement, true);
3217 this.el.dom.unselectable = "on";
3219 this.resizingEl = Roo.get(resizingElement, true);
3223 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3224 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3227 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3230 * The minimum size of the resizing element. (Defaults to 0)
3236 * The maximum size of the resizing element. (Defaults to 2000)
3239 this.maxSize = 2000;
3242 * Whether to animate the transition to the new size
3245 this.animate = false;
3248 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3251 this.useShim = false;
3258 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3260 this.proxy = Roo.get(existingProxy).dom;
3263 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3266 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3269 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3272 this.dragSpecs = {};
3275 * @private The adapter to use to positon and resize elements
3277 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3278 this.adapter.init(this);
3280 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3282 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3283 this.el.addClass("x-splitbar-h");
3286 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3287 this.el.addClass("x-splitbar-v");
3293 * Fires when the splitter is moved (alias for {@link #event-moved})
3294 * @param {Roo.SplitBar} this
3295 * @param {Number} newSize the new width or height
3300 * Fires when the splitter is moved
3301 * @param {Roo.SplitBar} this
3302 * @param {Number} newSize the new width or height
3306 * @event beforeresize
3307 * Fires before the splitter is dragged
3308 * @param {Roo.SplitBar} this
3310 "beforeresize" : true,
3312 "beforeapply" : true
3315 Roo.util.Observable.call(this);
3318 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3319 onStartProxyDrag : function(x, y){
3320 this.fireEvent("beforeresize", this);
3322 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3324 o.enableDisplayMode("block");
3325 // all splitbars share the same overlay
3326 Roo.SplitBar.prototype.overlay = o;
3328 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3329 this.overlay.show();
3330 Roo.get(this.proxy).setDisplayed("block");
3331 var size = this.adapter.getElementSize(this);
3332 this.activeMinSize = this.getMinimumSize();;
3333 this.activeMaxSize = this.getMaximumSize();;
3334 var c1 = size - this.activeMinSize;
3335 var c2 = Math.max(this.activeMaxSize - size, 0);
3336 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3337 this.dd.resetConstraints();
3338 this.dd.setXConstraint(
3339 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3340 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3342 this.dd.setYConstraint(0, 0);
3344 this.dd.resetConstraints();
3345 this.dd.setXConstraint(0, 0);
3346 this.dd.setYConstraint(
3347 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3348 this.placement == Roo.SplitBar.TOP ? c2 : c1
3351 this.dragSpecs.startSize = size;
3352 this.dragSpecs.startPoint = [x, y];
3353 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3357 * @private Called after the drag operation by the DDProxy
3359 onEndProxyDrag : function(e){
3360 Roo.get(this.proxy).setDisplayed(false);
3361 var endPoint = Roo.lib.Event.getXY(e);
3363 this.overlay.hide();
3366 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3367 newSize = this.dragSpecs.startSize +
3368 (this.placement == Roo.SplitBar.LEFT ?
3369 endPoint[0] - this.dragSpecs.startPoint[0] :
3370 this.dragSpecs.startPoint[0] - endPoint[0]
3373 newSize = this.dragSpecs.startSize +
3374 (this.placement == Roo.SplitBar.TOP ?
3375 endPoint[1] - this.dragSpecs.startPoint[1] :
3376 this.dragSpecs.startPoint[1] - endPoint[1]
3379 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3380 if(newSize != this.dragSpecs.startSize){
3381 if(this.fireEvent('beforeapply', this, newSize) !== false){
3382 this.adapter.setElementSize(this, newSize);
3383 this.fireEvent("moved", this, newSize);
3384 this.fireEvent("resize", this, newSize);
3390 * Get the adapter this SplitBar uses
3391 * @return The adapter object
3393 getAdapter : function(){
3394 return this.adapter;
3398 * Set the adapter this SplitBar uses
3399 * @param {Object} adapter A SplitBar adapter object
3401 setAdapter : function(adapter){
3402 this.adapter = adapter;
3403 this.adapter.init(this);
3407 * Gets the minimum size for the resizing element
3408 * @return {Number} The minimum size
3410 getMinimumSize : function(){
3411 return this.minSize;
3415 * Sets the minimum size for the resizing element
3416 * @param {Number} minSize The minimum size
3418 setMinimumSize : function(minSize){
3419 this.minSize = minSize;
3423 * Gets the maximum size for the resizing element
3424 * @return {Number} The maximum size
3426 getMaximumSize : function(){
3427 return this.maxSize;
3431 * Sets the maximum size for the resizing element
3432 * @param {Number} maxSize The maximum size
3434 setMaximumSize : function(maxSize){
3435 this.maxSize = maxSize;
3439 * Sets the initialize size for the resizing element
3440 * @param {Number} size The initial size
3442 setCurrentSize : function(size){
3443 var oldAnimate = this.animate;
3444 this.animate = false;
3445 this.adapter.setElementSize(this, size);
3446 this.animate = oldAnimate;
3450 * Destroy this splitbar.
3451 * @param {Boolean} removeEl True to remove the element
3453 destroy : function(removeEl){
3458 this.proxy.parentNode.removeChild(this.proxy);
3466 * @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.
3468 Roo.SplitBar.createProxy = function(dir){
3469 var proxy = new Roo.Element(document.createElement("div"));
3470 proxy.unselectable();
3471 var cls = 'x-splitbar-proxy';
3472 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3473 document.body.appendChild(proxy.dom);
3478 * @class Roo.SplitBar.BasicLayoutAdapter
3479 * Default Adapter. It assumes the splitter and resizing element are not positioned
3480 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3482 Roo.SplitBar.BasicLayoutAdapter = function(){
3485 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3486 // do nothing for now
3491 * Called before drag operations to get the current size of the resizing element.
3492 * @param {Roo.SplitBar} s The SplitBar using this adapter
3494 getElementSize : function(s){
3495 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3496 return s.resizingEl.getWidth();
3498 return s.resizingEl.getHeight();
3503 * Called after drag operations to set the size of the resizing element.
3504 * @param {Roo.SplitBar} s The SplitBar using this adapter
3505 * @param {Number} newSize The new size to set
3506 * @param {Function} onComplete A function to be invoked when resizing is complete
3508 setElementSize : function(s, newSize, onComplete){
3509 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3511 s.resizingEl.setWidth(newSize);
3513 onComplete(s, newSize);
3516 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3521 s.resizingEl.setHeight(newSize);
3523 onComplete(s, newSize);
3526 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3533 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3534 * @extends Roo.SplitBar.BasicLayoutAdapter
3535 * Adapter that moves the splitter element to align with the resized sizing element.
3536 * Used with an absolute positioned SplitBar.
3537 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3538 * document.body, make sure you assign an id to the body element.
3540 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3541 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3542 this.container = Roo.get(container);
3545 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3550 getElementSize : function(s){
3551 return this.basic.getElementSize(s);
3554 setElementSize : function(s, newSize, onComplete){
3555 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3558 moveSplitter : function(s){
3559 var yes = Roo.SplitBar;
3560 switch(s.placement){
3562 s.el.setX(s.resizingEl.getRight());
3565 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3568 s.el.setY(s.resizingEl.getBottom());
3571 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3578 * Orientation constant - Create a vertical SplitBar
3582 Roo.SplitBar.VERTICAL = 1;
3585 * Orientation constant - Create a horizontal SplitBar
3589 Roo.SplitBar.HORIZONTAL = 2;
3592 * Placement constant - The resizing element is to the left of the splitter element
3596 Roo.SplitBar.LEFT = 1;
3599 * Placement constant - The resizing element is to the right of the splitter element
3603 Roo.SplitBar.RIGHT = 2;
3606 * Placement constant - The resizing element is positioned above the splitter element
3610 Roo.SplitBar.TOP = 3;
3613 * Placement constant - The resizing element is positioned under splitter element
3617 Roo.SplitBar.BOTTOM = 4;
3620 * Ext JS Library 1.1.1
3621 * Copyright(c) 2006-2007, Ext JS, LLC.
3623 * Originally Released Under LGPL - original licence link has changed is not relivant.
3626 * <script type="text/javascript">
3631 * @extends Roo.util.Observable
3632 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
3633 * This class also supports single and multi selection modes. <br>
3634 * Create a data model bound view:
3636 var store = new Roo.data.Store(...);
3638 var view = new Roo.View({
3640 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
3643 selectedClass: "ydataview-selected",
3647 // listen for node click?
3648 view.on("click", function(vw, index, node, e){
3649 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3653 dataModel.load("foobar.xml");
3655 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3657 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3658 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3660 * Note: old style constructor is still suported (container, template, config)
3664 * @param {Object} config The config object
3667 Roo.View = function(config, depreciated_tpl, depreciated_config){
3669 this.parent = false;
3671 if (typeof(depreciated_tpl) == 'undefined') {
3672 // new way.. - universal constructor.
3673 Roo.apply(this, config);
3674 this.el = Roo.get(this.el);
3677 this.el = Roo.get(config);
3678 this.tpl = depreciated_tpl;
3679 Roo.apply(this, depreciated_config);
3681 this.wrapEl = this.el.wrap().wrap();
3682 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3685 if(typeof(this.tpl) == "string"){
3686 this.tpl = new Roo.Template(this.tpl);
3688 // support xtype ctors..
3689 this.tpl = new Roo.factory(this.tpl, Roo);
3698 * @event beforeclick
3699 * Fires before a click is processed. Returns false to cancel the default action.
3700 * @param {Roo.View} this
3701 * @param {Number} index The index of the target node
3702 * @param {HTMLElement} node The target node
3703 * @param {Roo.EventObject} e The raw event object
3705 "beforeclick" : true,
3708 * Fires when a template node is clicked.
3709 * @param {Roo.View} this
3710 * @param {Number} index The index of the target node
3711 * @param {HTMLElement} node The target node
3712 * @param {Roo.EventObject} e The raw event object
3717 * Fires when a template node is double clicked.
3718 * @param {Roo.View} this
3719 * @param {Number} index The index of the target node
3720 * @param {HTMLElement} node The target node
3721 * @param {Roo.EventObject} e The raw event object
3725 * @event contextmenu
3726 * Fires when a template node is right clicked.
3727 * @param {Roo.View} this
3728 * @param {Number} index The index of the target node
3729 * @param {HTMLElement} node The target node
3730 * @param {Roo.EventObject} e The raw event object
3732 "contextmenu" : true,
3734 * @event selectionchange
3735 * Fires when the selected nodes change.
3736 * @param {Roo.View} this
3737 * @param {Array} selections Array of the selected nodes
3739 "selectionchange" : true,
3742 * @event beforeselect
3743 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3744 * @param {Roo.View} this
3745 * @param {HTMLElement} node The node to be selected
3746 * @param {Array} selections Array of currently selected nodes
3748 "beforeselect" : true,
3750 * @event preparedata
3751 * Fires on every row to render, to allow you to change the data.
3752 * @param {Roo.View} this
3753 * @param {Object} data to be rendered (change this)
3755 "preparedata" : true
3763 "click": this.onClick,
3764 "dblclick": this.onDblClick,
3765 "contextmenu": this.onContextMenu,
3769 this.selections = [];
3771 this.cmp = new Roo.CompositeElementLite([]);
3773 this.store = Roo.factory(this.store, Roo.data);
3774 this.setStore(this.store, true);
3777 if ( this.footer && this.footer.xtype) {
3779 var fctr = this.wrapEl.appendChild(document.createElement("div"));
3781 this.footer.dataSource = this.store;
3782 this.footer.container = fctr;
3783 this.footer = Roo.factory(this.footer, Roo);
3784 fctr.insertFirst(this.el);
3786 // this is a bit insane - as the paging toolbar seems to detach the el..
3787 // dom.parentNode.parentNode.parentNode
3788 // they get detached?
3792 Roo.View.superclass.constructor.call(this);
3797 Roo.extend(Roo.View, Roo.util.Observable, {
3800 * @cfg {Roo.data.Store} store Data store to load data from.
3805 * @cfg {String|Roo.Element} el The container element.
3810 * @cfg {String|Roo.Template} tpl The template used by this View
3814 * @cfg {String} dataName the named area of the template to use as the data area
3815 * Works with domtemplates roo-name="name"
3819 * @cfg {String} selectedClass The css class to add to selected nodes
3821 selectedClass : "x-view-selected",
3823 * @cfg {String} emptyText The empty text to show when nothing is loaded.
3828 * @cfg {String} text to display on mask (default Loading)
3832 * @cfg {Boolean} multiSelect Allow multiple selection
3834 multiSelect : false,
3836 * @cfg {Boolean} singleSelect Allow single selection
3838 singleSelect: false,
3841 * @cfg {Boolean} toggleSelect - selecting
3843 toggleSelect : false,
3846 * @cfg {Boolean} tickable - selecting
3851 * Returns the element this view is bound to.
3852 * @return {Roo.Element}
3861 * Refreshes the view. - called by datachanged on the store. - do not call directly.
3863 refresh : function(){
3864 //Roo.log('refresh');
3867 // if we are using something like 'domtemplate', then
3868 // the what gets used is:
3869 // t.applySubtemplate(NAME, data, wrapping data..)
3870 // the outer template then get' applied with
3871 // the store 'extra data'
3872 // and the body get's added to the
3873 // roo-name="data" node?
3874 // <span class='roo-tpl-{name}'></span> ?????
3878 this.clearSelections();
3881 var records = this.store.getRange();
3882 if(records.length < 1) {
3884 // is this valid?? = should it render a template??
3886 this.el.update(this.emptyText);
3890 if (this.dataName) {
3891 this.el.update(t.apply(this.store.meta)); //????
3892 el = this.el.child('.roo-tpl-' + this.dataName);
3895 for(var i = 0, len = records.length; i < len; i++){
3896 var data = this.prepareData(records[i].data, i, records[i]);
3897 this.fireEvent("preparedata", this, data, i, records[i]);
3899 var d = Roo.apply({}, data);
3902 Roo.apply(d, {'roo-id' : Roo.id()});
3906 Roo.each(this.parent.item, function(item){
3907 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3910 Roo.apply(d, {'roo-data-checked' : 'checked'});
3914 html[html.length] = Roo.util.Format.trim(
3916 t.applySubtemplate(this.dataName, d, this.store.meta) :
3923 el.update(html.join(""));
3924 this.nodes = el.dom.childNodes;
3925 this.updateIndexes(0);
3930 * Function to override to reformat the data that is sent to
3931 * the template for each node.
3932 * DEPRICATED - use the preparedata event handler.
3933 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3934 * a JSON object for an UpdateManager bound view).
3936 prepareData : function(data, index, record)
3938 this.fireEvent("preparedata", this, data, index, record);
3942 onUpdate : function(ds, record){
3943 // Roo.log('on update');
3944 this.clearSelections();
3945 var index = this.store.indexOf(record);
3946 var n = this.nodes[index];
3947 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3948 n.parentNode.removeChild(n);
3949 this.updateIndexes(index, index);
3955 onAdd : function(ds, records, index)
3957 //Roo.log(['on Add', ds, records, index] );
3958 this.clearSelections();
3959 if(this.nodes.length == 0){
3963 var n = this.nodes[index];
3964 for(var i = 0, len = records.length; i < len; i++){
3965 var d = this.prepareData(records[i].data, i, records[i]);
3967 this.tpl.insertBefore(n, d);
3970 this.tpl.append(this.el, d);
3973 this.updateIndexes(index);
3976 onRemove : function(ds, record, index){
3977 // Roo.log('onRemove');
3978 this.clearSelections();
3979 var el = this.dataName ?
3980 this.el.child('.roo-tpl-' + this.dataName) :
3983 el.dom.removeChild(this.nodes[index]);
3984 this.updateIndexes(index);
3988 * Refresh an individual node.
3989 * @param {Number} index
3991 refreshNode : function(index){
3992 this.onUpdate(this.store, this.store.getAt(index));
3995 updateIndexes : function(startIndex, endIndex){
3996 var ns = this.nodes;
3997 startIndex = startIndex || 0;
3998 endIndex = endIndex || ns.length - 1;
3999 for(var i = startIndex; i <= endIndex; i++){
4000 ns[i].nodeIndex = i;
4005 * Changes the data store this view uses and refresh the view.
4006 * @param {Store} store
4008 setStore : function(store, initial){
4009 if(!initial && this.store){
4010 this.store.un("datachanged", this.refresh);
4011 this.store.un("add", this.onAdd);
4012 this.store.un("remove", this.onRemove);
4013 this.store.un("update", this.onUpdate);
4014 this.store.un("clear", this.refresh);
4015 this.store.un("beforeload", this.onBeforeLoad);
4016 this.store.un("load", this.onLoad);
4017 this.store.un("loadexception", this.onLoad);
4021 store.on("datachanged", this.refresh, this);
4022 store.on("add", this.onAdd, this);
4023 store.on("remove", this.onRemove, this);
4024 store.on("update", this.onUpdate, this);
4025 store.on("clear", this.refresh, this);
4026 store.on("beforeload", this.onBeforeLoad, this);
4027 store.on("load", this.onLoad, this);
4028 store.on("loadexception", this.onLoad, this);
4036 * onbeforeLoad - masks the loading area.
4039 onBeforeLoad : function(store,opts)
4041 //Roo.log('onBeforeLoad');
4045 this.el.mask(this.mask ? this.mask : "Loading" );
4047 onLoad : function ()
4054 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4055 * @param {HTMLElement} node
4056 * @return {HTMLElement} The template node
4058 findItemFromChild : function(node){
4059 var el = this.dataName ?
4060 this.el.child('.roo-tpl-' + this.dataName,true) :
4063 if(!node || node.parentNode == el){
4066 var p = node.parentNode;
4067 while(p && p != el){
4068 if(p.parentNode == el){
4077 onClick : function(e){
4078 var item = this.findItemFromChild(e.getTarget());
4080 var index = this.indexOf(item);
4081 if(this.onItemClick(item, index, e) !== false){
4082 this.fireEvent("click", this, index, item, e);
4085 this.clearSelections();
4090 onContextMenu : function(e){
4091 var item = this.findItemFromChild(e.getTarget());
4093 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4098 onDblClick : function(e){
4099 var item = this.findItemFromChild(e.getTarget());
4101 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4105 onItemClick : function(item, index, e)
4107 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4110 if (this.toggleSelect) {
4111 var m = this.isSelected(item) ? 'unselect' : 'select';
4114 _t[m](item, true, false);
4117 if(this.multiSelect || this.singleSelect){
4118 if(this.multiSelect && e.shiftKey && this.lastSelection){
4119 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4121 this.select(item, this.multiSelect && e.ctrlKey);
4122 this.lastSelection = item;
4134 * Get the number of selected nodes.
4137 getSelectionCount : function(){
4138 return this.selections.length;
4142 * Get the currently selected nodes.
4143 * @return {Array} An array of HTMLElements
4145 getSelectedNodes : function(){
4146 return this.selections;
4150 * Get the indexes of the selected nodes.
4153 getSelectedIndexes : function(){
4154 var indexes = [], s = this.selections;
4155 for(var i = 0, len = s.length; i < len; i++){
4156 indexes.push(s[i].nodeIndex);
4162 * Clear all selections
4163 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4165 clearSelections : function(suppressEvent){
4166 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4167 this.cmp.elements = this.selections;
4168 this.cmp.removeClass(this.selectedClass);
4169 this.selections = [];
4171 this.fireEvent("selectionchange", this, this.selections);
4177 * Returns true if the passed node is selected
4178 * @param {HTMLElement/Number} node The node or node index
4181 isSelected : function(node){
4182 var s = this.selections;
4186 node = this.getNode(node);
4187 return s.indexOf(node) !== -1;
4192 * @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
4193 * @param {Boolean} keepExisting (optional) true to keep existing selections
4194 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4196 select : function(nodeInfo, keepExisting, suppressEvent){
4197 if(nodeInfo instanceof Array){
4199 this.clearSelections(true);
4201 for(var i = 0, len = nodeInfo.length; i < len; i++){
4202 this.select(nodeInfo[i], true, true);
4206 var node = this.getNode(nodeInfo);
4207 if(!node || this.isSelected(node)){
4208 return; // already selected.
4211 this.clearSelections(true);
4214 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4215 Roo.fly(node).addClass(this.selectedClass);
4216 this.selections.push(node);
4218 this.fireEvent("selectionchange", this, this.selections);
4226 * @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
4227 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4228 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4230 unselect : function(nodeInfo, keepExisting, suppressEvent)
4232 if(nodeInfo instanceof Array){
4233 Roo.each(this.selections, function(s) {
4234 this.unselect(s, nodeInfo);
4238 var node = this.getNode(nodeInfo);
4239 if(!node || !this.isSelected(node)){
4240 //Roo.log("not selected");
4241 return; // not selected.
4245 Roo.each(this.selections, function(s) {
4247 Roo.fly(node).removeClass(this.selectedClass);
4254 this.selections= ns;
4255 this.fireEvent("selectionchange", this, this.selections);
4259 * Gets a template node.
4260 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4261 * @return {HTMLElement} The node or null if it wasn't found
4263 getNode : function(nodeInfo){
4264 if(typeof nodeInfo == "string"){
4265 return document.getElementById(nodeInfo);
4266 }else if(typeof nodeInfo == "number"){
4267 return this.nodes[nodeInfo];
4273 * Gets a range template nodes.
4274 * @param {Number} startIndex
4275 * @param {Number} endIndex
4276 * @return {Array} An array of nodes
4278 getNodes : function(start, end){
4279 var ns = this.nodes;
4281 end = typeof end == "undefined" ? ns.length - 1 : end;
4284 for(var i = start; i <= end; i++){
4288 for(var i = start; i >= end; i--){
4296 * Finds the index of the passed node
4297 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4298 * @return {Number} The index of the node or -1
4300 indexOf : function(node){
4301 node = this.getNode(node);
4302 if(typeof node.nodeIndex == "number"){
4303 return node.nodeIndex;
4305 var ns = this.nodes;
4306 for(var i = 0, len = ns.length; i < len; i++){
4316 * Ext JS Library 1.1.1
4317 * Copyright(c) 2006-2007, Ext JS, LLC.
4319 * Originally Released Under LGPL - original licence link has changed is not relivant.
4322 * <script type="text/javascript">
4326 * @class Roo.JsonView
4328 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4330 var view = new Roo.JsonView({
4331 container: "my-element",
4332 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4337 // listen for node click?
4338 view.on("click", function(vw, index, node, e){
4339 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4342 // direct load of JSON data
4343 view.load("foobar.php");
4345 // Example from my blog list
4346 var tpl = new Roo.Template(
4347 '<div class="entry">' +
4348 '<a class="entry-title" href="{link}">{title}</a>' +
4349 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4350 "</div><hr />"
4353 var moreView = new Roo.JsonView({
4354 container : "entry-list",
4358 moreView.on("beforerender", this.sortEntries, this);
4360 url: "/blog/get-posts.php",
4361 params: "allposts=true",
4362 text: "Loading Blog Entries..."
4366 * Note: old code is supported with arguments : (container, template, config)
4370 * Create a new JsonView
4372 * @param {Object} config The config object
4375 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4378 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4380 var um = this.el.getUpdateManager();
4381 um.setRenderer(this);
4382 um.on("update", this.onLoad, this);
4383 um.on("failure", this.onLoadException, this);
4386 * @event beforerender
4387 * Fires before rendering of the downloaded JSON data.
4388 * @param {Roo.JsonView} this
4389 * @param {Object} data The JSON data loaded
4393 * Fires when data is loaded.
4394 * @param {Roo.JsonView} this
4395 * @param {Object} data The JSON data loaded
4396 * @param {Object} response The raw Connect response object
4399 * @event loadexception
4400 * Fires when loading fails.
4401 * @param {Roo.JsonView} this
4402 * @param {Object} response The raw Connect response object
4405 'beforerender' : true,
4407 'loadexception' : true
4410 Roo.extend(Roo.JsonView, Roo.View, {
4412 * @type {String} The root property in the loaded JSON object that contains the data
4417 * Refreshes the view.
4419 refresh : function(){
4420 this.clearSelections();
4423 var o = this.jsonData;
4424 if(o && o.length > 0){
4425 for(var i = 0, len = o.length; i < len; i++){
4426 var data = this.prepareData(o[i], i, o);
4427 html[html.length] = this.tpl.apply(data);
4430 html.push(this.emptyText);
4432 this.el.update(html.join(""));
4433 this.nodes = this.el.dom.childNodes;
4434 this.updateIndexes(0);
4438 * 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.
4439 * @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:
4442 url: "your-url.php",
4443 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4444 callback: yourFunction,
4445 scope: yourObject, //(optional scope)
4453 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4454 * 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.
4455 * @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}
4456 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4457 * @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.
4460 var um = this.el.getUpdateManager();
4461 um.update.apply(um, arguments);
4464 // note - render is a standard framework call...
4465 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4466 render : function(el, response){
4468 this.clearSelections();
4472 if (response != '') {
4473 o = Roo.util.JSON.decode(response.responseText);
4476 o = o[this.jsonRoot];
4482 * The current JSON data or null
4485 this.beforeRender();
4490 * Get the number of records in the current JSON dataset
4493 getCount : function(){
4494 return this.jsonData ? this.jsonData.length : 0;
4498 * Returns the JSON object for the specified node(s)
4499 * @param {HTMLElement/Array} node The node or an array of nodes
4500 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4501 * you get the JSON object for the node
4503 getNodeData : function(node){
4504 if(node instanceof Array){
4506 for(var i = 0, len = node.length; i < len; i++){
4507 data.push(this.getNodeData(node[i]));
4511 return this.jsonData[this.indexOf(node)] || null;
4514 beforeRender : function(){
4515 this.snapshot = this.jsonData;
4517 this.sort.apply(this, this.sortInfo);
4519 this.fireEvent("beforerender", this, this.jsonData);
4522 onLoad : function(el, o){
4523 this.fireEvent("load", this, this.jsonData, o);
4526 onLoadException : function(el, o){
4527 this.fireEvent("loadexception", this, o);
4531 * Filter the data by a specific property.
4532 * @param {String} property A property on your JSON objects
4533 * @param {String/RegExp} value Either string that the property values
4534 * should start with, or a RegExp to test against the property
4536 filter : function(property, value){
4539 var ss = this.snapshot;
4540 if(typeof value == "string"){
4541 var vlen = value.length;
4546 value = value.toLowerCase();
4547 for(var i = 0, len = ss.length; i < len; i++){
4549 if(o[property].substr(0, vlen).toLowerCase() == value){
4553 } else if(value.exec){ // regex?
4554 for(var i = 0, len = ss.length; i < len; i++){
4556 if(value.test(o[property])){
4563 this.jsonData = data;
4569 * Filter by a function. The passed function will be called with each
4570 * object in the current dataset. If the function returns true the value is kept,
4571 * otherwise it is filtered.
4572 * @param {Function} fn
4573 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4575 filterBy : function(fn, scope){
4578 var ss = this.snapshot;
4579 for(var i = 0, len = ss.length; i < len; i++){
4581 if(fn.call(scope || this, o)){
4585 this.jsonData = data;
4591 * Clears the current filter.
4593 clearFilter : function(){
4594 if(this.snapshot && this.jsonData != this.snapshot){
4595 this.jsonData = this.snapshot;
4602 * Sorts the data for this view and refreshes it.
4603 * @param {String} property A property on your JSON objects to sort on
4604 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4605 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4607 sort : function(property, dir, sortType){
4608 this.sortInfo = Array.prototype.slice.call(arguments, 0);
4611 var dsc = dir && dir.toLowerCase() == "desc";
4612 var f = function(o1, o2){
4613 var v1 = sortType ? sortType(o1[p]) : o1[p];
4614 var v2 = sortType ? sortType(o2[p]) : o2[p];
4617 return dsc ? +1 : -1;
4619 return dsc ? -1 : +1;
4624 this.jsonData.sort(f);
4626 if(this.jsonData != this.snapshot){
4627 this.snapshot.sort(f);
4633 * Ext JS Library 1.1.1
4634 * Copyright(c) 2006-2007, Ext JS, LLC.
4636 * Originally Released Under LGPL - original licence link has changed is not relivant.
4639 * <script type="text/javascript">
4644 * @class Roo.ColorPalette
4645 * @extends Roo.Component
4646 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
4647 * Here's an example of typical usage:
4649 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
4650 cp.render('my-div');
4652 cp.on('select', function(palette, selColor){
4653 // do something with selColor
4657 * Create a new ColorPalette
4658 * @param {Object} config The config object
4660 Roo.ColorPalette = function(config){
4661 Roo.ColorPalette.superclass.constructor.call(this, config);
4665 * Fires when a color is selected
4666 * @param {ColorPalette} this
4667 * @param {String} color The 6-digit color hex code (without the # symbol)
4673 this.on("select", this.handler, this.scope, true);
4676 Roo.extend(Roo.ColorPalette, Roo.Component, {
4678 * @cfg {String} itemCls
4679 * The CSS class to apply to the containing element (defaults to "x-color-palette")
4681 itemCls : "x-color-palette",
4683 * @cfg {String} value
4684 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
4685 * the hex codes are case-sensitive.
4690 ctype: "Roo.ColorPalette",
4693 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4695 allowReselect : false,
4698 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
4699 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
4700 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4701 * of colors with the width setting until the box is symmetrical.</p>
4702 * <p>You can override individual colors if needed:</p>
4704 var cp = new Roo.ColorPalette();
4705 cp.colors[0] = "FF0000"; // change the first box to red
4708 Or you can provide a custom array of your own for complete control:
4710 var cp = new Roo.ColorPalette();
4711 cp.colors = ["000000", "993300", "333300"];
4716 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4717 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4718 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4719 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4720 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4724 onRender : function(container, position){
4725 var t = new Roo.MasterTemplate(
4726 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
4728 var c = this.colors;
4729 for(var i = 0, len = c.length; i < len; i++){
4732 var el = document.createElement("div");
4733 el.className = this.itemCls;
4735 container.dom.insertBefore(el, position);
4736 this.el = Roo.get(el);
4737 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
4738 if(this.clickEvent != 'click'){
4739 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
4744 afterRender : function(){
4745 Roo.ColorPalette.superclass.afterRender.call(this);
4754 handleClick : function(e, t){
4757 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4758 this.select(c.toUpperCase());
4763 * Selects the specified color in the palette (fires the select event)
4764 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4766 select : function(color){
4767 color = color.replace("#", "");
4768 if(color != this.value || this.allowReselect){
4771 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4773 el.child("a.color-"+color).addClass("x-color-palette-sel");
4775 this.fireEvent("select", this, color);
4780 * Ext JS Library 1.1.1
4781 * Copyright(c) 2006-2007, Ext JS, LLC.
4783 * Originally Released Under LGPL - original licence link has changed is not relivant.
4786 * <script type="text/javascript">
4790 * @class Roo.DatePicker
4791 * @extends Roo.Component
4792 * Simple date picker class.
4794 * Create a new DatePicker
4795 * @param {Object} config The config object
4797 Roo.DatePicker = function(config){
4798 Roo.DatePicker.superclass.constructor.call(this, config);
4800 this.value = config && config.value ?
4801 config.value.clearTime() : new Date().clearTime();
4806 * Fires when a date is selected
4807 * @param {DatePicker} this
4808 * @param {Date} date The selected date
4812 * @event monthchange
4813 * Fires when the displayed month changes
4814 * @param {DatePicker} this
4815 * @param {Date} date The selected month
4821 this.on("select", this.handler, this.scope || this);
4823 // build the disabledDatesRE
4824 if(!this.disabledDatesRE && this.disabledDates){
4825 var dd = this.disabledDates;
4827 for(var i = 0; i < dd.length; i++){
4829 if(i != dd.length-1) {
4833 this.disabledDatesRE = new RegExp(re + ")");
4837 Roo.extend(Roo.DatePicker, Roo.Component, {
4839 * @cfg {String} todayText
4840 * The text to display on the button that selects the current date (defaults to "Today")
4842 todayText : "Today",
4844 * @cfg {String} okText
4845 * The text to display on the ok button
4847 okText : " OK ", //   to give the user extra clicking room
4849 * @cfg {String} cancelText
4850 * The text to display on the cancel button
4852 cancelText : "Cancel",
4854 * @cfg {String} todayTip
4855 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4857 todayTip : "{0} (Spacebar)",
4859 * @cfg {Date} minDate
4860 * Minimum allowable date (JavaScript date object, defaults to null)
4864 * @cfg {Date} maxDate
4865 * Maximum allowable date (JavaScript date object, defaults to null)
4869 * @cfg {String} minText
4870 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4872 minText : "This date is before the minimum date",
4874 * @cfg {String} maxText
4875 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4877 maxText : "This date is after the maximum date",
4879 * @cfg {String} format
4880 * The default date format string which can be overriden for localization support. The format must be
4881 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4885 * @cfg {Array} disabledDays
4886 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4888 disabledDays : null,
4890 * @cfg {String} disabledDaysText
4891 * The tooltip to display when the date falls on a disabled day (defaults to "")
4893 disabledDaysText : "",
4895 * @cfg {RegExp} disabledDatesRE
4896 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4898 disabledDatesRE : null,
4900 * @cfg {String} disabledDatesText
4901 * The tooltip text to display when the date falls on a disabled date (defaults to "")
4903 disabledDatesText : "",
4905 * @cfg {Boolean} constrainToViewport
4906 * True to constrain the date picker to the viewport (defaults to true)
4908 constrainToViewport : true,
4910 * @cfg {Array} monthNames
4911 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4913 monthNames : Date.monthNames,
4915 * @cfg {Array} dayNames
4916 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4918 dayNames : Date.dayNames,
4920 * @cfg {String} nextText
4921 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4923 nextText: 'Next Month (Control+Right)',
4925 * @cfg {String} prevText
4926 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4928 prevText: 'Previous Month (Control+Left)',
4930 * @cfg {String} monthYearText
4931 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4933 monthYearText: 'Choose a month (Control+Up/Down to move years)',
4935 * @cfg {Number} startDay
4936 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4940 * @cfg {Bool} showClear
4941 * Show a clear button (usefull for date form elements that can be blank.)
4947 * Sets the value of the date field
4948 * @param {Date} value The date to set
4950 setValue : function(value){
4951 var old = this.value;
4953 if (typeof(value) == 'string') {
4955 value = Date.parseDate(value, this.format);
4961 this.value = value.clearTime(true);
4963 this.update(this.value);
4968 * Gets the current selected value of the date field
4969 * @return {Date} The selected date
4971 getValue : function(){
4978 this.update(this.activeDate);
4983 onRender : function(container, position){
4986 '<table cellspacing="0">',
4987 '<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>',
4988 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4989 var dn = this.dayNames;
4990 for(var i = 0; i < 7; i++){
4991 var d = this.startDay+i;
4995 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4997 m[m.length] = "</tr></thead><tbody><tr>";
4998 for(var i = 0; i < 42; i++) {
4999 if(i % 7 == 0 && i != 0){
5000 m[m.length] = "</tr><tr>";
5002 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5004 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5005 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5007 var el = document.createElement("div");
5008 el.className = "x-date-picker";
5009 el.innerHTML = m.join("");
5011 container.dom.insertBefore(el, position);
5013 this.el = Roo.get(el);
5014 this.eventEl = Roo.get(el.firstChild);
5016 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5017 handler: this.showPrevMonth,
5019 preventDefault:true,
5023 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5024 handler: this.showNextMonth,
5026 preventDefault:true,
5030 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5032 this.monthPicker = this.el.down('div.x-date-mp');
5033 this.monthPicker.enableDisplayMode('block');
5035 var kn = new Roo.KeyNav(this.eventEl, {
5036 "left" : function(e){
5038 this.showPrevMonth() :
5039 this.update(this.activeDate.add("d", -1));
5042 "right" : function(e){
5044 this.showNextMonth() :
5045 this.update(this.activeDate.add("d", 1));
5050 this.showNextYear() :
5051 this.update(this.activeDate.add("d", -7));
5054 "down" : function(e){
5056 this.showPrevYear() :
5057 this.update(this.activeDate.add("d", 7));
5060 "pageUp" : function(e){
5061 this.showNextMonth();
5064 "pageDown" : function(e){
5065 this.showPrevMonth();
5068 "enter" : function(e){
5069 e.stopPropagation();
5076 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5078 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5080 this.el.unselectable();
5082 this.cells = this.el.select("table.x-date-inner tbody td");
5083 this.textNodes = this.el.query("table.x-date-inner tbody span");
5085 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5087 tooltip: this.monthYearText
5090 this.mbtn.on('click', this.showMonthPicker, this);
5091 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5094 var today = (new Date()).dateFormat(this.format);
5096 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5097 if (this.showClear) {
5098 baseTb.add( new Roo.Toolbar.Fill());
5101 text: String.format(this.todayText, today),
5102 tooltip: String.format(this.todayTip, today),
5103 handler: this.selectToday,
5107 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5110 if (this.showClear) {
5112 baseTb.add( new Roo.Toolbar.Fill());
5115 cls: 'x-btn-icon x-btn-clear',
5116 handler: function() {
5118 this.fireEvent("select", this, '');
5128 this.update(this.value);
5131 createMonthPicker : function(){
5132 if(!this.monthPicker.dom.firstChild){
5133 var buf = ['<table border="0" cellspacing="0">'];
5134 for(var i = 0; i < 6; i++){
5136 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5137 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5139 '<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>' :
5140 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5144 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5146 '</button><button type="button" class="x-date-mp-cancel">',
5148 '</button></td></tr>',
5151 this.monthPicker.update(buf.join(''));
5152 this.monthPicker.on('click', this.onMonthClick, this);
5153 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5155 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5156 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5158 this.mpMonths.each(function(m, a, i){
5161 m.dom.xmonth = 5 + Math.round(i * .5);
5163 m.dom.xmonth = Math.round((i-1) * .5);
5169 showMonthPicker : function(){
5170 this.createMonthPicker();
5171 var size = this.el.getSize();
5172 this.monthPicker.setSize(size);
5173 this.monthPicker.child('table').setSize(size);
5175 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5176 this.updateMPMonth(this.mpSelMonth);
5177 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5178 this.updateMPYear(this.mpSelYear);
5180 this.monthPicker.slideIn('t', {duration:.2});
5183 updateMPYear : function(y){
5185 var ys = this.mpYears.elements;
5186 for(var i = 1; i <= 10; i++){
5187 var td = ys[i-1], y2;
5189 y2 = y + Math.round(i * .5);
5190 td.firstChild.innerHTML = y2;
5193 y2 = y - (5-Math.round(i * .5));
5194 td.firstChild.innerHTML = y2;
5197 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5201 updateMPMonth : function(sm){
5202 this.mpMonths.each(function(m, a, i){
5203 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5207 selectMPMonth: function(m){
5211 onMonthClick : function(e, t){
5213 var el = new Roo.Element(t), pn;
5214 if(el.is('button.x-date-mp-cancel')){
5215 this.hideMonthPicker();
5217 else if(el.is('button.x-date-mp-ok')){
5218 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5219 this.hideMonthPicker();
5221 else if(pn = el.up('td.x-date-mp-month', 2)){
5222 this.mpMonths.removeClass('x-date-mp-sel');
5223 pn.addClass('x-date-mp-sel');
5224 this.mpSelMonth = pn.dom.xmonth;
5226 else if(pn = el.up('td.x-date-mp-year', 2)){
5227 this.mpYears.removeClass('x-date-mp-sel');
5228 pn.addClass('x-date-mp-sel');
5229 this.mpSelYear = pn.dom.xyear;
5231 else if(el.is('a.x-date-mp-prev')){
5232 this.updateMPYear(this.mpyear-10);
5234 else if(el.is('a.x-date-mp-next')){
5235 this.updateMPYear(this.mpyear+10);
5239 onMonthDblClick : function(e, t){
5241 var el = new Roo.Element(t), pn;
5242 if(pn = el.up('td.x-date-mp-month', 2)){
5243 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5244 this.hideMonthPicker();
5246 else if(pn = el.up('td.x-date-mp-year', 2)){
5247 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5248 this.hideMonthPicker();
5252 hideMonthPicker : function(disableAnim){
5253 if(this.monthPicker){
5254 if(disableAnim === true){
5255 this.monthPicker.hide();
5257 this.monthPicker.slideOut('t', {duration:.2});
5263 showPrevMonth : function(e){
5264 this.update(this.activeDate.add("mo", -1));
5268 showNextMonth : function(e){
5269 this.update(this.activeDate.add("mo", 1));
5273 showPrevYear : function(){
5274 this.update(this.activeDate.add("y", -1));
5278 showNextYear : function(){
5279 this.update(this.activeDate.add("y", 1));
5283 handleMouseWheel : function(e){
5284 var delta = e.getWheelDelta();
5286 this.showPrevMonth();
5288 } else if(delta < 0){
5289 this.showNextMonth();
5295 handleDateClick : function(e, t){
5297 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5298 this.setValue(new Date(t.dateValue));
5299 this.fireEvent("select", this, this.value);
5304 selectToday : function(){
5305 this.setValue(new Date().clearTime());
5306 this.fireEvent("select", this, this.value);
5310 update : function(date)
5312 var vd = this.activeDate;
5313 this.activeDate = date;
5315 var t = date.getTime();
5316 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5317 this.cells.removeClass("x-date-selected");
5318 this.cells.each(function(c){
5319 if(c.dom.firstChild.dateValue == t){
5320 c.addClass("x-date-selected");
5321 setTimeout(function(){
5322 try{c.dom.firstChild.focus();}catch(e){}
5331 var days = date.getDaysInMonth();
5332 var firstOfMonth = date.getFirstDateOfMonth();
5333 var startingPos = firstOfMonth.getDay()-this.startDay;
5335 if(startingPos <= this.startDay){
5339 var pm = date.add("mo", -1);
5340 var prevStart = pm.getDaysInMonth()-startingPos;
5342 var cells = this.cells.elements;
5343 var textEls = this.textNodes;
5344 days += startingPos;
5346 // convert everything to numbers so it's fast
5348 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5349 var today = new Date().clearTime().getTime();
5350 var sel = date.clearTime().getTime();
5351 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5352 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5353 var ddMatch = this.disabledDatesRE;
5354 var ddText = this.disabledDatesText;
5355 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5356 var ddaysText = this.disabledDaysText;
5357 var format = this.format;
5359 var setCellClass = function(cal, cell){
5361 var t = d.getTime();
5362 cell.firstChild.dateValue = t;
5364 cell.className += " x-date-today";
5365 cell.title = cal.todayText;
5368 cell.className += " x-date-selected";
5369 setTimeout(function(){
5370 try{cell.firstChild.focus();}catch(e){}
5375 cell.className = " x-date-disabled";
5376 cell.title = cal.minText;
5380 cell.className = " x-date-disabled";
5381 cell.title = cal.maxText;
5385 if(ddays.indexOf(d.getDay()) != -1){
5386 cell.title = ddaysText;
5387 cell.className = " x-date-disabled";
5390 if(ddMatch && format){
5391 var fvalue = d.dateFormat(format);
5392 if(ddMatch.test(fvalue)){
5393 cell.title = ddText.replace("%0", fvalue);
5394 cell.className = " x-date-disabled";
5400 for(; i < startingPos; i++) {
5401 textEls[i].innerHTML = (++prevStart);
5402 d.setDate(d.getDate()+1);
5403 cells[i].className = "x-date-prevday";
5404 setCellClass(this, cells[i]);
5406 for(; i < days; i++){
5407 intDay = i - startingPos + 1;
5408 textEls[i].innerHTML = (intDay);
5409 d.setDate(d.getDate()+1);
5410 cells[i].className = "x-date-active";
5411 setCellClass(this, cells[i]);
5414 for(; i < 42; i++) {
5415 textEls[i].innerHTML = (++extraDays);
5416 d.setDate(d.getDate()+1);
5417 cells[i].className = "x-date-nextday";
5418 setCellClass(this, cells[i]);
5421 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5422 this.fireEvent('monthchange', this, date);
5424 if(!this.internalRender){
5425 var main = this.el.dom.firstChild;
5426 var w = main.offsetWidth;
5427 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5428 Roo.fly(main).setWidth(w);
5429 this.internalRender = true;
5430 // opera does not respect the auto grow header center column
5431 // then, after it gets a width opera refuses to recalculate
5432 // without a second pass
5433 if(Roo.isOpera && !this.secondPass){
5434 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5435 this.secondPass = true;
5436 this.update.defer(10, this, [date]);
5444 * Ext JS Library 1.1.1
5445 * Copyright(c) 2006-2007, Ext JS, LLC.
5447 * Originally Released Under LGPL - original licence link has changed is not relivant.
5450 * <script type="text/javascript">
5453 * @class Roo.TabPanel
5454 * @extends Roo.util.Observable
5455 * A lightweight tab container.
5459 // basic tabs 1, built from existing content
5460 var tabs = new Roo.TabPanel("tabs1");
5461 tabs.addTab("script", "View Script");
5462 tabs.addTab("markup", "View Markup");
5463 tabs.activate("script");
5465 // more advanced tabs, built from javascript
5466 var jtabs = new Roo.TabPanel("jtabs");
5467 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5469 // set up the UpdateManager
5470 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5471 var updater = tab2.getUpdateManager();
5472 updater.setDefaultUrl("ajax1.htm");
5473 tab2.on('activate', updater.refresh, updater, true);
5475 // Use setUrl for Ajax loading
5476 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5477 tab3.setUrl("ajax2.htm", null, true);
5480 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5483 jtabs.activate("jtabs-1");
5486 * Create a new TabPanel.
5487 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5488 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5490 Roo.TabPanel = function(container, config){
5492 * The container element for this TabPanel.
5495 this.el = Roo.get(container, true);
5497 if(typeof config == "boolean"){
5498 this.tabPosition = config ? "bottom" : "top";
5500 Roo.apply(this, config);
5503 if(this.tabPosition == "bottom"){
5504 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5505 this.el.addClass("x-tabs-bottom");
5507 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5508 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5509 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5511 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5513 if(this.tabPosition != "bottom"){
5514 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5517 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5518 this.el.addClass("x-tabs-top");
5522 this.bodyEl.setStyle("position", "relative");
5525 this.activateDelegate = this.activate.createDelegate(this);
5530 * Fires when the active tab changes
5531 * @param {Roo.TabPanel} this
5532 * @param {Roo.TabPanelItem} activePanel The new active tab
5536 * @event beforetabchange
5537 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5538 * @param {Roo.TabPanel} this
5539 * @param {Object} e Set cancel to true on this object to cancel the tab change
5540 * @param {Roo.TabPanelItem} tab The tab being changed to
5542 "beforetabchange" : true
5545 Roo.EventManager.onWindowResize(this.onResize, this);
5546 this.cpad = this.el.getPadding("lr");
5547 this.hiddenCount = 0;
5550 // toolbar on the tabbar support...
5552 var tcfg = this.toolbar;
5553 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5554 this.toolbar = new Roo.Toolbar(tcfg);
5556 var tbl = tcfg.container.child('table', true);
5557 tbl.setAttribute('width', '100%');
5564 Roo.TabPanel.superclass.constructor.call(this);
5567 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5569 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5571 tabPosition : "top",
5573 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5575 currentTabWidth : 0,
5577 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5581 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5585 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5587 preferredTabWidth : 175,
5589 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5593 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5595 monitorResize : true,
5597 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
5602 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5603 * @param {String} id The id of the div to use <b>or create</b>
5604 * @param {String} text The text for the tab
5605 * @param {String} content (optional) Content to put in the TabPanelItem body
5606 * @param {Boolean} closable (optional) True to create a close icon on the tab
5607 * @return {Roo.TabPanelItem} The created TabPanelItem
5609 addTab : function(id, text, content, closable){
5610 var item = new Roo.TabPanelItem(this, id, text, closable);
5611 this.addTabItem(item);
5613 item.setContent(content);
5619 * Returns the {@link Roo.TabPanelItem} with the specified id/index
5620 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5621 * @return {Roo.TabPanelItem}
5623 getTab : function(id){
5624 return this.items[id];
5628 * Hides the {@link Roo.TabPanelItem} with the specified id/index
5629 * @param {String/Number} id The id or index of the TabPanelItem to hide.
5631 hideTab : function(id){
5632 var t = this.items[id];
5636 this.autoSizeTabs();
5641 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5642 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5644 unhideTab : function(id){
5645 var t = this.items[id];
5649 this.autoSizeTabs();
5654 * Adds an existing {@link Roo.TabPanelItem}.
5655 * @param {Roo.TabPanelItem} item The TabPanelItem to add
5657 addTabItem : function(item){
5658 this.items[item.id] = item;
5659 this.items.push(item);
5660 if(this.resizeTabs){
5661 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5662 this.autoSizeTabs();
5669 * Removes a {@link Roo.TabPanelItem}.
5670 * @param {String/Number} id The id or index of the TabPanelItem to remove.
5672 removeTab : function(id){
5673 var items = this.items;
5674 var tab = items[id];
5675 if(!tab) { return; }
5676 var index = items.indexOf(tab);
5677 if(this.active == tab && items.length > 1){
5678 var newTab = this.getNextAvailable(index);
5683 this.stripEl.dom.removeChild(tab.pnode.dom);
5684 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5685 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5687 items.splice(index, 1);
5688 delete this.items[tab.id];
5689 tab.fireEvent("close", tab);
5690 tab.purgeListeners();
5691 this.autoSizeTabs();
5694 getNextAvailable : function(start){
5695 var items = this.items;
5697 // look for a next tab that will slide over to
5698 // replace the one being removed
5699 while(index < items.length){
5700 var item = items[++index];
5701 if(item && !item.isHidden()){
5705 // if one isn't found select the previous tab (on the left)
5708 var item = items[--index];
5709 if(item && !item.isHidden()){
5717 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5718 * @param {String/Number} id The id or index of the TabPanelItem to disable.
5720 disableTab : function(id){
5721 var tab = this.items[id];
5722 if(tab && this.active != tab){
5728 * Enables a {@link Roo.TabPanelItem} that is disabled.
5729 * @param {String/Number} id The id or index of the TabPanelItem to enable.
5731 enableTab : function(id){
5732 var tab = this.items[id];
5737 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5738 * @param {String/Number} id The id or index of the TabPanelItem to activate.
5739 * @return {Roo.TabPanelItem} The TabPanelItem.
5741 activate : function(id){
5742 var tab = this.items[id];
5746 if(tab == this.active || tab.disabled){
5750 this.fireEvent("beforetabchange", this, e, tab);
5751 if(e.cancel !== true && !tab.disabled){
5755 this.active = this.items[id];
5757 this.fireEvent("tabchange", this, this.active);
5763 * Gets the active {@link Roo.TabPanelItem}.
5764 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5766 getActiveTab : function(){
5771 * Updates the tab body element to fit the height of the container element
5772 * for overflow scrolling
5773 * @param {Number} targetHeight (optional) Override the starting height from the elements height
5775 syncHeight : function(targetHeight){
5776 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5777 var bm = this.bodyEl.getMargins();
5778 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5779 this.bodyEl.setHeight(newHeight);
5783 onResize : function(){
5784 if(this.monitorResize){
5785 this.autoSizeTabs();
5790 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5792 beginUpdate : function(){
5793 this.updating = true;
5797 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5799 endUpdate : function(){
5800 this.updating = false;
5801 this.autoSizeTabs();
5805 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5807 autoSizeTabs : function(){
5808 var count = this.items.length;
5809 var vcount = count - this.hiddenCount;
5810 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5813 var w = Math.max(this.el.getWidth() - this.cpad, 10);
5814 var availWidth = Math.floor(w / vcount);
5815 var b = this.stripBody;
5816 if(b.getWidth() > w){
5817 var tabs = this.items;
5818 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5819 if(availWidth < this.minTabWidth){
5820 /*if(!this.sleft){ // incomplete scrolling code
5821 this.createScrollButtons();
5824 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5827 if(this.currentTabWidth < this.preferredTabWidth){
5828 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5834 * Returns the number of tabs in this TabPanel.
5837 getCount : function(){
5838 return this.items.length;
5842 * Resizes all the tabs to the passed width
5843 * @param {Number} The new width
5845 setTabWidth : function(width){
5846 this.currentTabWidth = width;
5847 for(var i = 0, len = this.items.length; i < len; i++) {
5848 if(!this.items[i].isHidden()) {
5849 this.items[i].setWidth(width);
5855 * Destroys this TabPanel
5856 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5858 destroy : function(removeEl){
5859 Roo.EventManager.removeResizeListener(this.onResize, this);
5860 for(var i = 0, len = this.items.length; i < len; i++){
5861 this.items[i].purgeListeners();
5863 if(removeEl === true){
5871 * @class Roo.TabPanelItem
5872 * @extends Roo.util.Observable
5873 * Represents an individual item (tab plus body) in a TabPanel.
5874 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5875 * @param {String} id The id of this TabPanelItem
5876 * @param {String} text The text for the tab of this TabPanelItem
5877 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5879 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5881 * The {@link Roo.TabPanel} this TabPanelItem belongs to
5882 * @type Roo.TabPanel
5884 this.tabPanel = tabPanel;
5886 * The id for this TabPanelItem
5891 this.disabled = false;
5895 this.loaded = false;
5896 this.closable = closable;
5899 * The body element for this TabPanelItem.
5902 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5903 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5904 this.bodyEl.setStyle("display", "block");
5905 this.bodyEl.setStyle("zoom", "1");
5908 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5910 this.el = Roo.get(els.el, true);
5911 this.inner = Roo.get(els.inner, true);
5912 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5913 this.pnode = Roo.get(els.el.parentNode, true);
5914 this.el.on("mousedown", this.onTabMouseDown, this);
5915 this.el.on("click", this.onTabClick, this);
5918 var c = Roo.get(els.close, true);
5919 c.dom.title = this.closeText;
5920 c.addClassOnOver("close-over");
5921 c.on("click", this.closeClick, this);
5927 * Fires when this tab becomes the active tab.
5928 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5929 * @param {Roo.TabPanelItem} this
5933 * @event beforeclose
5934 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5935 * @param {Roo.TabPanelItem} this
5936 * @param {Object} e Set cancel to true on this object to cancel the close.
5938 "beforeclose": true,
5941 * Fires when this tab is closed.
5942 * @param {Roo.TabPanelItem} this
5947 * Fires when this tab is no longer the active tab.
5948 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5949 * @param {Roo.TabPanelItem} this
5953 this.hidden = false;
5955 Roo.TabPanelItem.superclass.constructor.call(this);
5958 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5959 purgeListeners : function(){
5960 Roo.util.Observable.prototype.purgeListeners.call(this);
5961 this.el.removeAllListeners();
5964 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5967 this.pnode.addClass("on");
5970 this.tabPanel.stripWrap.repaint();
5972 this.fireEvent("activate", this.tabPanel, this);
5976 * Returns true if this tab is the active tab.
5979 isActive : function(){
5980 return this.tabPanel.getActiveTab() == this;
5984 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5987 this.pnode.removeClass("on");
5989 this.fireEvent("deactivate", this.tabPanel, this);
5992 hideAction : function(){
5994 this.bodyEl.setStyle("position", "absolute");
5995 this.bodyEl.setLeft("-20000px");
5996 this.bodyEl.setTop("-20000px");
5999 showAction : function(){
6000 this.bodyEl.setStyle("position", "relative");
6001 this.bodyEl.setTop("");
6002 this.bodyEl.setLeft("");
6007 * Set the tooltip for the tab.
6008 * @param {String} tooltip The tab's tooltip
6010 setTooltip : function(text){
6011 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6012 this.textEl.dom.qtip = text;
6013 this.textEl.dom.removeAttribute('title');
6015 this.textEl.dom.title = text;
6019 onTabClick : function(e){
6021 this.tabPanel.activate(this.id);
6024 onTabMouseDown : function(e){
6026 this.tabPanel.activate(this.id);
6029 getWidth : function(){
6030 return this.inner.getWidth();
6033 setWidth : function(width){
6034 var iwidth = width - this.pnode.getPadding("lr");
6035 this.inner.setWidth(iwidth);
6036 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6037 this.pnode.setWidth(width);
6041 * Show or hide the tab
6042 * @param {Boolean} hidden True to hide or false to show.
6044 setHidden : function(hidden){
6045 this.hidden = hidden;
6046 this.pnode.setStyle("display", hidden ? "none" : "");
6050 * Returns true if this tab is "hidden"
6053 isHidden : function(){
6058 * Returns the text for this tab
6061 getText : function(){
6065 autoSize : function(){
6066 //this.el.beginMeasure();
6067 this.textEl.setWidth(1);
6069 * #2804 [new] Tabs in Roojs
6070 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6072 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6073 //this.el.endMeasure();
6077 * Sets the text for the tab (Note: this also sets the tooltip text)
6078 * @param {String} text The tab's text and tooltip
6080 setText : function(text){
6082 this.textEl.update(text);
6083 this.setTooltip(text);
6084 if(!this.tabPanel.resizeTabs){
6089 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6091 activate : function(){
6092 this.tabPanel.activate(this.id);
6096 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6098 disable : function(){
6099 if(this.tabPanel.active != this){
6100 this.disabled = true;
6101 this.pnode.addClass("disabled");
6106 * Enables this TabPanelItem if it was previously disabled.
6108 enable : function(){
6109 this.disabled = false;
6110 this.pnode.removeClass("disabled");
6114 * Sets the content for this TabPanelItem.
6115 * @param {String} content The content
6116 * @param {Boolean} loadScripts true to look for and load scripts
6118 setContent : function(content, loadScripts){
6119 this.bodyEl.update(content, loadScripts);
6123 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6124 * @return {Roo.UpdateManager} The UpdateManager
6126 getUpdateManager : function(){
6127 return this.bodyEl.getUpdateManager();
6131 * Set a URL to be used to load the content for this TabPanelItem.
6132 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6133 * @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)
6134 * @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)
6135 * @return {Roo.UpdateManager} The UpdateManager
6137 setUrl : function(url, params, loadOnce){
6138 if(this.refreshDelegate){
6139 this.un('activate', this.refreshDelegate);
6141 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6142 this.on("activate", this.refreshDelegate);
6143 return this.bodyEl.getUpdateManager();
6147 _handleRefresh : function(url, params, loadOnce){
6148 if(!loadOnce || !this.loaded){
6149 var updater = this.bodyEl.getUpdateManager();
6150 updater.update(url, params, this._setLoaded.createDelegate(this));
6155 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6156 * Will fail silently if the setUrl method has not been called.
6157 * This does not activate the panel, just updates its content.
6159 refresh : function(){
6160 if(this.refreshDelegate){
6161 this.loaded = false;
6162 this.refreshDelegate();
6167 _setLoaded : function(){
6172 closeClick : function(e){
6175 this.fireEvent("beforeclose", this, o);
6176 if(o.cancel !== true){
6177 this.tabPanel.removeTab(this.id);
6181 * The text displayed in the tooltip for the close icon.
6184 closeText : "Close this tab"
6188 Roo.TabPanel.prototype.createStrip = function(container){
6189 var strip = document.createElement("div");
6190 strip.className = "x-tabs-wrap";
6191 container.appendChild(strip);
6195 Roo.TabPanel.prototype.createStripList = function(strip){
6196 // div wrapper for retard IE
6197 // returns the "tr" element.
6198 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6199 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6200 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6201 return strip.firstChild.firstChild.firstChild.firstChild;
6204 Roo.TabPanel.prototype.createBody = function(container){
6205 var body = document.createElement("div");
6206 Roo.id(body, "tab-body");
6207 Roo.fly(body).addClass("x-tabs-body");
6208 container.appendChild(body);
6212 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6213 var body = Roo.getDom(id);
6215 body = document.createElement("div");
6218 Roo.fly(body).addClass("x-tabs-item-body");
6219 bodyEl.insertBefore(body, bodyEl.firstChild);
6223 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6224 var td = document.createElement("td");
6225 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6226 //stripEl.appendChild(td);
6228 td.className = "x-tabs-closable";
6230 this.closeTpl = new Roo.Template(
6231 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6232 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6233 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6236 var el = this.closeTpl.overwrite(td, {"text": text});
6237 var close = el.getElementsByTagName("div")[0];
6238 var inner = el.getElementsByTagName("em")[0];
6239 return {"el": el, "close": close, "inner": inner};
6242 this.tabTpl = new Roo.Template(
6243 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6244 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6247 var el = this.tabTpl.overwrite(td, {"text": text});
6248 var inner = el.getElementsByTagName("em")[0];
6249 return {"el": el, "inner": inner};
6253 * Ext JS Library 1.1.1
6254 * Copyright(c) 2006-2007, Ext JS, LLC.
6256 * Originally Released Under LGPL - original licence link has changed is not relivant.
6259 * <script type="text/javascript">
6264 * @extends Roo.util.Observable
6265 * Simple Button class
6266 * @cfg {String} text The button text
6267 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6268 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6269 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6270 * @cfg {Object} scope The scope of the handler
6271 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6272 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6273 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6274 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6275 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6276 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6277 applies if enableToggle = true)
6278 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6279 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6280 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6282 * Create a new button
6283 * @param {Object} config The config object
6285 Roo.Button = function(renderTo, config)
6289 renderTo = config.renderTo || false;
6292 Roo.apply(this, config);
6296 * Fires when this button is clicked
6297 * @param {Button} this
6298 * @param {EventObject} e The click event
6303 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6304 * @param {Button} this
6305 * @param {Boolean} pressed
6310 * Fires when the mouse hovers over the button
6311 * @param {Button} this
6312 * @param {Event} e The event object
6317 * Fires when the mouse exits the button
6318 * @param {Button} this
6319 * @param {Event} e The event object
6324 * Fires when the button is rendered
6325 * @param {Button} this
6330 this.menu = Roo.menu.MenuMgr.get(this.menu);
6332 // register listeners first!! - so render can be captured..
6333 Roo.util.Observable.call(this);
6335 this.render(renderTo);
6341 Roo.extend(Roo.Button, Roo.util.Observable, {
6347 * Read-only. True if this button is hidden
6352 * Read-only. True if this button is disabled
6357 * Read-only. True if this button is pressed (only if enableToggle = true)
6363 * @cfg {Number} tabIndex
6364 * The DOM tabIndex for this button (defaults to undefined)
6366 tabIndex : undefined,
6369 * @cfg {Boolean} enableToggle
6370 * True to enable pressed/not pressed toggling (defaults to false)
6372 enableToggle: false,
6374 * @cfg {Roo.menu.Menu} menu
6375 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6379 * @cfg {String} menuAlign
6380 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6382 menuAlign : "tl-bl?",
6385 * @cfg {String} iconCls
6386 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6388 iconCls : undefined,
6390 * @cfg {String} type
6391 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6396 menuClassTarget: 'tr',
6399 * @cfg {String} clickEvent
6400 * The type of event to map to the button's event handler (defaults to 'click')
6402 clickEvent : 'click',
6405 * @cfg {Boolean} handleMouseEvents
6406 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6408 handleMouseEvents : true,
6411 * @cfg {String} tooltipType
6412 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6414 tooltipType : 'qtip',
6418 * A CSS class to apply to the button's main element.
6422 * @cfg {Roo.Template} template (Optional)
6423 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6424 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6425 * require code modifications if required elements (e.g. a button) aren't present.
6429 render : function(renderTo){
6431 if(this.hideParent){
6432 this.parentEl = Roo.get(renderTo);
6436 if(!Roo.Button.buttonTemplate){
6437 // hideous table template
6438 Roo.Button.buttonTemplate = new Roo.Template(
6439 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6440 '<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>',
6441 "</tr></tbody></table>");
6443 this.template = Roo.Button.buttonTemplate;
6445 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6446 var btnEl = btn.child("button:first");
6447 btnEl.on('focus', this.onFocus, this);
6448 btnEl.on('blur', this.onBlur, this);
6450 btn.addClass(this.cls);
6453 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6456 btnEl.addClass(this.iconCls);
6458 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6461 if(this.tabIndex !== undefined){
6462 btnEl.dom.tabIndex = this.tabIndex;
6465 if(typeof this.tooltip == 'object'){
6466 Roo.QuickTips.tips(Roo.apply({
6470 btnEl.dom[this.tooltipType] = this.tooltip;
6474 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6478 this.el.dom.id = this.el.id = this.id;
6481 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6482 this.menu.on("show", this.onMenuShow, this);
6483 this.menu.on("hide", this.onMenuHide, this);
6485 btn.addClass("x-btn");
6486 if(Roo.isIE && !Roo.isIE7){
6487 this.autoWidth.defer(1, this);
6491 if(this.handleMouseEvents){
6492 btn.on("mouseover", this.onMouseOver, this);
6493 btn.on("mouseout", this.onMouseOut, this);
6494 btn.on("mousedown", this.onMouseDown, this);
6496 btn.on(this.clickEvent, this.onClick, this);
6497 //btn.on("mouseup", this.onMouseUp, this);
6504 Roo.ButtonToggleMgr.register(this);
6506 this.el.addClass("x-btn-pressed");
6509 var repeater = new Roo.util.ClickRepeater(btn,
6510 typeof this.repeat == "object" ? this.repeat : {}
6512 repeater.on("click", this.onClick, this);
6515 this.fireEvent('render', this);
6519 * Returns the button's underlying element
6520 * @return {Roo.Element} The element
6527 * Destroys this Button and removes any listeners.
6529 destroy : function(){
6530 Roo.ButtonToggleMgr.unregister(this);
6531 this.el.removeAllListeners();
6532 this.purgeListeners();
6537 autoWidth : function(){
6539 this.el.setWidth("auto");
6540 if(Roo.isIE7 && Roo.isStrict){
6541 var ib = this.el.child('button');
6542 if(ib && ib.getWidth() > 20){
6544 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6549 this.el.beginMeasure();
6551 if(this.el.getWidth() < this.minWidth){
6552 this.el.setWidth(this.minWidth);
6555 this.el.endMeasure();
6562 * Assigns this button's click handler
6563 * @param {Function} handler The function to call when the button is clicked
6564 * @param {Object} scope (optional) Scope for the function passed in
6566 setHandler : function(handler, scope){
6567 this.handler = handler;
6572 * Sets this button's text
6573 * @param {String} text The button text
6575 setText : function(text){
6578 this.el.child("td.x-btn-center button.x-btn-text").update(text);
6584 * Gets the text for this button
6585 * @return {String} The button text
6587 getText : function(){
6595 this.hidden = false;
6597 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6607 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6612 * Convenience function for boolean show/hide
6613 * @param {Boolean} visible True to show, false to hide
6615 setVisible: function(visible){
6624 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6625 * @param {Boolean} state (optional) Force a particular state
6627 toggle : function(state){
6628 state = state === undefined ? !this.pressed : state;
6629 if(state != this.pressed){
6631 this.el.addClass("x-btn-pressed");
6632 this.pressed = true;
6633 this.fireEvent("toggle", this, true);
6635 this.el.removeClass("x-btn-pressed");
6636 this.pressed = false;
6637 this.fireEvent("toggle", this, false);
6639 if(this.toggleHandler){
6640 this.toggleHandler.call(this.scope || this, this, state);
6649 this.el.child('button:first').focus();
6653 * Disable this button
6655 disable : function(){
6657 this.el.addClass("x-btn-disabled");
6659 this.disabled = true;
6663 * Enable this button
6665 enable : function(){
6667 this.el.removeClass("x-btn-disabled");
6669 this.disabled = false;
6673 * Convenience function for boolean enable/disable
6674 * @param {Boolean} enabled True to enable, false to disable
6676 setDisabled : function(v){
6677 this[v !== true ? "enable" : "disable"]();
6681 onClick : function(e)
6690 if(this.enableToggle){
6693 if(this.menu && !this.menu.isVisible()){
6694 this.menu.show(this.el, this.menuAlign);
6696 this.fireEvent("click", this, e);
6698 this.el.removeClass("x-btn-over");
6699 this.handler.call(this.scope || this, this, e);
6704 onMouseOver : function(e){
6706 this.el.addClass("x-btn-over");
6707 this.fireEvent('mouseover', this, e);
6711 onMouseOut : function(e){
6712 if(!e.within(this.el, true)){
6713 this.el.removeClass("x-btn-over");
6714 this.fireEvent('mouseout', this, e);
6718 onFocus : function(e){
6720 this.el.addClass("x-btn-focus");
6724 onBlur : function(e){
6725 this.el.removeClass("x-btn-focus");
6728 onMouseDown : function(e){
6729 if(!this.disabled && e.button == 0){
6730 this.el.addClass("x-btn-click");
6731 Roo.get(document).on('mouseup', this.onMouseUp, this);
6735 onMouseUp : function(e){
6737 this.el.removeClass("x-btn-click");
6738 Roo.get(document).un('mouseup', this.onMouseUp, this);
6742 onMenuShow : function(e){
6743 this.el.addClass("x-btn-menu-active");
6746 onMenuHide : function(e){
6747 this.el.removeClass("x-btn-menu-active");
6751 // Private utility class used by Button
6752 Roo.ButtonToggleMgr = function(){
6755 function toggleGroup(btn, state){
6757 var g = groups[btn.toggleGroup];
6758 for(var i = 0, l = g.length; i < l; i++){
6767 register : function(btn){
6768 if(!btn.toggleGroup){
6771 var g = groups[btn.toggleGroup];
6773 g = groups[btn.toggleGroup] = [];
6776 btn.on("toggle", toggleGroup);
6779 unregister : function(btn){
6780 if(!btn.toggleGroup){
6783 var g = groups[btn.toggleGroup];
6786 btn.un("toggle", toggleGroup);
6792 * Ext JS Library 1.1.1
6793 * Copyright(c) 2006-2007, Ext JS, LLC.
6795 * Originally Released Under LGPL - original licence link has changed is not relivant.
6798 * <script type="text/javascript">
6802 * @class Roo.SplitButton
6803 * @extends Roo.Button
6804 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6805 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
6806 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6807 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6808 * @cfg {String} arrowTooltip The title attribute of the arrow
6810 * Create a new menu button
6811 * @param {String/HTMLElement/Element} renderTo The element to append the button to
6812 * @param {Object} config The config object
6814 Roo.SplitButton = function(renderTo, config){
6815 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6818 * Fires when this button's arrow is clicked
6819 * @param {SplitButton} this
6820 * @param {EventObject} e The click event
6822 this.addEvents({"arrowclick":true});
6825 Roo.extend(Roo.SplitButton, Roo.Button, {
6826 render : function(renderTo){
6827 // this is one sweet looking template!
6828 var tpl = new Roo.Template(
6829 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6830 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6831 '<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>',
6832 "</tbody></table></td><td>",
6833 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6834 '<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>',
6835 "</tbody></table></td></tr></table>"
6837 var btn = tpl.append(renderTo, [this.text, this.type], true);
6838 var btnEl = btn.child("button");
6840 btn.addClass(this.cls);
6843 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6846 btnEl.addClass(this.iconCls);
6848 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6852 if(this.handleMouseEvents){
6853 btn.on("mouseover", this.onMouseOver, this);
6854 btn.on("mouseout", this.onMouseOut, this);
6855 btn.on("mousedown", this.onMouseDown, this);
6856 btn.on("mouseup", this.onMouseUp, this);
6858 btn.on(this.clickEvent, this.onClick, this);
6860 if(typeof this.tooltip == 'object'){
6861 Roo.QuickTips.tips(Roo.apply({
6865 btnEl.dom[this.tooltipType] = this.tooltip;
6868 if(this.arrowTooltip){
6869 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6878 this.el.addClass("x-btn-pressed");
6880 if(Roo.isIE && !Roo.isIE7){
6881 this.autoWidth.defer(1, this);
6886 this.menu.on("show", this.onMenuShow, this);
6887 this.menu.on("hide", this.onMenuHide, this);
6889 this.fireEvent('render', this);
6893 autoWidth : function(){
6895 var tbl = this.el.child("table:first");
6896 var tbl2 = this.el.child("table:last");
6897 this.el.setWidth("auto");
6898 tbl.setWidth("auto");
6899 if(Roo.isIE7 && Roo.isStrict){
6900 var ib = this.el.child('button:first');
6901 if(ib && ib.getWidth() > 20){
6903 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6908 this.el.beginMeasure();
6910 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6911 tbl.setWidth(this.minWidth-tbl2.getWidth());
6914 this.el.endMeasure();
6917 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6921 * Sets this button's click handler
6922 * @param {Function} handler The function to call when the button is clicked
6923 * @param {Object} scope (optional) Scope for the function passed above
6925 setHandler : function(handler, scope){
6926 this.handler = handler;
6931 * Sets this button's arrow click handler
6932 * @param {Function} handler The function to call when the arrow is clicked
6933 * @param {Object} scope (optional) Scope for the function passed above
6935 setArrowHandler : function(handler, scope){
6936 this.arrowHandler = handler;
6945 this.el.child("button:first").focus();
6950 onClick : function(e){
6953 if(e.getTarget(".x-btn-menu-arrow-wrap")){
6954 if(this.menu && !this.menu.isVisible()){
6955 this.menu.show(this.el, this.menuAlign);
6957 this.fireEvent("arrowclick", this, e);
6958 if(this.arrowHandler){
6959 this.arrowHandler.call(this.scope || this, this, e);
6962 this.fireEvent("click", this, e);
6964 this.handler.call(this.scope || this, this, e);
6970 onMouseDown : function(e){
6972 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6976 onMouseUp : function(e){
6977 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6983 Roo.MenuButton = Roo.SplitButton;/*
6985 * Ext JS Library 1.1.1
6986 * Copyright(c) 2006-2007, Ext JS, LLC.
6988 * Originally Released Under LGPL - original licence link has changed is not relivant.
6991 * <script type="text/javascript">
6995 * @class Roo.Toolbar
6996 * @children Roo.Toolbar.Item Roo.form.Field
6997 * Basic Toolbar class.
6999 * Creates a new Toolbar
7000 * @param {Object} container The config object
7002 Roo.Toolbar = function(container, buttons, config)
7004 /// old consturctor format still supported..
7005 if(container instanceof Array){ // omit the container for later rendering
7006 buttons = container;
7010 if (typeof(container) == 'object' && container.xtype) {
7012 container = config.container;
7013 buttons = config.buttons || []; // not really - use items!!
7016 if (config && config.items) {
7017 xitems = config.items;
7018 delete config.items;
7020 Roo.apply(this, config);
7021 this.buttons = buttons;
7024 this.render(container);
7026 this.xitems = xitems;
7027 Roo.each(xitems, function(b) {
7033 Roo.Toolbar.prototype = {
7035 * @cfg {Array} items
7036 * array of button configs or elements to add (will be converted to a MixedCollection)
7040 * @cfg {String/HTMLElement/Element} container
7041 * The id or element that will contain the toolbar
7044 render : function(ct){
7045 this.el = Roo.get(ct);
7047 this.el.addClass(this.cls);
7049 // using a table allows for vertical alignment
7050 // 100% width is needed by Safari...
7051 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7052 this.tr = this.el.child("tr", true);
7054 this.items = new Roo.util.MixedCollection(false, function(o){
7055 return o.id || ("item" + (++autoId));
7058 this.add.apply(this, this.buttons);
7059 delete this.buttons;
7064 * Adds element(s) to the toolbar -- this function takes a variable number of
7065 * arguments of mixed type and adds them to the toolbar.
7066 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7068 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7069 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7070 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7071 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7072 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7073 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7074 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7075 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7076 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7078 * @param {Mixed} arg2
7079 * @param {Mixed} etc.
7082 var a = arguments, l = a.length;
7083 for(var i = 0; i < l; i++){
7088 _add : function(el) {
7091 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7094 if (el.applyTo){ // some kind of form field
7095 return this.addField(el);
7097 if (el.render){ // some kind of Toolbar.Item
7098 return this.addItem(el);
7100 if (typeof el == "string"){ // string
7101 if(el == "separator" || el == "-"){
7102 return this.addSeparator();
7105 return this.addSpacer();
7108 return this.addFill();
7110 return this.addText(el);
7113 if(el.tagName){ // element
7114 return this.addElement(el);
7116 if(typeof el == "object"){ // must be button config?
7117 return this.addButton(el);
7125 * Add an Xtype element
7126 * @param {Object} xtype Xtype Object
7127 * @return {Object} created Object
7129 addxtype : function(e){
7134 * Returns the Element for this toolbar.
7135 * @return {Roo.Element}
7143 * @return {Roo.Toolbar.Item} The separator item
7145 addSeparator : function(){
7146 return this.addItem(new Roo.Toolbar.Separator());
7150 * Adds a spacer element
7151 * @return {Roo.Toolbar.Spacer} The spacer item
7153 addSpacer : function(){
7154 return this.addItem(new Roo.Toolbar.Spacer());
7158 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7159 * @return {Roo.Toolbar.Fill} The fill item
7161 addFill : function(){
7162 return this.addItem(new Roo.Toolbar.Fill());
7166 * Adds any standard HTML element to the toolbar
7167 * @param {String/HTMLElement/Element} el The element or id of the element to add
7168 * @return {Roo.Toolbar.Item} The element's item
7170 addElement : function(el){
7171 return this.addItem(new Roo.Toolbar.Item(el));
7174 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7175 * @type Roo.util.MixedCollection
7180 * Adds any Toolbar.Item or subclass
7181 * @param {Roo.Toolbar.Item} item
7182 * @return {Roo.Toolbar.Item} The item
7184 addItem : function(item){
7185 var td = this.nextBlock();
7187 this.items.add(item);
7192 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7193 * @param {Object/Array} config A button config or array of configs
7194 * @return {Roo.Toolbar.Button/Array}
7196 addButton : function(config){
7197 if(config instanceof Array){
7199 for(var i = 0, len = config.length; i < len; i++) {
7200 buttons.push(this.addButton(config[i]));
7205 if(!(config instanceof Roo.Toolbar.Button)){
7207 new Roo.Toolbar.SplitButton(config) :
7208 new Roo.Toolbar.Button(config);
7210 var td = this.nextBlock();
7217 * Adds text to the toolbar
7218 * @param {String} text The text to add
7219 * @return {Roo.Toolbar.Item} The element's item
7221 addText : function(text){
7222 return this.addItem(new Roo.Toolbar.TextItem(text));
7226 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7227 * @param {Number} index The index where the item is to be inserted
7228 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7229 * @return {Roo.Toolbar.Button/Item}
7231 insertButton : function(index, item){
7232 if(item instanceof Array){
7234 for(var i = 0, len = item.length; i < len; i++) {
7235 buttons.push(this.insertButton(index + i, item[i]));
7239 if (!(item instanceof Roo.Toolbar.Button)){
7240 item = new Roo.Toolbar.Button(item);
7242 var td = document.createElement("td");
7243 this.tr.insertBefore(td, this.tr.childNodes[index]);
7245 this.items.insert(index, item);
7250 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7251 * @param {Object} config
7252 * @return {Roo.Toolbar.Item} The element's item
7254 addDom : function(config, returnEl){
7255 var td = this.nextBlock();
7256 Roo.DomHelper.overwrite(td, config);
7257 var ti = new Roo.Toolbar.Item(td.firstChild);
7264 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7265 * @type Roo.util.MixedCollection
7270 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7271 * Note: the field should not have been rendered yet. For a field that has already been
7272 * rendered, use {@link #addElement}.
7273 * @param {Roo.form.Field} field
7274 * @return {Roo.ToolbarItem}
7278 addField : function(field) {
7281 this.fields = new Roo.util.MixedCollection(false, function(o){
7282 return o.id || ("item" + (++autoId));
7287 var td = this.nextBlock();
7289 var ti = new Roo.Toolbar.Item(td.firstChild);
7292 this.fields.add(field);
7303 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7304 this.el.child('div').hide();
7312 this.el.child('div').show();
7316 nextBlock : function(){
7317 var td = document.createElement("td");
7318 this.tr.appendChild(td);
7323 destroy : function(){
7324 if(this.items){ // rendered?
7325 Roo.destroy.apply(Roo, this.items.items);
7327 if(this.fields){ // rendered?
7328 Roo.destroy.apply(Roo, this.fields.items);
7330 Roo.Element.uncache(this.el, this.tr);
7335 * @class Roo.Toolbar.Item
7336 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7338 * Creates a new Item
7339 * @param {HTMLElement} el
7341 Roo.Toolbar.Item = function(el){
7343 if (typeof (el.xtype) != 'undefined') {
7348 this.el = Roo.getDom(el);
7349 this.id = Roo.id(this.el);
7350 this.hidden = false;
7355 * Fires when the button is rendered
7356 * @param {Button} this
7360 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7362 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7363 //Roo.Toolbar.Item.prototype = {
7366 * Get this item's HTML Element
7367 * @return {HTMLElement}
7374 render : function(td){
7377 td.appendChild(this.el);
7379 this.fireEvent('render', this);
7383 * Removes and destroys this item.
7385 destroy : function(){
7386 this.td.parentNode.removeChild(this.td);
7393 this.hidden = false;
7394 this.td.style.display = "";
7402 this.td.style.display = "none";
7406 * Convenience function for boolean show/hide.
7407 * @param {Boolean} visible true to show/false to hide
7409 setVisible: function(visible){
7418 * Try to focus this item.
7421 Roo.fly(this.el).focus();
7425 * Disables this item.
7427 disable : function(){
7428 Roo.fly(this.td).addClass("x-item-disabled");
7429 this.disabled = true;
7430 this.el.disabled = true;
7434 * Enables this item.
7436 enable : function(){
7437 Roo.fly(this.td).removeClass("x-item-disabled");
7438 this.disabled = false;
7439 this.el.disabled = false;
7445 * @class Roo.Toolbar.Separator
7446 * @extends Roo.Toolbar.Item
7447 * A simple toolbar separator class
7449 * Creates a new Separator
7451 Roo.Toolbar.Separator = function(cfg){
7453 var s = document.createElement("span");
7454 s.className = "ytb-sep";
7459 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7461 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7463 disable:Roo.emptyFn,
7468 * @class Roo.Toolbar.Spacer
7469 * @extends Roo.Toolbar.Item
7470 * A simple element that adds extra horizontal space to a toolbar.
7472 * Creates a new Spacer
7474 Roo.Toolbar.Spacer = function(cfg){
7475 var s = document.createElement("div");
7476 s.className = "ytb-spacer";
7480 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7482 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7484 disable:Roo.emptyFn,
7489 * @class Roo.Toolbar.Fill
7490 * @extends Roo.Toolbar.Spacer
7491 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7493 * Creates a new Spacer
7495 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7497 render : function(td){
7498 td.style.width = '100%';
7499 Roo.Toolbar.Fill.superclass.render.call(this, td);
7504 * @class Roo.Toolbar.TextItem
7505 * @extends Roo.Toolbar.Item
7506 * A simple class that renders text directly into a toolbar.
7508 * Creates a new TextItem
7509 * @cfg {string} text
7511 Roo.Toolbar.TextItem = function(cfg){
7512 var text = cfg || "";
7513 if (typeof(cfg) == 'object') {
7514 text = cfg.text || "";
7518 var s = document.createElement("span");
7519 s.className = "ytb-text";
7525 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7527 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7531 disable:Roo.emptyFn,
7536 * @class Roo.Toolbar.Button
7537 * @extends Roo.Button
7538 * A button that renders into a toolbar.
7540 * Creates a new Button
7541 * @param {Object} config A standard {@link Roo.Button} config object
7543 Roo.Toolbar.Button = function(config){
7544 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7546 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7550 render : function(td){
7552 Roo.Toolbar.Button.superclass.render.call(this, td);
7556 * Removes and destroys this button
7558 destroy : function(){
7559 Roo.Toolbar.Button.superclass.destroy.call(this);
7560 this.td.parentNode.removeChild(this.td);
7567 this.hidden = false;
7568 this.td.style.display = "";
7576 this.td.style.display = "none";
7580 * Disables this item
7582 disable : function(){
7583 Roo.fly(this.td).addClass("x-item-disabled");
7584 this.disabled = true;
7590 enable : function(){
7591 Roo.fly(this.td).removeClass("x-item-disabled");
7592 this.disabled = false;
7596 Roo.ToolbarButton = Roo.Toolbar.Button;
7599 * @class Roo.Toolbar.SplitButton
7600 * @extends Roo.SplitButton
7601 * A menu button that renders into a toolbar.
7603 * Creates a new SplitButton
7604 * @param {Object} config A standard {@link Roo.SplitButton} config object
7606 Roo.Toolbar.SplitButton = function(config){
7607 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7609 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7610 render : function(td){
7612 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7616 * Removes and destroys this button
7618 destroy : function(){
7619 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7620 this.td.parentNode.removeChild(this.td);
7627 this.hidden = false;
7628 this.td.style.display = "";
7636 this.td.style.display = "none";
7641 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7643 * Ext JS Library 1.1.1
7644 * Copyright(c) 2006-2007, Ext JS, LLC.
7646 * Originally Released Under LGPL - original licence link has changed is not relivant.
7649 * <script type="text/javascript">
7653 * @class Roo.PagingToolbar
7654 * @extends Roo.Toolbar
7655 * @children Roo.Toolbar.Item Roo.form.Field
7656 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7658 * Create a new PagingToolbar
7659 * @param {Object} config The config object
7661 Roo.PagingToolbar = function(el, ds, config)
7663 // old args format still supported... - xtype is prefered..
7664 if (typeof(el) == 'object' && el.xtype) {
7665 // created from xtype...
7668 el = config.container;
7672 items = config.items;
7676 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7679 this.renderButtons(this.el);
7682 // supprot items array.
7684 Roo.each(items, function(e) {
7685 this.add(Roo.factory(e));
7690 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7692 * @cfg {Roo.data.Store} dataSource
7693 * The underlying data store providing the paged data
7696 * @cfg {String/HTMLElement/Element} container
7697 * container The id or element that will contain the toolbar
7700 * @cfg {Boolean} displayInfo
7701 * True to display the displayMsg (defaults to false)
7704 * @cfg {Number} pageSize
7705 * The number of records to display per page (defaults to 20)
7709 * @cfg {String} displayMsg
7710 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7712 displayMsg : 'Displaying {0} - {1} of {2}',
7714 * @cfg {String} emptyMsg
7715 * The message to display when no records are found (defaults to "No data to display")
7717 emptyMsg : 'No data to display',
7719 * Customizable piece of the default paging text (defaults to "Page")
7722 beforePageText : "Page",
7724 * Customizable piece of the default paging text (defaults to "of %0")
7727 afterPageText : "of {0}",
7729 * Customizable piece of the default paging text (defaults to "First Page")
7732 firstText : "First Page",
7734 * Customizable piece of the default paging text (defaults to "Previous Page")
7737 prevText : "Previous Page",
7739 * Customizable piece of the default paging text (defaults to "Next Page")
7742 nextText : "Next Page",
7744 * Customizable piece of the default paging text (defaults to "Last Page")
7747 lastText : "Last Page",
7749 * Customizable piece of the default paging text (defaults to "Refresh")
7752 refreshText : "Refresh",
7755 renderButtons : function(el){
7756 Roo.PagingToolbar.superclass.render.call(this, el);
7757 this.first = this.addButton({
7758 tooltip: this.firstText,
7759 cls: "x-btn-icon x-grid-page-first",
7761 handler: this.onClick.createDelegate(this, ["first"])
7763 this.prev = this.addButton({
7764 tooltip: this.prevText,
7765 cls: "x-btn-icon x-grid-page-prev",
7767 handler: this.onClick.createDelegate(this, ["prev"])
7769 //this.addSeparator();
7770 this.add(this.beforePageText);
7771 this.field = Roo.get(this.addDom({
7776 cls: "x-grid-page-number"
7778 this.field.on("keydown", this.onPagingKeydown, this);
7779 this.field.on("focus", function(){this.dom.select();});
7780 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7781 this.field.setHeight(18);
7782 //this.addSeparator();
7783 this.next = this.addButton({
7784 tooltip: this.nextText,
7785 cls: "x-btn-icon x-grid-page-next",
7787 handler: this.onClick.createDelegate(this, ["next"])
7789 this.last = this.addButton({
7790 tooltip: this.lastText,
7791 cls: "x-btn-icon x-grid-page-last",
7793 handler: this.onClick.createDelegate(this, ["last"])
7795 //this.addSeparator();
7796 this.loading = this.addButton({
7797 tooltip: this.refreshText,
7798 cls: "x-btn-icon x-grid-loading",
7799 handler: this.onClick.createDelegate(this, ["refresh"])
7802 if(this.displayInfo){
7803 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7808 updateInfo : function(){
7810 var count = this.ds.getCount();
7811 var msg = count == 0 ?
7815 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
7817 this.displayEl.update(msg);
7822 onLoad : function(ds, r, o){
7823 this.cursor = o.params ? o.params.start : 0;
7824 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7826 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7827 this.field.dom.value = ap;
7828 this.first.setDisabled(ap == 1);
7829 this.prev.setDisabled(ap == 1);
7830 this.next.setDisabled(ap == ps);
7831 this.last.setDisabled(ap == ps);
7832 this.loading.enable();
7837 getPageData : function(){
7838 var total = this.ds.getTotalCount();
7841 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7842 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7847 onLoadError : function(){
7848 this.loading.enable();
7852 onPagingKeydown : function(e){
7854 var d = this.getPageData();
7856 var v = this.field.dom.value, pageNum;
7857 if(!v || isNaN(pageNum = parseInt(v, 10))){
7858 this.field.dom.value = d.activePage;
7861 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7862 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7865 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))
7867 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7868 this.field.dom.value = pageNum;
7869 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7872 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7874 var v = this.field.dom.value, pageNum;
7875 var increment = (e.shiftKey) ? 10 : 1;
7876 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7879 if(!v || isNaN(pageNum = parseInt(v, 10))) {
7880 this.field.dom.value = d.activePage;
7883 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7885 this.field.dom.value = parseInt(v, 10) + increment;
7886 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7887 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7894 beforeLoad : function(){
7896 this.loading.disable();
7901 onClick : function(which){
7905 ds.load({params:{start: 0, limit: this.pageSize}});
7908 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7911 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7914 var total = ds.getTotalCount();
7915 var extra = total % this.pageSize;
7916 var lastStart = extra ? (total - extra) : total-this.pageSize;
7917 ds.load({params:{start: lastStart, limit: this.pageSize}});
7920 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7926 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7927 * @param {Roo.data.Store} store The data store to unbind
7929 unbind : function(ds){
7930 ds.un("beforeload", this.beforeLoad, this);
7931 ds.un("load", this.onLoad, this);
7932 ds.un("loadexception", this.onLoadError, this);
7933 ds.un("remove", this.updateInfo, this);
7934 ds.un("add", this.updateInfo, this);
7935 this.ds = undefined;
7939 * Binds the paging toolbar to the specified {@link Roo.data.Store}
7940 * @param {Roo.data.Store} store The data store to bind
7942 bind : function(ds){
7943 ds.on("beforeload", this.beforeLoad, this);
7944 ds.on("load", this.onLoad, this);
7945 ds.on("loadexception", this.onLoadError, this);
7946 ds.on("remove", this.updateInfo, this);
7947 ds.on("add", this.updateInfo, this);
7952 * Ext JS Library 1.1.1
7953 * Copyright(c) 2006-2007, Ext JS, LLC.
7955 * Originally Released Under LGPL - original licence link has changed is not relivant.
7958 * <script type="text/javascript">
7962 * @class Roo.Resizable
7963 * @extends Roo.util.Observable
7964 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7965 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7966 * 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
7967 * the element will be wrapped for you automatically.</p>
7968 * <p>Here is the list of valid resize handles:</p>
7971 ------ -------------------
7980 'hd' horizontal drag
7983 * <p>Here's an example showing the creation of a typical Resizable:</p>
7985 var resizer = new Roo.Resizable("element-id", {
7993 resizer.on("resize", myHandler);
7995 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7996 * resizer.east.setDisplayed(false);</p>
7997 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7998 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7999 * resize operation's new size (defaults to [0, 0])
8000 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8001 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8002 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8003 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8004 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8005 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8006 * @cfg {Number} width The width of the element in pixels (defaults to null)
8007 * @cfg {Number} height The height of the element in pixels (defaults to null)
8008 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8009 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8010 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8011 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8012 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8013 * in favor of the handles config option (defaults to false)
8014 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8015 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8016 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8017 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8018 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8019 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8020 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8021 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8022 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8023 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8024 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8026 * Create a new resizable component
8027 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8028 * @param {Object} config configuration options
8030 Roo.Resizable = function(el, config)
8032 this.el = Roo.get(el);
8034 if(config && config.wrap){
8035 config.resizeChild = this.el;
8036 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8037 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8038 this.el.setStyle("overflow", "hidden");
8039 this.el.setPositioning(config.resizeChild.getPositioning());
8040 config.resizeChild.clearPositioning();
8041 if(!config.width || !config.height){
8042 var csize = config.resizeChild.getSize();
8043 this.el.setSize(csize.width, csize.height);
8045 if(config.pinned && !config.adjustments){
8046 config.adjustments = "auto";
8050 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8051 this.proxy.unselectable();
8052 this.proxy.enableDisplayMode('block');
8054 Roo.apply(this, config);
8057 this.disableTrackOver = true;
8058 this.el.addClass("x-resizable-pinned");
8060 // if the element isn't positioned, make it relative
8061 var position = this.el.getStyle("position");
8062 if(position != "absolute" && position != "fixed"){
8063 this.el.setStyle("position", "relative");
8065 if(!this.handles){ // no handles passed, must be legacy style
8066 this.handles = 's,e,se';
8067 if(this.multiDirectional){
8068 this.handles += ',n,w';
8071 if(this.handles == "all"){
8072 this.handles = "n s e w ne nw se sw";
8074 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8075 var ps = Roo.Resizable.positions;
8076 for(var i = 0, len = hs.length; i < len; i++){
8077 if(hs[i] && ps[hs[i]]){
8078 var pos = ps[hs[i]];
8079 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8083 this.corner = this.southeast;
8085 // updateBox = the box can move..
8086 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8087 this.updateBox = true;
8090 this.activeHandle = null;
8092 if(this.resizeChild){
8093 if(typeof this.resizeChild == "boolean"){
8094 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8096 this.resizeChild = Roo.get(this.resizeChild, true);
8100 if(this.adjustments == "auto"){
8101 var rc = this.resizeChild;
8102 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8103 if(rc && (hw || hn)){
8104 rc.position("relative");
8105 rc.setLeft(hw ? hw.el.getWidth() : 0);
8106 rc.setTop(hn ? hn.el.getHeight() : 0);
8108 this.adjustments = [
8109 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8110 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8115 this.dd = this.dynamic ?
8116 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8117 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8123 * @event beforeresize
8124 * Fired before resize is allowed. Set enabled to false to cancel resize.
8125 * @param {Roo.Resizable} this
8126 * @param {Roo.EventObject} e The mousedown event
8128 "beforeresize" : true,
8132 * @param {Roo.Resizable} this
8133 * @param {Number} x The new x position
8134 * @param {Number} y The new y position
8135 * @param {Number} w The new w width
8136 * @param {Number} h The new h hight
8137 * @param {Roo.EventObject} e The mouseup event
8142 * Fired after a resize.
8143 * @param {Roo.Resizable} this
8144 * @param {Number} width The new width
8145 * @param {Number} height The new height
8146 * @param {Roo.EventObject} e The mouseup event
8151 if(this.width !== null && this.height !== null){
8152 this.resizeTo(this.width, this.height);
8154 this.updateChildSize();
8157 this.el.dom.style.zoom = 1;
8159 Roo.Resizable.superclass.constructor.call(this);
8162 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8163 resizeChild : false,
8164 adjustments : [0, 0],
8174 multiDirectional : false,
8175 disableTrackOver : false,
8176 easing : 'easeOutStrong',
8178 heightIncrement : 0,
8182 preserveRatio : false,
8189 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8191 constrainTo: undefined,
8193 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8195 resizeRegion: undefined,
8199 * Perform a manual resize
8200 * @param {Number} width
8201 * @param {Number} height
8203 resizeTo : function(width, height){
8204 this.el.setSize(width, height);
8205 this.updateChildSize();
8206 this.fireEvent("resize", this, width, height, null);
8210 startSizing : function(e, handle){
8211 this.fireEvent("beforeresize", this, e);
8212 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8215 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8216 this.overlay.unselectable();
8217 this.overlay.enableDisplayMode("block");
8218 this.overlay.on("mousemove", this.onMouseMove, this);
8219 this.overlay.on("mouseup", this.onMouseUp, this);
8221 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8223 this.resizing = true;
8224 this.startBox = this.el.getBox();
8225 this.startPoint = e.getXY();
8226 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8227 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8229 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8230 this.overlay.show();
8232 if(this.constrainTo) {
8233 var ct = Roo.get(this.constrainTo);
8234 this.resizeRegion = ct.getRegion().adjust(
8235 ct.getFrameWidth('t'),
8236 ct.getFrameWidth('l'),
8237 -ct.getFrameWidth('b'),
8238 -ct.getFrameWidth('r')
8242 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8244 this.proxy.setBox(this.startBox);
8246 this.proxy.setStyle('visibility', 'visible');
8252 onMouseDown : function(handle, e){
8255 this.activeHandle = handle;
8256 this.startSizing(e, handle);
8261 onMouseUp : function(e){
8262 var size = this.resizeElement();
8263 this.resizing = false;
8265 this.overlay.hide();
8267 this.fireEvent("resize", this, size.width, size.height, e);
8271 updateChildSize : function(){
8273 if(this.resizeChild){
8275 var child = this.resizeChild;
8276 var adj = this.adjustments;
8277 if(el.dom.offsetWidth){
8278 var b = el.getSize(true);
8279 child.setSize(b.width+adj[0], b.height+adj[1]);
8281 // Second call here for IE
8282 // The first call enables instant resizing and
8283 // the second call corrects scroll bars if they
8286 setTimeout(function(){
8287 if(el.dom.offsetWidth){
8288 var b = el.getSize(true);
8289 child.setSize(b.width+adj[0], b.height+adj[1]);
8297 snap : function(value, inc, min){
8298 if(!inc || !value) {
8301 var newValue = value;
8302 var m = value % inc;
8305 newValue = value + (inc-m);
8307 newValue = value - m;
8310 return Math.max(min, newValue);
8314 resizeElement : function(){
8315 var box = this.proxy.getBox();
8317 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8319 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8321 this.updateChildSize();
8329 constrain : function(v, diff, m, mx){
8332 }else if(v - diff > mx){
8339 onMouseMove : function(e){
8342 try{// try catch so if something goes wrong the user doesn't get hung
8344 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8348 //var curXY = this.startPoint;
8349 var curSize = this.curSize || this.startBox;
8350 var x = this.startBox.x, y = this.startBox.y;
8352 var w = curSize.width, h = curSize.height;
8354 var mw = this.minWidth, mh = this.minHeight;
8355 var mxw = this.maxWidth, mxh = this.maxHeight;
8356 var wi = this.widthIncrement;
8357 var hi = this.heightIncrement;
8359 var eventXY = e.getXY();
8360 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8361 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8363 var pos = this.activeHandle.position;
8368 w = Math.min(Math.max(mw, w), mxw);
8373 h = Math.min(Math.max(mh, h), mxh);
8378 w = Math.min(Math.max(mw, w), mxw);
8379 h = Math.min(Math.max(mh, h), mxh);
8382 diffY = this.constrain(h, diffY, mh, mxh);
8389 var adiffX = Math.abs(diffX);
8390 var sub = (adiffX % wi); // how much
8391 if (sub > (wi/2)) { // far enough to snap
8392 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8394 // remove difference..
8395 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8399 x = Math.max(this.minX, x);
8402 diffX = this.constrain(w, diffX, mw, mxw);
8408 w = Math.min(Math.max(mw, w), mxw);
8409 diffY = this.constrain(h, diffY, mh, mxh);
8414 diffX = this.constrain(w, diffX, mw, mxw);
8415 diffY = this.constrain(h, diffY, mh, mxh);
8422 diffX = this.constrain(w, diffX, mw, mxw);
8424 h = Math.min(Math.max(mh, h), mxh);
8430 var sw = this.snap(w, wi, mw);
8431 var sh = this.snap(h, hi, mh);
8432 if(sw != w || sh != h){
8455 if(this.preserveRatio){
8460 h = Math.min(Math.max(mh, h), mxh);
8465 w = Math.min(Math.max(mw, w), mxw);
8470 w = Math.min(Math.max(mw, w), mxw);
8476 w = Math.min(Math.max(mw, w), mxw);
8482 h = Math.min(Math.max(mh, h), mxh);
8490 h = Math.min(Math.max(mh, h), mxh);
8500 h = Math.min(Math.max(mh, h), mxh);
8508 if (pos == 'hdrag') {
8511 this.proxy.setBounds(x, y, w, h);
8513 this.resizeElement();
8517 this.fireEvent("resizing", this, x, y, w, h, e);
8521 handleOver : function(){
8523 this.el.addClass("x-resizable-over");
8528 handleOut : function(){
8530 this.el.removeClass("x-resizable-over");
8535 * Returns the element this component is bound to.
8536 * @return {Roo.Element}
8543 * Returns the resizeChild element (or null).
8544 * @return {Roo.Element}
8546 getResizeChild : function(){
8547 return this.resizeChild;
8549 groupHandler : function()
8554 * Destroys this resizable. If the element was wrapped and
8555 * removeEl is not true then the element remains.
8556 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8558 destroy : function(removeEl){
8559 this.proxy.remove();
8561 this.overlay.removeAllListeners();
8562 this.overlay.remove();
8564 var ps = Roo.Resizable.positions;
8566 if(typeof ps[k] != "function" && this[ps[k]]){
8567 var h = this[ps[k]];
8568 h.el.removeAllListeners();
8580 // hash to map config positions to true positions
8581 Roo.Resizable.positions = {
8582 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
8587 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8589 // only initialize the template if resizable is used
8590 var tpl = Roo.DomHelper.createTemplate(
8591 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8594 Roo.Resizable.Handle.prototype.tpl = tpl;
8596 this.position = pos;
8598 // show north drag fro topdra
8599 var handlepos = pos == 'hdrag' ? 'north' : pos;
8601 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8602 if (pos == 'hdrag') {
8603 this.el.setStyle('cursor', 'pointer');
8605 this.el.unselectable();
8607 this.el.setOpacity(0);
8609 this.el.on("mousedown", this.onMouseDown, this);
8610 if(!disableTrackOver){
8611 this.el.on("mouseover", this.onMouseOver, this);
8612 this.el.on("mouseout", this.onMouseOut, this);
8617 Roo.Resizable.Handle.prototype = {
8618 afterResize : function(rz){
8623 onMouseDown : function(e){
8624 this.rz.onMouseDown(this, e);
8627 onMouseOver : function(e){
8628 this.rz.handleOver(this, e);
8631 onMouseOut : function(e){
8632 this.rz.handleOut(this, e);
8636 * Ext JS Library 1.1.1
8637 * Copyright(c) 2006-2007, Ext JS, LLC.
8639 * Originally Released Under LGPL - original licence link has changed is not relivant.
8642 * <script type="text/javascript">
8647 * @extends Roo.Component
8648 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8650 * Create a new Editor
8651 * @param {Roo.form.Field} field The Field object (or descendant)
8652 * @param {Object} config The config object
8654 Roo.Editor = function(field, config){
8655 Roo.Editor.superclass.constructor.call(this, config);
8659 * @event beforestartedit
8660 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
8661 * false from the handler of this event.
8662 * @param {Editor} this
8663 * @param {Roo.Element} boundEl The underlying element bound to this editor
8664 * @param {Mixed} value The field value being set
8666 "beforestartedit" : true,
8669 * Fires when this editor is displayed
8670 * @param {Roo.Element} boundEl The underlying element bound to this editor
8671 * @param {Mixed} value The starting field value
8675 * @event beforecomplete
8676 * Fires after a change has been made to the field, but before the change is reflected in the underlying
8677 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
8678 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8679 * event will not fire since no edit actually occurred.
8680 * @param {Editor} this
8681 * @param {Mixed} value The current field value
8682 * @param {Mixed} startValue The original field value
8684 "beforecomplete" : true,
8687 * Fires after editing is complete and any changed value has been written to the underlying field.
8688 * @param {Editor} this
8689 * @param {Mixed} value The current field value
8690 * @param {Mixed} startValue The original field value
8695 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8696 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8697 * @param {Roo.form.Field} this
8698 * @param {Roo.EventObject} e The event object
8704 Roo.extend(Roo.Editor, Roo.Component, {
8706 * @cfg {Boolean/String} autosize
8707 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8708 * or "height" to adopt the height only (defaults to false)
8711 * @cfg {Boolean} revertInvalid
8712 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8713 * validation fails (defaults to true)
8716 * @cfg {Boolean} ignoreNoChange
8717 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8718 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
8719 * will never be ignored.
8722 * @cfg {Boolean} hideEl
8723 * False to keep the bound element visible while the editor is displayed (defaults to true)
8726 * @cfg {Mixed} value
8727 * The data value of the underlying field (defaults to "")
8731 * @cfg {String} alignment
8732 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8736 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8737 * for bottom-right shadow (defaults to "frame")
8741 * @cfg {Boolean} constrain True to constrain the editor to the viewport
8745 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8747 completeOnEnter : false,
8749 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8751 cancelOnEsc : false,
8753 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8758 onRender : function(ct, position){
8759 this.el = new Roo.Layer({
8760 shadow: this.shadow,
8766 constrain: this.constrain
8768 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8769 if(this.field.msgTarget != 'title'){
8770 this.field.msgTarget = 'qtip';
8772 this.field.render(this.el);
8774 this.field.el.dom.setAttribute('autocomplete', 'off');
8776 this.field.on("specialkey", this.onSpecialKey, this);
8777 if(this.swallowKeys){
8778 this.field.el.swallowEvent(['keydown','keypress']);
8781 this.field.on("blur", this.onBlur, this);
8782 if(this.field.grow){
8783 this.field.on("autosize", this.el.sync, this.el, {delay:1});
8787 onSpecialKey : function(field, e)
8789 //Roo.log('editor onSpecialKey');
8790 if(this.completeOnEnter && e.getKey() == e.ENTER){
8792 this.completeEdit();
8795 // do not fire special key otherwise it might hide close the editor...
8796 if(e.getKey() == e.ENTER){
8799 if(this.cancelOnEsc && e.getKey() == e.ESC){
8803 this.fireEvent('specialkey', field, e);
8808 * Starts the editing process and shows the editor.
8809 * @param {String/HTMLElement/Element} el The element to edit
8810 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8811 * to the innerHTML of el.
8813 startEdit : function(el, value){
8815 this.completeEdit();
8817 this.boundEl = Roo.get(el);
8818 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8820 this.render(this.parentEl || document.body);
8822 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8825 this.startValue = v;
8826 this.field.setValue(v);
8828 var sz = this.boundEl.getSize();
8829 switch(this.autoSize){
8831 this.setSize(sz.width, "");
8834 this.setSize("", sz.height);
8837 this.setSize(sz.width, sz.height);
8840 this.el.alignTo(this.boundEl, this.alignment);
8841 this.editing = true;
8843 Roo.QuickTips.disable();
8849 * Sets the height and width of this editor.
8850 * @param {Number} width The new width
8851 * @param {Number} height The new height
8853 setSize : function(w, h){
8854 this.field.setSize(w, h);
8861 * Realigns the editor to the bound field based on the current alignment config value.
8863 realign : function(){
8864 this.el.alignTo(this.boundEl, this.alignment);
8868 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8869 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8871 completeEdit : function(remainVisible){
8875 var v = this.getValue();
8876 if(this.revertInvalid !== false && !this.field.isValid()){
8877 v = this.startValue;
8878 this.cancelEdit(true);
8880 if(String(v) === String(this.startValue) && this.ignoreNoChange){
8881 this.editing = false;
8885 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8886 this.editing = false;
8887 if(this.updateEl && this.boundEl){
8888 this.boundEl.update(v);
8890 if(remainVisible !== true){
8893 this.fireEvent("complete", this, v, this.startValue);
8898 onShow : function(){
8900 if(this.hideEl !== false){
8901 this.boundEl.hide();
8904 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8905 this.fixIEFocus = true;
8906 this.deferredFocus.defer(50, this);
8910 this.fireEvent("startedit", this.boundEl, this.startValue);
8913 deferredFocus : function(){
8920 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
8921 * reverted to the original starting value.
8922 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8923 * cancel (defaults to false)
8925 cancelEdit : function(remainVisible){
8927 this.setValue(this.startValue);
8928 if(remainVisible !== true){
8935 onBlur : function(){
8936 if(this.allowBlur !== true && this.editing){
8937 this.completeEdit();
8942 onHide : function(){
8944 this.completeEdit();
8948 if(this.field.collapse){
8949 this.field.collapse();
8952 if(this.hideEl !== false){
8953 this.boundEl.show();
8956 Roo.QuickTips.enable();
8961 * Sets the data value of the editor
8962 * @param {Mixed} value Any valid value supported by the underlying field
8964 setValue : function(v){
8965 this.field.setValue(v);
8969 * Gets the data value of the editor
8970 * @return {Mixed} The data value
8972 getValue : function(){
8973 return this.field.getValue();
8977 * Ext JS Library 1.1.1
8978 * Copyright(c) 2006-2007, Ext JS, LLC.
8980 * Originally Released Under LGPL - original licence link has changed is not relivant.
8983 * <script type="text/javascript">
8987 * @class Roo.BasicDialog
8988 * @extends Roo.util.Observable
8989 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
8991 var dlg = new Roo.BasicDialog("my-dlg", {
9000 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9001 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9002 dlg.addButton('Cancel', dlg.hide, dlg);
9005 <b>A Dialog should always be a direct child of the body element.</b>
9006 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9007 * @cfg {String} title Default text to display in the title bar (defaults to null)
9008 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9009 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9010 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9011 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9012 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9013 * (defaults to null with no animation)
9014 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9015 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9016 * property for valid values (defaults to 'all')
9017 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9018 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9019 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9020 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9021 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9022 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9023 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9024 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9025 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9026 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9027 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9028 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9029 * draggable = true (defaults to false)
9030 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9031 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9032 * shadow (defaults to false)
9033 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9034 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9035 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9036 * @cfg {Array} buttons Array of buttons
9037 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9039 * Create a new BasicDialog.
9040 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9041 * @param {Object} config Configuration options
9043 Roo.BasicDialog = function(el, config){
9044 this.el = Roo.get(el);
9045 var dh = Roo.DomHelper;
9046 if(!this.el && config && config.autoCreate){
9047 if(typeof config.autoCreate == "object"){
9048 if(!config.autoCreate.id){
9049 config.autoCreate.id = el;
9051 this.el = dh.append(document.body,
9052 config.autoCreate, true);
9054 this.el = dh.append(document.body,
9055 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9059 el.setDisplayed(true);
9060 el.hide = this.hideAction;
9062 el.addClass("x-dlg");
9064 Roo.apply(this, config);
9066 this.proxy = el.createProxy("x-dlg-proxy");
9067 this.proxy.hide = this.hideAction;
9068 this.proxy.setOpacity(.5);
9072 el.setWidth(config.width);
9075 el.setHeight(config.height);
9077 this.size = el.getSize();
9078 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9079 this.xy = [config.x,config.y];
9081 this.xy = el.getCenterXY(true);
9083 /** The header element @type Roo.Element */
9084 this.header = el.child("> .x-dlg-hd");
9085 /** The body element @type Roo.Element */
9086 this.body = el.child("> .x-dlg-bd");
9087 /** The footer element @type Roo.Element */
9088 this.footer = el.child("> .x-dlg-ft");
9091 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9094 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9097 this.header.unselectable();
9099 this.header.update(this.title);
9101 // this element allows the dialog to be focused for keyboard event
9102 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9103 this.focusEl.swallowEvent("click", true);
9105 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9107 // wrap the body and footer for special rendering
9108 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9110 this.bwrap.dom.appendChild(this.footer.dom);
9113 this.bg = this.el.createChild({
9114 tag: "div", cls:"x-dlg-bg",
9115 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9117 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9120 if(this.autoScroll !== false && !this.autoTabs){
9121 this.body.setStyle("overflow", "auto");
9124 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9126 if(this.closable !== false){
9127 this.el.addClass("x-dlg-closable");
9128 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9129 this.close.on("click", this.closeClick, this);
9130 this.close.addClassOnOver("x-dlg-close-over");
9132 if(this.collapsible !== false){
9133 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9134 this.collapseBtn.on("click", this.collapseClick, this);
9135 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9136 this.header.on("dblclick", this.collapseClick, this);
9138 if(this.resizable !== false){
9139 this.el.addClass("x-dlg-resizable");
9140 this.resizer = new Roo.Resizable(el, {
9141 minWidth: this.minWidth || 80,
9142 minHeight:this.minHeight || 80,
9143 handles: this.resizeHandles || "all",
9146 this.resizer.on("beforeresize", this.beforeResize, this);
9147 this.resizer.on("resize", this.onResize, this);
9149 if(this.draggable !== false){
9150 el.addClass("x-dlg-draggable");
9151 if (!this.proxyDrag) {
9152 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9155 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9157 dd.setHandleElId(this.header.id);
9158 dd.endDrag = this.endMove.createDelegate(this);
9159 dd.startDrag = this.startMove.createDelegate(this);
9160 dd.onDrag = this.onDrag.createDelegate(this);
9165 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9166 this.mask.enableDisplayMode("block");
9168 this.el.addClass("x-dlg-modal");
9171 this.shadow = new Roo.Shadow({
9172 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9173 offset : this.shadowOffset
9176 this.shadowOffset = 0;
9178 if(Roo.useShims && this.shim !== false){
9179 this.shim = this.el.createShim();
9180 this.shim.hide = this.hideAction;
9189 var bts= this.buttons;
9191 Roo.each(bts, function(b) {
9200 * Fires when a key is pressed
9201 * @param {Roo.BasicDialog} this
9202 * @param {Roo.EventObject} e
9207 * Fires when this dialog is moved by the user.
9208 * @param {Roo.BasicDialog} this
9209 * @param {Number} x The new page X
9210 * @param {Number} y The new page Y
9215 * Fires when this dialog is resized by the user.
9216 * @param {Roo.BasicDialog} this
9217 * @param {Number} width The new width
9218 * @param {Number} height The new height
9223 * Fires before this dialog is hidden.
9224 * @param {Roo.BasicDialog} this
9226 "beforehide" : true,
9229 * Fires when this dialog is hidden.
9230 * @param {Roo.BasicDialog} this
9235 * Fires before this dialog is shown.
9236 * @param {Roo.BasicDialog} this
9238 "beforeshow" : true,
9241 * Fires when this dialog is shown.
9242 * @param {Roo.BasicDialog} this
9246 el.on("keydown", this.onKeyDown, this);
9247 el.on("mousedown", this.toFront, this);
9248 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9250 Roo.DialogManager.register(this);
9251 Roo.BasicDialog.superclass.constructor.call(this);
9254 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9255 shadowOffset: Roo.isIE ? 6 : 5,
9259 defaultButton: null,
9260 buttonAlign: "right",
9265 * Sets the dialog title text
9266 * @param {String} text The title text to display
9267 * @return {Roo.BasicDialog} this
9269 setTitle : function(text){
9270 this.header.update(text);
9275 closeClick : function(){
9280 collapseClick : function(){
9281 this[this.collapsed ? "expand" : "collapse"]();
9285 * Collapses the dialog to its minimized state (only the title bar is visible).
9286 * Equivalent to the user clicking the collapse dialog button.
9288 collapse : function(){
9289 if(!this.collapsed){
9290 this.collapsed = true;
9291 this.el.addClass("x-dlg-collapsed");
9292 this.restoreHeight = this.el.getHeight();
9293 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9298 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9299 * clicking the expand dialog button.
9301 expand : function(){
9303 this.collapsed = false;
9304 this.el.removeClass("x-dlg-collapsed");
9305 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9310 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9311 * @return {Roo.TabPanel} The tabs component
9313 initTabs : function(){
9314 var tabs = this.getTabs();
9315 while(tabs.getTab(0)){
9318 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9320 tabs.addTab(Roo.id(dom), dom.title);
9328 beforeResize : function(){
9329 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9333 onResize : function(){
9335 this.syncBodyHeight();
9336 this.adjustAssets();
9338 this.fireEvent("resize", this, this.size.width, this.size.height);
9342 onKeyDown : function(e){
9343 if(this.isVisible()){
9344 this.fireEvent("keydown", this, e);
9349 * Resizes the dialog.
9350 * @param {Number} width
9351 * @param {Number} height
9352 * @return {Roo.BasicDialog} this
9354 resizeTo : function(width, height){
9355 this.el.setSize(width, height);
9356 this.size = {width: width, height: height};
9357 this.syncBodyHeight();
9358 if(this.fixedcenter){
9361 if(this.isVisible()){
9363 this.adjustAssets();
9365 this.fireEvent("resize", this, width, height);
9371 * Resizes the dialog to fit the specified content size.
9372 * @param {Number} width
9373 * @param {Number} height
9374 * @return {Roo.BasicDialog} this
9376 setContentSize : function(w, h){
9377 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9378 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9379 //if(!this.el.isBorderBox()){
9380 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9381 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9384 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9385 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9387 this.resizeTo(w, h);
9392 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9393 * executed in response to a particular key being pressed while the dialog is active.
9394 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9395 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9396 * @param {Function} fn The function to call
9397 * @param {Object} scope (optional) The scope of the function
9398 * @return {Roo.BasicDialog} this
9400 addKeyListener : function(key, fn, scope){
9401 var keyCode, shift, ctrl, alt;
9402 if(typeof key == "object" && !(key instanceof Array)){
9403 keyCode = key["key"];
9404 shift = key["shift"];
9410 var handler = function(dlg, e){
9411 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9413 if(keyCode instanceof Array){
9414 for(var i = 0, len = keyCode.length; i < len; i++){
9415 if(keyCode[i] == k){
9416 fn.call(scope || window, dlg, k, e);
9422 fn.call(scope || window, dlg, k, e);
9427 this.on("keydown", handler);
9432 * Returns the TabPanel component (creates it if it doesn't exist).
9433 * Note: If you wish to simply check for the existence of tabs without creating them,
9434 * check for a null 'tabs' property.
9435 * @return {Roo.TabPanel} The tabs component
9437 getTabs : function(){
9439 this.el.addClass("x-dlg-auto-tabs");
9440 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9441 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9447 * Adds a button to the footer section of the dialog.
9448 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9449 * object or a valid Roo.DomHelper element config
9450 * @param {Function} handler The function called when the button is clicked
9451 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9452 * @return {Roo.Button} The new button
9454 addButton : function(config, handler, scope){
9455 var dh = Roo.DomHelper;
9457 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9459 if(!this.btnContainer){
9460 var tb = this.footer.createChild({
9462 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9463 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9465 this.btnContainer = tb.firstChild.firstChild.firstChild;
9470 minWidth: this.minButtonWidth,
9473 if(typeof config == "string"){
9474 bconfig.text = config;
9477 bconfig.dhconfig = config;
9479 Roo.apply(bconfig, config);
9483 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9484 bconfig.position = Math.max(0, bconfig.position);
9485 fc = this.btnContainer.childNodes[bconfig.position];
9488 var btn = new Roo.Button(
9490 this.btnContainer.insertBefore(document.createElement("td"),fc)
9491 : this.btnContainer.appendChild(document.createElement("td")),
9492 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9495 this.syncBodyHeight();
9498 * Array of all the buttons that have been added to this dialog via addButton
9503 this.buttons.push(btn);
9508 * Sets the default button to be focused when the dialog is displayed.
9509 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9510 * @return {Roo.BasicDialog} this
9512 setDefaultButton : function(btn){
9513 this.defaultButton = btn;
9518 getHeaderFooterHeight : function(safe){
9521 height += this.header.getHeight();
9524 var fm = this.footer.getMargins();
9525 height += (this.footer.getHeight()+fm.top+fm.bottom);
9527 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9528 height += this.centerBg.getPadding("tb");
9533 syncBodyHeight : function()
9535 var bd = this.body, // the text
9536 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9538 var height = this.size.height - this.getHeaderFooterHeight(false);
9539 bd.setHeight(height-bd.getMargins("tb"));
9540 var hh = this.header.getHeight();
9541 var h = this.size.height-hh;
9544 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9545 bw.setHeight(h-cb.getPadding("tb"));
9547 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9548 bd.setWidth(bw.getWidth(true));
9550 this.tabs.syncHeight();
9552 this.tabs.el.repaint();
9558 * Restores the previous state of the dialog if Roo.state is configured.
9559 * @return {Roo.BasicDialog} this
9561 restoreState : function(){
9562 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9563 if(box && box.width){
9564 this.xy = [box.x, box.y];
9565 this.resizeTo(box.width, box.height);
9571 beforeShow : function(){
9573 if(this.fixedcenter){
9574 this.xy = this.el.getCenterXY(true);
9577 Roo.get(document.body).addClass("x-body-masked");
9578 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9585 animShow : function(){
9586 var b = Roo.get(this.animateTarget).getBox();
9587 this.proxy.setSize(b.width, b.height);
9588 this.proxy.setLocation(b.x, b.y);
9590 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9591 true, .35, this.showEl.createDelegate(this));
9596 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9597 * @return {Roo.BasicDialog} this
9599 show : function(animateTarget){
9600 if (this.fireEvent("beforeshow", this) === false){
9603 if(this.syncHeightBeforeShow){
9604 this.syncBodyHeight();
9605 }else if(this.firstShow){
9606 this.firstShow = false;
9607 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9609 this.animateTarget = animateTarget || this.animateTarget;
9610 if(!this.el.isVisible()){
9612 if(this.animateTarget && Roo.get(this.animateTarget)){
9622 showEl : function(){
9624 this.el.setXY(this.xy);
9626 this.adjustAssets(true);
9629 // IE peekaboo bug - fix found by Dave Fenwick
9633 this.fireEvent("show", this);
9637 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
9638 * dialog itself will receive focus.
9641 if(this.defaultButton){
9642 this.defaultButton.focus();
9644 this.focusEl.focus();
9649 constrainXY : function(){
9650 if(this.constraintoviewport !== false){
9653 var s = this.container.getSize();
9654 this.viewSize = [s.width, s.height];
9656 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9659 var s = Roo.get(this.container||document).getScroll();
9661 var x = this.xy[0], y = this.xy[1];
9662 var w = this.size.width, h = this.size.height;
9663 var vw = this.viewSize[0], vh = this.viewSize[1];
9664 // only move it if it needs it
9666 // first validate right/bottom
9667 if(x + w > vw+s.left){
9671 if(y + h > vh+s.top){
9675 // then make sure top/left isn't negative
9687 if(this.isVisible()){
9688 this.el.setLocation(x, y);
9689 this.adjustAssets();
9696 onDrag : function(){
9697 if(!this.proxyDrag){
9698 this.xy = this.el.getXY();
9699 this.adjustAssets();
9704 adjustAssets : function(doShow){
9705 var x = this.xy[0], y = this.xy[1];
9706 var w = this.size.width, h = this.size.height;
9707 if(doShow === true){
9709 this.shadow.show(this.el);
9715 if(this.shadow && this.shadow.isVisible()){
9716 this.shadow.show(this.el);
9718 if(this.shim && this.shim.isVisible()){
9719 this.shim.setBounds(x, y, w, h);
9724 adjustViewport : function(w, h){
9726 w = Roo.lib.Dom.getViewWidth();
9727 h = Roo.lib.Dom.getViewHeight();
9730 this.viewSize = [w, h];
9731 if(this.modal && this.mask.isVisible()){
9732 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9733 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9735 if(this.isVisible()){
9741 * Destroys this dialog and all its supporting elements (including any tabs, shim,
9742 * shadow, proxy, mask, etc.) Also removes all event listeners.
9743 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9745 destroy : function(removeEl){
9746 if(this.isVisible()){
9747 this.animateTarget = null;
9750 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9752 this.tabs.destroy(removeEl);
9765 for(var i = 0, len = this.buttons.length; i < len; i++){
9766 this.buttons[i].destroy();
9769 this.el.removeAllListeners();
9770 if(removeEl === true){
9774 Roo.DialogManager.unregister(this);
9778 startMove : function(){
9782 if(this.constraintoviewport !== false){
9783 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9788 endMove : function(){
9789 if(!this.proxyDrag){
9790 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9792 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9796 this.adjustAssets();
9798 this.fireEvent("move", this, this.xy[0], this.xy[1]);
9802 * Brings this dialog to the front of any other visible dialogs
9803 * @return {Roo.BasicDialog} this
9805 toFront : function(){
9806 Roo.DialogManager.bringToFront(this);
9811 * Sends this dialog to the back (under) of any other visible dialogs
9812 * @return {Roo.BasicDialog} this
9814 toBack : function(){
9815 Roo.DialogManager.sendToBack(this);
9820 * Centers this dialog in the viewport
9821 * @return {Roo.BasicDialog} this
9823 center : function(){
9824 var xy = this.el.getCenterXY(true);
9825 this.moveTo(xy[0], xy[1]);
9830 * Moves the dialog's top-left corner to the specified point
9833 * @return {Roo.BasicDialog} this
9835 moveTo : function(x, y){
9837 if(this.isVisible()){
9838 this.el.setXY(this.xy);
9839 this.adjustAssets();
9845 * Aligns the dialog to the specified element
9846 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9847 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9848 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9849 * @return {Roo.BasicDialog} this
9851 alignTo : function(element, position, offsets){
9852 this.xy = this.el.getAlignToXY(element, position, offsets);
9853 if(this.isVisible()){
9854 this.el.setXY(this.xy);
9855 this.adjustAssets();
9861 * Anchors an element to another element and realigns it when the window is resized.
9862 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9863 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9864 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9865 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9866 * is a number, it is used as the buffer delay (defaults to 50ms).
9867 * @return {Roo.BasicDialog} this
9869 anchorTo : function(el, alignment, offsets, monitorScroll){
9870 var action = function(){
9871 this.alignTo(el, alignment, offsets);
9873 Roo.EventManager.onWindowResize(action, this);
9874 var tm = typeof monitorScroll;
9875 if(tm != 'undefined'){
9876 Roo.EventManager.on(window, 'scroll', action, this,
9877 {buffer: tm == 'number' ? monitorScroll : 50});
9884 * Returns true if the dialog is visible
9887 isVisible : function(){
9888 return this.el.isVisible();
9892 animHide : function(callback){
9893 var b = Roo.get(this.animateTarget).getBox();
9895 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9897 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9898 this.hideEl.createDelegate(this, [callback]));
9903 * @param {Function} callback (optional) Function to call when the dialog is hidden
9904 * @return {Roo.BasicDialog} this
9906 hide : function(callback){
9907 if (this.fireEvent("beforehide", this) === false){
9916 // sometimes animateTarget seems to get set.. causing problems...
9917 // this just double checks..
9918 if(this.animateTarget && Roo.get(this.animateTarget)) {
9919 this.animHide(callback);
9922 this.hideEl(callback);
9928 hideEl : function(callback){
9932 Roo.get(document.body).removeClass("x-body-masked");
9934 this.fireEvent("hide", this);
9935 if(typeof callback == "function"){
9941 hideAction : function(){
9942 this.setLeft("-10000px");
9943 this.setTop("-10000px");
9944 this.setStyle("visibility", "hidden");
9948 refreshSize : function(){
9949 this.size = this.el.getSize();
9950 this.xy = this.el.getXY();
9951 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9955 // z-index is managed by the DialogManager and may be overwritten at any time
9956 setZIndex : function(index){
9958 this.mask.setStyle("z-index", index);
9961 this.shim.setStyle("z-index", ++index);
9964 this.shadow.setZIndex(++index);
9966 this.el.setStyle("z-index", ++index);
9968 this.proxy.setStyle("z-index", ++index);
9971 this.resizer.proxy.setStyle("z-index", ++index);
9974 this.lastZIndex = index;
9978 * Returns the element for this dialog
9979 * @return {Roo.Element} The underlying dialog Element
9987 * @class Roo.DialogManager
9988 * Provides global access to BasicDialogs that have been created and
9989 * support for z-indexing (layering) multiple open dialogs.
9991 Roo.DialogManager = function(){
9993 var accessList = [];
9997 var sortDialogs = function(d1, d2){
9998 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10002 var orderDialogs = function(){
10003 accessList.sort(sortDialogs);
10004 var seed = Roo.DialogManager.zseed;
10005 for(var i = 0, len = accessList.length; i < len; i++){
10006 var dlg = accessList[i];
10008 dlg.setZIndex(seed + (i*10));
10015 * The starting z-index for BasicDialogs (defaults to 9000)
10016 * @type Number The z-index value
10021 register : function(dlg){
10022 list[dlg.id] = dlg;
10023 accessList.push(dlg);
10027 unregister : function(dlg){
10028 delete list[dlg.id];
10031 if(!accessList.indexOf){
10032 for( i = 0, len = accessList.length; i < len; i++){
10033 if(accessList[i] == dlg){
10034 accessList.splice(i, 1);
10039 i = accessList.indexOf(dlg);
10041 accessList.splice(i, 1);
10047 * Gets a registered dialog by id
10048 * @param {String/Object} id The id of the dialog or a dialog
10049 * @return {Roo.BasicDialog} this
10051 get : function(id){
10052 return typeof id == "object" ? id : list[id];
10056 * Brings the specified dialog to the front
10057 * @param {String/Object} dlg The id of the dialog or a dialog
10058 * @return {Roo.BasicDialog} this
10060 bringToFront : function(dlg){
10061 dlg = this.get(dlg);
10064 dlg._lastAccess = new Date().getTime();
10071 * Sends the specified dialog to the back
10072 * @param {String/Object} dlg The id of the dialog or a dialog
10073 * @return {Roo.BasicDialog} this
10075 sendToBack : function(dlg){
10076 dlg = this.get(dlg);
10077 dlg._lastAccess = -(new Date().getTime());
10083 * Hides all dialogs
10085 hideAll : function(){
10086 for(var id in list){
10087 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10096 * @class Roo.LayoutDialog
10097 * @extends Roo.BasicDialog
10098 * @children Roo.ContentPanel
10099 * Dialog which provides adjustments for working with a layout in a Dialog.
10100 * Add your necessary layout config options to the dialog's config.<br>
10101 * Example usage (including a nested layout):
10104 dialog = new Roo.LayoutDialog("download-dlg", {
10113 // layout config merges with the dialog config
10115 tabPosition: "top",
10116 alwaysShowTabs: true
10119 dialog.addKeyListener(27, dialog.hide, dialog);
10120 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10121 dialog.addButton("Build It!", this.getDownload, this);
10123 // we can even add nested layouts
10124 var innerLayout = new Roo.BorderLayout("dl-inner", {
10134 innerLayout.beginUpdate();
10135 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10136 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10137 innerLayout.endUpdate(true);
10139 var layout = dialog.getLayout();
10140 layout.beginUpdate();
10141 layout.add("center", new Roo.ContentPanel("standard-panel",
10142 {title: "Download the Source", fitToFrame:true}));
10143 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10144 {title: "Build your own roo.js"}));
10145 layout.getRegion("center").showPanel(sp);
10146 layout.endUpdate();
10150 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10151 * @param {Object} config configuration options
10153 Roo.LayoutDialog = function(el, cfg){
10156 if (typeof(cfg) == 'undefined') {
10157 config = Roo.apply({}, el);
10158 // not sure why we use documentElement here.. - it should always be body.
10159 // IE7 borks horribly if we use documentElement.
10160 // webkit also does not like documentElement - it creates a body element...
10161 el = Roo.get( document.body || document.documentElement ).createChild();
10162 //config.autoCreate = true;
10166 config.autoTabs = false;
10167 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10168 this.body.setStyle({overflow:"hidden", position:"relative"});
10169 this.layout = new Roo.BorderLayout(this.body.dom, config);
10170 this.layout.monitorWindowResize = false;
10171 this.el.addClass("x-dlg-auto-layout");
10172 // fix case when center region overwrites center function
10173 this.center = Roo.BasicDialog.prototype.center;
10174 this.on("show", this.layout.layout, this.layout, true);
10175 if (config.items) {
10176 var xitems = config.items;
10177 delete config.items;
10178 Roo.each(xitems, this.addxtype, this);
10183 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10187 * @cfg {Roo.LayoutRegion} east
10190 * @cfg {Roo.LayoutRegion} west
10193 * @cfg {Roo.LayoutRegion} south
10196 * @cfg {Roo.LayoutRegion} north
10199 * @cfg {Roo.LayoutRegion} center
10202 * @cfg {Roo.Button} buttons[] Bottom buttons..
10207 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10210 endUpdate : function(){
10211 this.layout.endUpdate();
10215 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10218 beginUpdate : function(){
10219 this.layout.beginUpdate();
10223 * Get the BorderLayout for this dialog
10224 * @return {Roo.BorderLayout}
10226 getLayout : function(){
10227 return this.layout;
10230 showEl : function(){
10231 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10233 this.layout.layout();
10238 // Use the syncHeightBeforeShow config option to control this automatically
10239 syncBodyHeight : function(){
10240 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10241 if(this.layout){this.layout.layout();}
10245 * Add an xtype element (actually adds to the layout.)
10246 * @return {Object} xdata xtype object data.
10249 addxtype : function(c) {
10250 return this.layout.addxtype(c);
10254 * Ext JS Library 1.1.1
10255 * Copyright(c) 2006-2007, Ext JS, LLC.
10257 * Originally Released Under LGPL - original licence link has changed is not relivant.
10260 * <script type="text/javascript">
10264 * @class Roo.MessageBox
10265 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10269 Roo.Msg.alert('Status', 'Changes saved successfully.');
10271 // Prompt for user data:
10272 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10274 // process text value...
10278 // Show a dialog using config options:
10280 title:'Save Changes?',
10281 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10282 buttons: Roo.Msg.YESNOCANCEL,
10289 Roo.MessageBox = function(){
10290 var dlg, opt, mask, waitTimer;
10291 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10292 var buttons, activeTextEl, bwidth;
10295 var handleButton = function(button){
10297 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10301 var handleHide = function(){
10302 if(opt && opt.cls){
10303 dlg.el.removeClass(opt.cls);
10306 Roo.TaskMgr.stop(waitTimer);
10312 var updateButtons = function(b){
10315 buttons["ok"].hide();
10316 buttons["cancel"].hide();
10317 buttons["yes"].hide();
10318 buttons["no"].hide();
10319 dlg.footer.dom.style.display = 'none';
10322 dlg.footer.dom.style.display = '';
10323 for(var k in buttons){
10324 if(typeof buttons[k] != "function"){
10327 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10328 width += buttons[k].el.getWidth()+15;
10338 var handleEsc = function(d, k, e){
10339 if(opt && opt.closable !== false){
10349 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10350 * @return {Roo.BasicDialog} The BasicDialog element
10352 getDialog : function(){
10354 dlg = new Roo.BasicDialog("x-msg-box", {
10359 constraintoviewport:false,
10361 collapsible : false,
10364 width:400, height:100,
10365 buttonAlign:"center",
10366 closeClick : function(){
10367 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10368 handleButton("no");
10370 handleButton("cancel");
10374 dlg.on("hide", handleHide);
10376 dlg.addKeyListener(27, handleEsc);
10378 var bt = this.buttonText;
10379 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10380 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10381 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10382 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10383 bodyEl = dlg.body.createChild({
10385 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>'
10387 msgEl = bodyEl.dom.firstChild;
10388 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10389 textboxEl.enableDisplayMode();
10390 textboxEl.addKeyListener([10,13], function(){
10391 if(dlg.isVisible() && opt && opt.buttons){
10392 if(opt.buttons.ok){
10393 handleButton("ok");
10394 }else if(opt.buttons.yes){
10395 handleButton("yes");
10399 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10400 textareaEl.enableDisplayMode();
10401 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10402 progressEl.enableDisplayMode();
10403 var pf = progressEl.dom.firstChild;
10405 pp = Roo.get(pf.firstChild);
10406 pp.setHeight(pf.offsetHeight);
10414 * Updates the message box body text
10415 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10416 * the XHTML-compliant non-breaking space character '&#160;')
10417 * @return {Roo.MessageBox} This message box
10419 updateText : function(text){
10420 if(!dlg.isVisible() && !opt.width){
10421 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10423 msgEl.innerHTML = text || ' ';
10425 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10426 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10428 Math.min(opt.width || cw , this.maxWidth),
10429 Math.max(opt.minWidth || this.minWidth, bwidth)
10432 activeTextEl.setWidth(w);
10434 if(dlg.isVisible()){
10435 dlg.fixedcenter = false;
10437 // to big, make it scroll. = But as usual stupid IE does not support
10440 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10441 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10442 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10444 bodyEl.dom.style.height = '';
10445 bodyEl.dom.style.overflowY = '';
10448 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10450 bodyEl.dom.style.overflowX = '';
10453 dlg.setContentSize(w, bodyEl.getHeight());
10454 if(dlg.isVisible()){
10455 dlg.fixedcenter = true;
10461 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10462 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10463 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10464 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10465 * @return {Roo.MessageBox} This message box
10467 updateProgress : function(value, text){
10469 this.updateText(text);
10471 if (pp) { // weird bug on my firefox - for some reason this is not defined
10472 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10478 * Returns true if the message box is currently displayed
10479 * @return {Boolean} True if the message box is visible, else false
10481 isVisible : function(){
10482 return dlg && dlg.isVisible();
10486 * Hides the message box if it is displayed
10489 if(this.isVisible()){
10495 * Displays a new message box, or reinitializes an existing message box, based on the config options
10496 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10497 * The following config object properties are supported:
10499 Property Type Description
10500 ---------- --------------- ------------------------------------------------------------------------------------
10501 animEl String/Element An id or Element from which the message box should animate as it opens and
10502 closes (defaults to undefined)
10503 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10504 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10505 closable Boolean False to hide the top-right close button (defaults to true). Note that
10506 progress and wait dialogs will ignore this property and always hide the
10507 close button as they can only be closed programmatically.
10508 cls String A custom CSS class to apply to the message box element
10509 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10510 displayed (defaults to 75)
10511 fn Function A callback function to execute after closing the dialog. The arguments to the
10512 function will be btn (the name of the button that was clicked, if applicable,
10513 e.g. "ok"), and text (the value of the active text field, if applicable).
10514 Progress and wait dialogs will ignore this option since they do not respond to
10515 user actions and can only be closed programmatically, so any required function
10516 should be called by the same code after it closes the dialog.
10517 icon String A CSS class that provides a background image to be used as an icon for
10518 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10519 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10520 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10521 modal Boolean False to allow user interaction with the page while the message box is
10522 displayed (defaults to true)
10523 msg String A string that will replace the existing message box body text (defaults
10524 to the XHTML-compliant non-breaking space character ' ')
10525 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10526 progress Boolean True to display a progress bar (defaults to false)
10527 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10528 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10529 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10530 title String The title text
10531 value String The string value to set into the active textbox element if displayed
10532 wait Boolean True to display a progress bar (defaults to false)
10533 width Number The width of the dialog in pixels
10540 msg: 'Please enter your address:',
10542 buttons: Roo.MessageBox.OKCANCEL,
10545 animEl: 'addAddressBtn'
10548 * @param {Object} config Configuration options
10549 * @return {Roo.MessageBox} This message box
10551 show : function(options)
10554 // this causes nightmares if you show one dialog after another
10555 // especially on callbacks..
10557 if(this.isVisible()){
10560 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10561 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10562 Roo.log("New Dialog Message:" + options.msg )
10563 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10564 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10567 var d = this.getDialog();
10569 d.setTitle(opt.title || " ");
10570 d.close.setDisplayed(opt.closable !== false);
10571 activeTextEl = textboxEl;
10572 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10577 textareaEl.setHeight(typeof opt.multiline == "number" ?
10578 opt.multiline : this.defaultTextHeight);
10579 activeTextEl = textareaEl;
10588 progressEl.setDisplayed(opt.progress === true);
10589 this.updateProgress(0);
10590 activeTextEl.dom.value = opt.value || "";
10592 dlg.setDefaultButton(activeTextEl);
10594 var bs = opt.buttons;
10597 db = buttons["ok"];
10598 }else if(bs && bs.yes){
10599 db = buttons["yes"];
10601 dlg.setDefaultButton(db);
10603 bwidth = updateButtons(opt.buttons);
10604 this.updateText(opt.msg);
10606 d.el.addClass(opt.cls);
10608 d.proxyDrag = opt.proxyDrag === true;
10609 d.modal = opt.modal !== false;
10610 d.mask = opt.modal !== false ? mask : false;
10611 if(!d.isVisible()){
10612 // force it to the end of the z-index stack so it gets a cursor in FF
10613 document.body.appendChild(dlg.el.dom);
10614 d.animateTarget = null;
10615 d.show(options.animEl);
10621 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
10622 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10623 * and closing the message box when the process is complete.
10624 * @param {String} title The title bar text
10625 * @param {String} msg The message box body text
10626 * @return {Roo.MessageBox} This message box
10628 progress : function(title, msg){
10635 minWidth: this.minProgressWidth,
10642 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10643 * If a callback function is passed it will be called after the user clicks the button, and the
10644 * id of the button that was clicked will be passed as the only parameter to the callback
10645 * (could also be the top-right close button).
10646 * @param {String} title The title bar text
10647 * @param {String} msg The message box body text
10648 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10649 * @param {Object} scope (optional) The scope of the callback function
10650 * @return {Roo.MessageBox} This message box
10652 alert : function(title, msg, fn, scope){
10665 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
10666 * interaction while waiting for a long-running process to complete that does not have defined intervals.
10667 * You are responsible for closing the message box when the process is complete.
10668 * @param {String} msg The message box body text
10669 * @param {String} title (optional) The title bar text
10670 * @return {Roo.MessageBox} This message box
10672 wait : function(msg, title){
10683 waitTimer = Roo.TaskMgr.start({
10685 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10693 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10694 * If a callback function is passed it will be called after the user clicks either button, and the id of the
10695 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10696 * @param {String} title The title bar text
10697 * @param {String} msg The message box body text
10698 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10699 * @param {Object} scope (optional) The scope of the callback function
10700 * @return {Roo.MessageBox} This message box
10702 confirm : function(title, msg, fn, scope){
10706 buttons: this.YESNO,
10715 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10716 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
10717 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10718 * (could also be the top-right close button) and the text that was entered will be passed as the two
10719 * parameters to the callback.
10720 * @param {String} title The title bar text
10721 * @param {String} msg The message box body text
10722 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10723 * @param {Object} scope (optional) The scope of the callback function
10724 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10725 * property, or the height in pixels to create the textbox (defaults to false / single-line)
10726 * @return {Roo.MessageBox} This message box
10728 prompt : function(title, msg, fn, scope, multiline){
10732 buttons: this.OKCANCEL,
10737 multiline: multiline,
10744 * Button config that displays a single OK button
10749 * Button config that displays Yes and No buttons
10752 YESNO : {yes:true, no:true},
10754 * Button config that displays OK and Cancel buttons
10757 OKCANCEL : {ok:true, cancel:true},
10759 * Button config that displays Yes, No and Cancel buttons
10762 YESNOCANCEL : {yes:true, no:true, cancel:true},
10765 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10768 defaultTextHeight : 75,
10770 * The maximum width in pixels of the message box (defaults to 600)
10775 * The minimum width in pixels of the message box (defaults to 100)
10780 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
10781 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10784 minProgressWidth : 250,
10786 * An object containing the default button text strings that can be overriden for localized language support.
10787 * Supported properties are: ok, cancel, yes and no.
10788 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10801 * Shorthand for {@link Roo.MessageBox}
10803 Roo.Msg = Roo.MessageBox;/*
10805 * Ext JS Library 1.1.1
10806 * Copyright(c) 2006-2007, Ext JS, LLC.
10808 * Originally Released Under LGPL - original licence link has changed is not relivant.
10811 * <script type="text/javascript">
10814 * @class Roo.QuickTips
10815 * Provides attractive and customizable tooltips for any element.
10818 Roo.QuickTips = function(){
10819 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10820 var ce, bd, xy, dd;
10821 var visible = false, disabled = true, inited = false;
10822 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10824 var onOver = function(e){
10828 var t = e.getTarget();
10829 if(!t || t.nodeType !== 1 || t == document || t == document.body){
10832 if(ce && t == ce.el){
10833 clearTimeout(hideProc);
10836 if(t && tagEls[t.id]){
10837 tagEls[t.id].el = t;
10838 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10841 var ttp, et = Roo.fly(t);
10842 var ns = cfg.namespace;
10843 if(tm.interceptTitles && t.title){
10846 t.removeAttribute("title");
10847 e.preventDefault();
10849 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10852 showProc = show.defer(tm.showDelay, tm, [{
10854 text: ttp.replace(/\\n/g,'<br/>'),
10855 width: et.getAttributeNS(ns, cfg.width),
10856 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10857 title: et.getAttributeNS(ns, cfg.title),
10858 cls: et.getAttributeNS(ns, cfg.cls)
10863 var onOut = function(e){
10864 clearTimeout(showProc);
10865 var t = e.getTarget();
10866 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10867 hideProc = setTimeout(hide, tm.hideDelay);
10871 var onMove = function(e){
10877 if(tm.trackMouse && ce){
10882 var onDown = function(e){
10883 clearTimeout(showProc);
10884 clearTimeout(hideProc);
10886 if(tm.hideOnClick){
10889 tm.enable.defer(100, tm);
10894 var getPad = function(){
10895 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10898 var show = function(o){
10902 clearTimeout(dismissProc);
10904 if(removeCls){ // in case manually hidden
10905 el.removeClass(removeCls);
10909 el.addClass(ce.cls);
10910 removeCls = ce.cls;
10913 tipTitle.update(ce.title);
10916 tipTitle.update('');
10919 el.dom.style.width = tm.maxWidth+'px';
10920 //tipBody.dom.style.width = '';
10921 tipBodyText.update(o.text);
10922 var p = getPad(), w = ce.width;
10924 var td = tipBodyText.dom;
10925 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10926 if(aw > tm.maxWidth){
10928 }else if(aw < tm.minWidth){
10934 //tipBody.setWidth(w);
10935 el.setWidth(parseInt(w, 10) + p);
10936 if(ce.autoHide === false){
10937 close.setDisplayed(true);
10942 close.setDisplayed(false);
10948 el.avoidY = xy[1]-18;
10953 el.setStyle("visibility", "visible");
10954 el.fadeIn({callback: afterShow});
10960 var afterShow = function(){
10964 if(tm.autoDismiss && ce.autoHide !== false){
10965 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10970 var hide = function(noanim){
10971 clearTimeout(dismissProc);
10972 clearTimeout(hideProc);
10974 if(el.isVisible()){
10976 if(noanim !== true && tm.animate){
10977 el.fadeOut({callback: afterHide});
10984 var afterHide = function(){
10987 el.removeClass(removeCls);
10994 * @cfg {Number} minWidth
10995 * The minimum width of the quick tip (defaults to 40)
10999 * @cfg {Number} maxWidth
11000 * The maximum width of the quick tip (defaults to 300)
11004 * @cfg {Boolean} interceptTitles
11005 * True to automatically use the element's DOM title value if available (defaults to false)
11007 interceptTitles : false,
11009 * @cfg {Boolean} trackMouse
11010 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11012 trackMouse : false,
11014 * @cfg {Boolean} hideOnClick
11015 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11017 hideOnClick : true,
11019 * @cfg {Number} showDelay
11020 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11024 * @cfg {Number} hideDelay
11025 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11029 * @cfg {Boolean} autoHide
11030 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11031 * Used in conjunction with hideDelay.
11036 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11037 * (defaults to true). Used in conjunction with autoDismissDelay.
11039 autoDismiss : true,
11042 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11044 autoDismissDelay : 5000,
11046 * @cfg {Boolean} animate
11047 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11052 * @cfg {String} title
11053 * Title text to display (defaults to ''). This can be any valid HTML markup.
11057 * @cfg {String} text
11058 * Body text to display (defaults to ''). This can be any valid HTML markup.
11062 * @cfg {String} cls
11063 * A CSS class to apply to the base quick tip element (defaults to '').
11067 * @cfg {Number} width
11068 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11069 * minWidth or maxWidth.
11074 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11075 * or display QuickTips in a page.
11078 tm = Roo.QuickTips;
11079 cfg = tm.tagConfig;
11081 if(!Roo.isReady){ // allow calling of init() before onReady
11082 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11085 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11086 el.fxDefaults = {stopFx: true};
11087 // maximum custom styling
11088 //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>');
11089 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>');
11090 tipTitle = el.child('h3');
11091 tipTitle.enableDisplayMode("block");
11092 tipBody = el.child('div.x-tip-bd');
11093 tipBodyText = el.child('div.x-tip-bd-inner');
11094 //bdLeft = el.child('div.x-tip-bd-left');
11095 //bdRight = el.child('div.x-tip-bd-right');
11096 close = el.child('div.x-tip-close');
11097 close.enableDisplayMode("block");
11098 close.on("click", hide);
11099 var d = Roo.get(document);
11100 d.on("mousedown", onDown);
11101 d.on("mouseover", onOver);
11102 d.on("mouseout", onOut);
11103 d.on("mousemove", onMove);
11104 esc = d.addKeyListener(27, hide);
11107 dd = el.initDD("default", null, {
11108 onDrag : function(){
11112 dd.setHandleElId(tipTitle.id);
11121 * Configures a new quick tip instance and assigns it to a target element. The following config options
11124 Property Type Description
11125 ---------- --------------------- ------------------------------------------------------------------------
11126 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11128 * @param {Object} config The config object
11130 register : function(config){
11131 var cs = config instanceof Array ? config : arguments;
11132 for(var i = 0, len = cs.length; i < len; i++) {
11134 var target = c.target;
11136 if(target instanceof Array){
11137 for(var j = 0, jlen = target.length; j < jlen; j++){
11138 tagEls[target[j]] = c;
11141 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11148 * Removes this quick tip from its element and destroys it.
11149 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11151 unregister : function(el){
11152 delete tagEls[Roo.id(el)];
11156 * Enable this quick tip.
11158 enable : function(){
11159 if(inited && disabled){
11161 if(locks.length < 1){
11168 * Disable this quick tip.
11170 disable : function(){
11172 clearTimeout(showProc);
11173 clearTimeout(hideProc);
11174 clearTimeout(dismissProc);
11182 * Returns true if the quick tip is enabled, else false.
11184 isEnabled : function(){
11190 namespace : "roo", // was ext?? this may break..
11191 alt_namespace : "ext",
11192 attribute : "qtip",
11202 // backwards compat
11203 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11205 * Ext JS Library 1.1.1
11206 * Copyright(c) 2006-2007, Ext JS, LLC.
11208 * Originally Released Under LGPL - original licence link has changed is not relivant.
11211 * <script type="text/javascript">
11216 * @class Roo.tree.TreePanel
11217 * @extends Roo.data.Tree
11218 * @cfg {Roo.tree.TreeNode} root The root node
11219 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11220 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11221 * @cfg {Boolean} enableDD true to enable drag and drop
11222 * @cfg {Boolean} enableDrag true to enable just drag
11223 * @cfg {Boolean} enableDrop true to enable just drop
11224 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11225 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11226 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11227 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11228 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11229 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11230 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11231 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11232 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11233 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11234 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11235 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11236 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11237 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11238 * @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>
11239 * @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>
11242 * @param {String/HTMLElement/Element} el The container element
11243 * @param {Object} config
11245 Roo.tree.TreePanel = function(el, config){
11247 var loader = false;
11249 root = config.root;
11250 delete config.root;
11252 if (config.loader) {
11253 loader = config.loader;
11254 delete config.loader;
11257 Roo.apply(this, config);
11258 Roo.tree.TreePanel.superclass.constructor.call(this);
11259 this.el = Roo.get(el);
11260 this.el.addClass('x-tree');
11261 //console.log(root);
11263 this.setRootNode( Roo.factory(root, Roo.tree));
11266 this.loader = Roo.factory(loader, Roo.tree);
11269 * Read-only. The id of the container element becomes this TreePanel's id.
11271 this.id = this.el.id;
11274 * @event beforeload
11275 * Fires before a node is loaded, return false to cancel
11276 * @param {Node} node The node being loaded
11278 "beforeload" : true,
11281 * Fires when a node is loaded
11282 * @param {Node} node The node that was loaded
11286 * @event textchange
11287 * Fires when the text for a node is changed
11288 * @param {Node} node The node
11289 * @param {String} text The new text
11290 * @param {String} oldText The old text
11292 "textchange" : true,
11294 * @event beforeexpand
11295 * Fires before a node is expanded, return false to cancel.
11296 * @param {Node} node The node
11297 * @param {Boolean} deep
11298 * @param {Boolean} anim
11300 "beforeexpand" : true,
11302 * @event beforecollapse
11303 * Fires before a node is collapsed, return false to cancel.
11304 * @param {Node} node The node
11305 * @param {Boolean} deep
11306 * @param {Boolean} anim
11308 "beforecollapse" : true,
11311 * Fires when a node is expanded
11312 * @param {Node} node The node
11316 * @event disabledchange
11317 * Fires when the disabled status of a node changes
11318 * @param {Node} node The node
11319 * @param {Boolean} disabled
11321 "disabledchange" : true,
11324 * Fires when a node is collapsed
11325 * @param {Node} node The node
11329 * @event beforeclick
11330 * Fires before click processing on a node. Return false to cancel the default action.
11331 * @param {Node} node The node
11332 * @param {Roo.EventObject} e The event object
11334 "beforeclick":true,
11336 * @event checkchange
11337 * Fires when a node with a checkbox's checked property changes
11338 * @param {Node} this This node
11339 * @param {Boolean} checked
11341 "checkchange":true,
11344 * Fires when a node is clicked
11345 * @param {Node} node The node
11346 * @param {Roo.EventObject} e The event object
11351 * Fires when a node is double clicked
11352 * @param {Node} node The node
11353 * @param {Roo.EventObject} e The event object
11357 * @event contextmenu
11358 * Fires when a node is right clicked
11359 * @param {Node} node The node
11360 * @param {Roo.EventObject} e The event object
11362 "contextmenu":true,
11364 * @event beforechildrenrendered
11365 * Fires right before the child nodes for a node are rendered
11366 * @param {Node} node The node
11368 "beforechildrenrendered":true,
11371 * Fires when a node starts being dragged
11372 * @param {Roo.tree.TreePanel} this
11373 * @param {Roo.tree.TreeNode} node
11374 * @param {event} e The raw browser event
11376 "startdrag" : true,
11379 * Fires when a drag operation is complete
11380 * @param {Roo.tree.TreePanel} this
11381 * @param {Roo.tree.TreeNode} node
11382 * @param {event} e The raw browser event
11387 * Fires when a dragged node is dropped on a valid DD target
11388 * @param {Roo.tree.TreePanel} this
11389 * @param {Roo.tree.TreeNode} node
11390 * @param {DD} dd The dd it was dropped on
11391 * @param {event} e The raw browser event
11395 * @event beforenodedrop
11396 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11397 * passed to handlers has the following properties:<br />
11398 * <ul style="padding:5px;padding-left:16px;">
11399 * <li>tree - The TreePanel</li>
11400 * <li>target - The node being targeted for the drop</li>
11401 * <li>data - The drag data from the drag source</li>
11402 * <li>point - The point of the drop - append, above or below</li>
11403 * <li>source - The drag source</li>
11404 * <li>rawEvent - Raw mouse event</li>
11405 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11406 * to be inserted by setting them on this object.</li>
11407 * <li>cancel - Set this to true to cancel the drop.</li>
11409 * @param {Object} dropEvent
11411 "beforenodedrop" : true,
11414 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11415 * passed to handlers has the following properties:<br />
11416 * <ul style="padding:5px;padding-left:16px;">
11417 * <li>tree - The TreePanel</li>
11418 * <li>target - The node being targeted for the drop</li>
11419 * <li>data - The drag data from the drag source</li>
11420 * <li>point - The point of the drop - append, above or below</li>
11421 * <li>source - The drag source</li>
11422 * <li>rawEvent - Raw mouse event</li>
11423 * <li>dropNode - Dropped node(s).</li>
11425 * @param {Object} dropEvent
11429 * @event nodedragover
11430 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11431 * passed to handlers has the following properties:<br />
11432 * <ul style="padding:5px;padding-left:16px;">
11433 * <li>tree - The TreePanel</li>
11434 * <li>target - The node being targeted for the drop</li>
11435 * <li>data - The drag data from the drag source</li>
11436 * <li>point - The point of the drop - append, above or below</li>
11437 * <li>source - The drag source</li>
11438 * <li>rawEvent - Raw mouse event</li>
11439 * <li>dropNode - Drop node(s) provided by the source.</li>
11440 * <li>cancel - Set this to true to signal drop not allowed.</li>
11442 * @param {Object} dragOverEvent
11444 "nodedragover" : true,
11446 * @event appendnode
11447 * Fires when append node to the tree
11448 * @param {Roo.tree.TreePanel} this
11449 * @param {Roo.tree.TreeNode} node
11450 * @param {Number} index The index of the newly appended node
11452 "appendnode" : true
11455 if(this.singleExpand){
11456 this.on("beforeexpand", this.restrictExpand, this);
11459 this.editor.tree = this;
11460 this.editor = Roo.factory(this.editor, Roo.tree);
11463 if (this.selModel) {
11464 this.selModel = Roo.factory(this.selModel, Roo.tree);
11468 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11469 rootVisible : true,
11470 animate: Roo.enableFx,
11473 hlDrop : Roo.enableFx,
11477 rendererTip: false,
11479 restrictExpand : function(node){
11480 var p = node.parentNode;
11482 if(p.expandedChild && p.expandedChild.parentNode == p){
11483 p.expandedChild.collapse();
11485 p.expandedChild = node;
11489 // private override
11490 setRootNode : function(node){
11491 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11492 if(!this.rootVisible){
11493 node.ui = new Roo.tree.RootTreeNodeUI(node);
11499 * Returns the container element for this TreePanel
11501 getEl : function(){
11506 * Returns the default TreeLoader for this TreePanel
11508 getLoader : function(){
11509 return this.loader;
11515 expandAll : function(){
11516 this.root.expand(true);
11520 * Collapse all nodes
11522 collapseAll : function(){
11523 this.root.collapse(true);
11527 * Returns the selection model used by this TreePanel
11529 getSelectionModel : function(){
11530 if(!this.selModel){
11531 this.selModel = new Roo.tree.DefaultSelectionModel();
11533 return this.selModel;
11537 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11538 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11539 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11542 getChecked : function(a, startNode){
11543 startNode = startNode || this.root;
11545 var f = function(){
11546 if(this.attributes.checked){
11547 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11550 startNode.cascade(f);
11555 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11556 * @param {String} path
11557 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11558 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11559 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11561 expandPath : function(path, attr, callback){
11562 attr = attr || "id";
11563 var keys = path.split(this.pathSeparator);
11564 var curNode = this.root;
11565 if(curNode.attributes[attr] != keys[1]){ // invalid root
11567 callback(false, null);
11572 var f = function(){
11573 if(++index == keys.length){
11575 callback(true, curNode);
11579 var c = curNode.findChild(attr, keys[index]);
11582 callback(false, curNode);
11587 c.expand(false, false, f);
11589 curNode.expand(false, false, f);
11593 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11594 * @param {String} path
11595 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11596 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11597 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11599 selectPath : function(path, attr, callback){
11600 attr = attr || "id";
11601 var keys = path.split(this.pathSeparator);
11602 var v = keys.pop();
11603 if(keys.length > 0){
11604 var f = function(success, node){
11605 if(success && node){
11606 var n = node.findChild(attr, v);
11612 }else if(callback){
11613 callback(false, n);
11617 callback(false, n);
11621 this.expandPath(keys.join(this.pathSeparator), attr, f);
11623 this.root.select();
11625 callback(true, this.root);
11630 getTreeEl : function(){
11635 * Trigger rendering of this TreePanel
11637 render : function(){
11638 if (this.innerCt) {
11639 return this; // stop it rendering more than once!!
11642 this.innerCt = this.el.createChild({tag:"ul",
11643 cls:"x-tree-root-ct " +
11644 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11646 if(this.containerScroll){
11647 Roo.dd.ScrollManager.register(this.el);
11649 if((this.enableDD || this.enableDrop) && !this.dropZone){
11651 * The dropZone used by this tree if drop is enabled
11652 * @type Roo.tree.TreeDropZone
11654 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11655 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11658 if((this.enableDD || this.enableDrag) && !this.dragZone){
11660 * The dragZone used by this tree if drag is enabled
11661 * @type Roo.tree.TreeDragZone
11663 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11664 ddGroup: this.ddGroup || "TreeDD",
11665 scroll: this.ddScroll
11668 this.getSelectionModel().init(this);
11670 Roo.log("ROOT not set in tree");
11673 this.root.render();
11674 if(!this.rootVisible){
11675 this.root.renderChildren();
11681 * Ext JS Library 1.1.1
11682 * Copyright(c) 2006-2007, Ext JS, LLC.
11684 * Originally Released Under LGPL - original licence link has changed is not relivant.
11687 * <script type="text/javascript">
11692 * @class Roo.tree.DefaultSelectionModel
11693 * @extends Roo.util.Observable
11694 * The default single selection for a TreePanel.
11695 * @param {Object} cfg Configuration
11697 Roo.tree.DefaultSelectionModel = function(cfg){
11698 this.selNode = null;
11704 * @event selectionchange
11705 * Fires when the selected node changes
11706 * @param {DefaultSelectionModel} this
11707 * @param {TreeNode} node the new selection
11709 "selectionchange" : true,
11712 * @event beforeselect
11713 * Fires before the selected node changes, return false to cancel the change
11714 * @param {DefaultSelectionModel} this
11715 * @param {TreeNode} node the new selection
11716 * @param {TreeNode} node the old selection
11718 "beforeselect" : true
11721 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11724 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11725 init : function(tree){
11727 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11728 tree.on("click", this.onNodeClick, this);
11731 onNodeClick : function(node, e){
11732 if (e.ctrlKey && this.selNode == node) {
11733 this.unselect(node);
11741 * @param {TreeNode} node The node to select
11742 * @return {TreeNode} The selected node
11744 select : function(node){
11745 var last = this.selNode;
11746 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11748 last.ui.onSelectedChange(false);
11750 this.selNode = node;
11751 node.ui.onSelectedChange(true);
11752 this.fireEvent("selectionchange", this, node, last);
11759 * @param {TreeNode} node The node to unselect
11761 unselect : function(node){
11762 if(this.selNode == node){
11763 this.clearSelections();
11768 * Clear all selections
11770 clearSelections : function(){
11771 var n = this.selNode;
11773 n.ui.onSelectedChange(false);
11774 this.selNode = null;
11775 this.fireEvent("selectionchange", this, null);
11781 * Get the selected node
11782 * @return {TreeNode} The selected node
11784 getSelectedNode : function(){
11785 return this.selNode;
11789 * Returns true if the node is selected
11790 * @param {TreeNode} node The node to check
11791 * @return {Boolean}
11793 isSelected : function(node){
11794 return this.selNode == node;
11798 * Selects the node above the selected node in the tree, intelligently walking the nodes
11799 * @return TreeNode The new selection
11801 selectPrevious : function(){
11802 var s = this.selNode || this.lastSelNode;
11806 var ps = s.previousSibling;
11808 if(!ps.isExpanded() || ps.childNodes.length < 1){
11809 return this.select(ps);
11811 var lc = ps.lastChild;
11812 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11815 return this.select(lc);
11817 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11818 return this.select(s.parentNode);
11824 * Selects the node above the selected node in the tree, intelligently walking the nodes
11825 * @return TreeNode The new selection
11827 selectNext : function(){
11828 var s = this.selNode || this.lastSelNode;
11832 if(s.firstChild && s.isExpanded()){
11833 return this.select(s.firstChild);
11834 }else if(s.nextSibling){
11835 return this.select(s.nextSibling);
11836 }else if(s.parentNode){
11838 s.parentNode.bubble(function(){
11839 if(this.nextSibling){
11840 newS = this.getOwnerTree().selModel.select(this.nextSibling);
11849 onKeyDown : function(e){
11850 var s = this.selNode || this.lastSelNode;
11851 // undesirable, but required
11856 var k = e.getKey();
11864 this.selectPrevious();
11867 e.preventDefault();
11868 if(s.hasChildNodes()){
11869 if(!s.isExpanded()){
11871 }else if(s.firstChild){
11872 this.select(s.firstChild, e);
11877 e.preventDefault();
11878 if(s.hasChildNodes() && s.isExpanded()){
11880 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11881 this.select(s.parentNode, e);
11889 * @class Roo.tree.MultiSelectionModel
11890 * @extends Roo.util.Observable
11891 * Multi selection for a TreePanel.
11892 * @param {Object} cfg Configuration
11894 Roo.tree.MultiSelectionModel = function(){
11895 this.selNodes = [];
11899 * @event selectionchange
11900 * Fires when the selected nodes change
11901 * @param {MultiSelectionModel} this
11902 * @param {Array} nodes Array of the selected nodes
11904 "selectionchange" : true
11906 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11910 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11911 init : function(tree){
11913 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11914 tree.on("click", this.onNodeClick, this);
11917 onNodeClick : function(node, e){
11918 this.select(node, e, e.ctrlKey);
11923 * @param {TreeNode} node The node to select
11924 * @param {EventObject} e (optional) An event associated with the selection
11925 * @param {Boolean} keepExisting True to retain existing selections
11926 * @return {TreeNode} The selected node
11928 select : function(node, e, keepExisting){
11929 if(keepExisting !== true){
11930 this.clearSelections(true);
11932 if(this.isSelected(node)){
11933 this.lastSelNode = node;
11936 this.selNodes.push(node);
11937 this.selMap[node.id] = node;
11938 this.lastSelNode = node;
11939 node.ui.onSelectedChange(true);
11940 this.fireEvent("selectionchange", this, this.selNodes);
11946 * @param {TreeNode} node The node to unselect
11948 unselect : function(node){
11949 if(this.selMap[node.id]){
11950 node.ui.onSelectedChange(false);
11951 var sn = this.selNodes;
11954 index = sn.indexOf(node);
11956 for(var i = 0, len = sn.length; i < len; i++){
11964 this.selNodes.splice(index, 1);
11966 delete this.selMap[node.id];
11967 this.fireEvent("selectionchange", this, this.selNodes);
11972 * Clear all selections
11974 clearSelections : function(suppressEvent){
11975 var sn = this.selNodes;
11977 for(var i = 0, len = sn.length; i < len; i++){
11978 sn[i].ui.onSelectedChange(false);
11980 this.selNodes = [];
11982 if(suppressEvent !== true){
11983 this.fireEvent("selectionchange", this, this.selNodes);
11989 * Returns true if the node is selected
11990 * @param {TreeNode} node The node to check
11991 * @return {Boolean}
11993 isSelected : function(node){
11994 return this.selMap[node.id] ? true : false;
11998 * Returns an array of the selected nodes
12001 getSelectedNodes : function(){
12002 return this.selNodes;
12005 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12007 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12009 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12012 * Ext JS Library 1.1.1
12013 * Copyright(c) 2006-2007, Ext JS, LLC.
12015 * Originally Released Under LGPL - original licence link has changed is not relivant.
12018 * <script type="text/javascript">
12022 * @class Roo.tree.TreeNode
12023 * @extends Roo.data.Node
12024 * @cfg {String} text The text for this node
12025 * @cfg {Boolean} expanded true to start the node expanded
12026 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12027 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12028 * @cfg {Boolean} disabled true to start the node disabled
12029 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12030 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12031 * @cfg {String} cls A css class to be added to the node
12032 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12033 * @cfg {String} href URL of the link used for the node (defaults to #)
12034 * @cfg {String} hrefTarget target frame for the link
12035 * @cfg {String} qtip An Ext QuickTip for the node
12036 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12037 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12038 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12039 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12040 * (defaults to undefined with no checkbox rendered)
12042 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12044 Roo.tree.TreeNode = function(attributes){
12045 attributes = attributes || {};
12046 if(typeof attributes == "string"){
12047 attributes = {text: attributes};
12049 this.childrenRendered = false;
12050 this.rendered = false;
12051 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12052 this.expanded = attributes.expanded === true;
12053 this.isTarget = attributes.isTarget !== false;
12054 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12055 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12058 * Read-only. The text for this node. To change it use setText().
12061 this.text = attributes.text;
12063 * True if this node is disabled.
12066 this.disabled = attributes.disabled === true;
12070 * @event textchange
12071 * Fires when the text for this node is changed
12072 * @param {Node} this This node
12073 * @param {String} text The new text
12074 * @param {String} oldText The old text
12076 "textchange" : true,
12078 * @event beforeexpand
12079 * Fires before this node is expanded, return false to cancel.
12080 * @param {Node} this This node
12081 * @param {Boolean} deep
12082 * @param {Boolean} anim
12084 "beforeexpand" : true,
12086 * @event beforecollapse
12087 * Fires before this node is collapsed, return false to cancel.
12088 * @param {Node} this This node
12089 * @param {Boolean} deep
12090 * @param {Boolean} anim
12092 "beforecollapse" : true,
12095 * Fires when this node is expanded
12096 * @param {Node} this This node
12100 * @event disabledchange
12101 * Fires when the disabled status of this node changes
12102 * @param {Node} this This node
12103 * @param {Boolean} disabled
12105 "disabledchange" : true,
12108 * Fires when this node is collapsed
12109 * @param {Node} this This node
12113 * @event beforeclick
12114 * Fires before click processing. Return false to cancel the default action.
12115 * @param {Node} this This node
12116 * @param {Roo.EventObject} e The event object
12118 "beforeclick":true,
12120 * @event checkchange
12121 * Fires when a node with a checkbox's checked property changes
12122 * @param {Node} this This node
12123 * @param {Boolean} checked
12125 "checkchange":true,
12128 * Fires when this node is clicked
12129 * @param {Node} this This node
12130 * @param {Roo.EventObject} e The event object
12135 * Fires when this node is double clicked
12136 * @param {Node} this This node
12137 * @param {Roo.EventObject} e The event object
12141 * @event contextmenu
12142 * Fires when this node is right clicked
12143 * @param {Node} this This node
12144 * @param {Roo.EventObject} e The event object
12146 "contextmenu":true,
12148 * @event beforechildrenrendered
12149 * Fires right before the child nodes for this node are rendered
12150 * @param {Node} this This node
12152 "beforechildrenrendered":true
12155 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12158 * Read-only. The UI for this node
12161 this.ui = new uiClass(this);
12163 // finally support items[]
12164 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12169 Roo.each(this.attributes.items, function(c) {
12170 this.appendChild(Roo.factory(c,Roo.Tree));
12172 delete this.attributes.items;
12177 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12178 preventHScroll: true,
12180 * Returns true if this node is expanded
12181 * @return {Boolean}
12183 isExpanded : function(){
12184 return this.expanded;
12188 * Returns the UI object for this node
12189 * @return {TreeNodeUI}
12191 getUI : function(){
12195 // private override
12196 setFirstChild : function(node){
12197 var of = this.firstChild;
12198 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12199 if(this.childrenRendered && of && node != of){
12200 of.renderIndent(true, true);
12203 this.renderIndent(true, true);
12207 // private override
12208 setLastChild : function(node){
12209 var ol = this.lastChild;
12210 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12211 if(this.childrenRendered && ol && node != ol){
12212 ol.renderIndent(true, true);
12215 this.renderIndent(true, true);
12219 // these methods are overridden to provide lazy rendering support
12220 // private override
12221 appendChild : function()
12223 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12224 if(node && this.childrenRendered){
12227 this.ui.updateExpandIcon();
12231 // private override
12232 removeChild : function(node){
12233 this.ownerTree.getSelectionModel().unselect(node);
12234 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12235 // if it's been rendered remove dom node
12236 if(this.childrenRendered){
12239 if(this.childNodes.length < 1){
12240 this.collapse(false, false);
12242 this.ui.updateExpandIcon();
12244 if(!this.firstChild) {
12245 this.childrenRendered = false;
12250 // private override
12251 insertBefore : function(node, refNode){
12252 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12253 if(newNode && refNode && this.childrenRendered){
12256 this.ui.updateExpandIcon();
12261 * Sets the text for this node
12262 * @param {String} text
12264 setText : function(text){
12265 var oldText = this.text;
12267 this.attributes.text = text;
12268 if(this.rendered){ // event without subscribing
12269 this.ui.onTextChange(this, text, oldText);
12271 this.fireEvent("textchange", this, text, oldText);
12275 * Triggers selection of this node
12277 select : function(){
12278 this.getOwnerTree().getSelectionModel().select(this);
12282 * Triggers deselection of this node
12284 unselect : function(){
12285 this.getOwnerTree().getSelectionModel().unselect(this);
12289 * Returns true if this node is selected
12290 * @return {Boolean}
12292 isSelected : function(){
12293 return this.getOwnerTree().getSelectionModel().isSelected(this);
12297 * Expand this node.
12298 * @param {Boolean} deep (optional) True to expand all children as well
12299 * @param {Boolean} anim (optional) false to cancel the default animation
12300 * @param {Function} callback (optional) A callback to be called when
12301 * expanding this node completes (does not wait for deep expand to complete).
12302 * Called with 1 parameter, this node.
12304 expand : function(deep, anim, callback){
12305 if(!this.expanded){
12306 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12309 if(!this.childrenRendered){
12310 this.renderChildren();
12312 this.expanded = true;
12314 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12315 this.ui.animExpand(function(){
12316 this.fireEvent("expand", this);
12317 if(typeof callback == "function"){
12321 this.expandChildNodes(true);
12323 }.createDelegate(this));
12327 this.fireEvent("expand", this);
12328 if(typeof callback == "function"){
12333 if(typeof callback == "function"){
12338 this.expandChildNodes(true);
12342 isHiddenRoot : function(){
12343 return this.isRoot && !this.getOwnerTree().rootVisible;
12347 * Collapse this node.
12348 * @param {Boolean} deep (optional) True to collapse all children as well
12349 * @param {Boolean} anim (optional) false to cancel the default animation
12351 collapse : function(deep, anim){
12352 if(this.expanded && !this.isHiddenRoot()){
12353 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12356 this.expanded = false;
12357 if((this.getOwnerTree().animate && anim !== false) || anim){
12358 this.ui.animCollapse(function(){
12359 this.fireEvent("collapse", this);
12361 this.collapseChildNodes(true);
12363 }.createDelegate(this));
12366 this.ui.collapse();
12367 this.fireEvent("collapse", this);
12371 var cs = this.childNodes;
12372 for(var i = 0, len = cs.length; i < len; i++) {
12373 cs[i].collapse(true, false);
12379 delayedExpand : function(delay){
12380 if(!this.expandProcId){
12381 this.expandProcId = this.expand.defer(delay, this);
12386 cancelExpand : function(){
12387 if(this.expandProcId){
12388 clearTimeout(this.expandProcId);
12390 this.expandProcId = false;
12394 * Toggles expanded/collapsed state of the node
12396 toggle : function(){
12405 * Ensures all parent nodes are expanded
12407 ensureVisible : function(callback){
12408 var tree = this.getOwnerTree();
12409 tree.expandPath(this.parentNode.getPath(), false, function(){
12410 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12411 Roo.callback(callback);
12412 }.createDelegate(this));
12416 * Expand all child nodes
12417 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12419 expandChildNodes : function(deep){
12420 var cs = this.childNodes;
12421 for(var i = 0, len = cs.length; i < len; i++) {
12422 cs[i].expand(deep);
12427 * Collapse all child nodes
12428 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12430 collapseChildNodes : function(deep){
12431 var cs = this.childNodes;
12432 for(var i = 0, len = cs.length; i < len; i++) {
12433 cs[i].collapse(deep);
12438 * Disables this node
12440 disable : function(){
12441 this.disabled = true;
12443 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12444 this.ui.onDisableChange(this, true);
12446 this.fireEvent("disabledchange", this, true);
12450 * Enables this node
12452 enable : function(){
12453 this.disabled = false;
12454 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12455 this.ui.onDisableChange(this, false);
12457 this.fireEvent("disabledchange", this, false);
12461 renderChildren : function(suppressEvent){
12462 if(suppressEvent !== false){
12463 this.fireEvent("beforechildrenrendered", this);
12465 var cs = this.childNodes;
12466 for(var i = 0, len = cs.length; i < len; i++){
12467 cs[i].render(true);
12469 this.childrenRendered = true;
12473 sort : function(fn, scope){
12474 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12475 if(this.childrenRendered){
12476 var cs = this.childNodes;
12477 for(var i = 0, len = cs.length; i < len; i++){
12478 cs[i].render(true);
12484 render : function(bulkRender){
12485 this.ui.render(bulkRender);
12486 if(!this.rendered){
12487 this.rendered = true;
12489 this.expanded = false;
12490 this.expand(false, false);
12496 renderIndent : function(deep, refresh){
12498 this.ui.childIndent = null;
12500 this.ui.renderIndent();
12501 if(deep === true && this.childrenRendered){
12502 var cs = this.childNodes;
12503 for(var i = 0, len = cs.length; i < len; i++){
12504 cs[i].renderIndent(true, refresh);
12510 * Ext JS Library 1.1.1
12511 * Copyright(c) 2006-2007, Ext JS, LLC.
12513 * Originally Released Under LGPL - original licence link has changed is not relivant.
12516 * <script type="text/javascript">
12520 * @class Roo.tree.AsyncTreeNode
12521 * @extends Roo.tree.TreeNode
12522 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12524 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12526 Roo.tree.AsyncTreeNode = function(config){
12527 this.loaded = false;
12528 this.loading = false;
12529 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12531 * @event beforeload
12532 * Fires before this node is loaded, return false to cancel
12533 * @param {Node} this This node
12535 this.addEvents({'beforeload':true, 'load': true});
12538 * Fires when this node is loaded
12539 * @param {Node} this This node
12542 * The loader used by this node (defaults to using the tree's defined loader)
12547 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12548 expand : function(deep, anim, callback){
12549 if(this.loading){ // if an async load is already running, waiting til it's done
12551 var f = function(){
12552 if(!this.loading){ // done loading
12553 clearInterval(timer);
12554 this.expand(deep, anim, callback);
12556 }.createDelegate(this);
12557 timer = setInterval(f, 200);
12561 if(this.fireEvent("beforeload", this) === false){
12564 this.loading = true;
12565 this.ui.beforeLoad(this);
12566 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12568 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12572 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12576 * Returns true if this node is currently loading
12577 * @return {Boolean}
12579 isLoading : function(){
12580 return this.loading;
12583 loadComplete : function(deep, anim, callback){
12584 this.loading = false;
12585 this.loaded = true;
12586 this.ui.afterLoad(this);
12587 this.fireEvent("load", this);
12588 this.expand(deep, anim, callback);
12592 * Returns true if this node has been loaded
12593 * @return {Boolean}
12595 isLoaded : function(){
12596 return this.loaded;
12599 hasChildNodes : function(){
12600 if(!this.isLeaf() && !this.loaded){
12603 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12608 * Trigger a reload for this node
12609 * @param {Function} callback
12611 reload : function(callback){
12612 this.collapse(false, false);
12613 while(this.firstChild){
12614 this.removeChild(this.firstChild);
12616 this.childrenRendered = false;
12617 this.loaded = false;
12618 if(this.isHiddenRoot()){
12619 this.expanded = false;
12621 this.expand(false, false, callback);
12625 * Ext JS Library 1.1.1
12626 * Copyright(c) 2006-2007, Ext JS, LLC.
12628 * Originally Released Under LGPL - original licence link has changed is not relivant.
12631 * <script type="text/javascript">
12635 * @class Roo.tree.TreeNodeUI
12637 * @param {Object} node The node to render
12638 * The TreeNode UI implementation is separate from the
12639 * tree implementation. Unless you are customizing the tree UI,
12640 * you should never have to use this directly.
12642 Roo.tree.TreeNodeUI = function(node){
12644 this.rendered = false;
12645 this.animating = false;
12646 this.emptyIcon = Roo.BLANK_IMAGE_URL;
12649 Roo.tree.TreeNodeUI.prototype = {
12650 removeChild : function(node){
12652 this.ctNode.removeChild(node.ui.getEl());
12656 beforeLoad : function(){
12657 this.addClass("x-tree-node-loading");
12660 afterLoad : function(){
12661 this.removeClass("x-tree-node-loading");
12664 onTextChange : function(node, text, oldText){
12666 this.textNode.innerHTML = text;
12670 onDisableChange : function(node, state){
12671 this.disabled = state;
12673 this.addClass("x-tree-node-disabled");
12675 this.removeClass("x-tree-node-disabled");
12679 onSelectedChange : function(state){
12682 this.addClass("x-tree-selected");
12685 this.removeClass("x-tree-selected");
12689 onMove : function(tree, node, oldParent, newParent, index, refNode){
12690 this.childIndent = null;
12692 var targetNode = newParent.ui.getContainer();
12693 if(!targetNode){//target not rendered
12694 this.holder = document.createElement("div");
12695 this.holder.appendChild(this.wrap);
12698 var insertBefore = refNode ? refNode.ui.getEl() : null;
12700 targetNode.insertBefore(this.wrap, insertBefore);
12702 targetNode.appendChild(this.wrap);
12704 this.node.renderIndent(true);
12708 addClass : function(cls){
12710 Roo.fly(this.elNode).addClass(cls);
12714 removeClass : function(cls){
12716 Roo.fly(this.elNode).removeClass(cls);
12720 remove : function(){
12722 this.holder = document.createElement("div");
12723 this.holder.appendChild(this.wrap);
12727 fireEvent : function(){
12728 return this.node.fireEvent.apply(this.node, arguments);
12731 initEvents : function(){
12732 this.node.on("move", this.onMove, this);
12733 var E = Roo.EventManager;
12734 var a = this.anchor;
12736 var el = Roo.fly(a, '_treeui');
12738 if(Roo.isOpera){ // opera render bug ignores the CSS
12739 el.setStyle("text-decoration", "none");
12742 el.on("click", this.onClick, this);
12743 el.on("dblclick", this.onDblClick, this);
12746 Roo.EventManager.on(this.checkbox,
12747 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12750 el.on("contextmenu", this.onContextMenu, this);
12752 var icon = Roo.fly(this.iconNode);
12753 icon.on("click", this.onClick, this);
12754 icon.on("dblclick", this.onDblClick, this);
12755 icon.on("contextmenu", this.onContextMenu, this);
12756 E.on(this.ecNode, "click", this.ecClick, this, true);
12758 if(this.node.disabled){
12759 this.addClass("x-tree-node-disabled");
12761 if(this.node.hidden){
12762 this.addClass("x-tree-node-disabled");
12764 var ot = this.node.getOwnerTree();
12765 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12766 if(dd && (!this.node.isRoot || ot.rootVisible)){
12767 Roo.dd.Registry.register(this.elNode, {
12769 handles: this.getDDHandles(),
12775 getDDHandles : function(){
12776 return [this.iconNode, this.textNode];
12781 this.wrap.style.display = "none";
12787 this.wrap.style.display = "";
12791 onContextMenu : function(e){
12792 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12793 e.preventDefault();
12795 this.fireEvent("contextmenu", this.node, e);
12799 onClick : function(e){
12804 if(this.fireEvent("beforeclick", this.node, e) !== false){
12805 if(!this.disabled && this.node.attributes.href){
12806 this.fireEvent("click", this.node, e);
12809 e.preventDefault();
12814 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12815 this.node.toggle();
12818 this.fireEvent("click", this.node, e);
12824 onDblClick : function(e){
12825 e.preventDefault();
12830 this.toggleCheck();
12832 if(!this.animating && this.node.hasChildNodes()){
12833 this.node.toggle();
12835 this.fireEvent("dblclick", this.node, e);
12838 onCheckChange : function(){
12839 var checked = this.checkbox.checked;
12840 this.node.attributes.checked = checked;
12841 this.fireEvent('checkchange', this.node, checked);
12844 ecClick : function(e){
12845 if(!this.animating && this.node.hasChildNodes()){
12846 this.node.toggle();
12850 startDrop : function(){
12851 this.dropping = true;
12854 // delayed drop so the click event doesn't get fired on a drop
12855 endDrop : function(){
12856 setTimeout(function(){
12857 this.dropping = false;
12858 }.createDelegate(this), 50);
12861 expand : function(){
12862 this.updateExpandIcon();
12863 this.ctNode.style.display = "";
12866 focus : function(){
12867 if(!this.node.preventHScroll){
12868 try{this.anchor.focus();
12870 }else if(!Roo.isIE){
12872 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12873 var l = noscroll.scrollLeft;
12874 this.anchor.focus();
12875 noscroll.scrollLeft = l;
12880 toggleCheck : function(value){
12881 var cb = this.checkbox;
12883 cb.checked = (value === undefined ? !cb.checked : value);
12889 this.anchor.blur();
12893 animExpand : function(callback){
12894 var ct = Roo.get(this.ctNode);
12896 if(!this.node.hasChildNodes()){
12897 this.updateExpandIcon();
12898 this.ctNode.style.display = "";
12899 Roo.callback(callback);
12902 this.animating = true;
12903 this.updateExpandIcon();
12906 callback : function(){
12907 this.animating = false;
12908 Roo.callback(callback);
12911 duration: this.node.ownerTree.duration || .25
12915 highlight : function(){
12916 var tree = this.node.getOwnerTree();
12917 Roo.fly(this.wrap).highlight(
12918 tree.hlColor || "C3DAF9",
12919 {endColor: tree.hlBaseColor}
12923 collapse : function(){
12924 this.updateExpandIcon();
12925 this.ctNode.style.display = "none";
12928 animCollapse : function(callback){
12929 var ct = Roo.get(this.ctNode);
12930 ct.enableDisplayMode('block');
12933 this.animating = true;
12934 this.updateExpandIcon();
12937 callback : function(){
12938 this.animating = false;
12939 Roo.callback(callback);
12942 duration: this.node.ownerTree.duration || .25
12946 getContainer : function(){
12947 return this.ctNode;
12950 getEl : function(){
12954 appendDDGhost : function(ghostNode){
12955 ghostNode.appendChild(this.elNode.cloneNode(true));
12958 getDDRepairXY : function(){
12959 return Roo.lib.Dom.getXY(this.iconNode);
12962 onRender : function(){
12966 render : function(bulkRender){
12967 var n = this.node, a = n.attributes;
12968 var targetNode = n.parentNode ?
12969 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12971 if(!this.rendered){
12972 this.rendered = true;
12974 this.renderElements(n, a, targetNode, bulkRender);
12977 if(this.textNode.setAttributeNS){
12978 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12980 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12983 this.textNode.setAttribute("ext:qtip", a.qtip);
12985 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12988 }else if(a.qtipCfg){
12989 a.qtipCfg.target = Roo.id(this.textNode);
12990 Roo.QuickTips.register(a.qtipCfg);
12993 if(!this.node.expanded){
12994 this.updateExpandIcon();
12997 if(bulkRender === true) {
12998 targetNode.appendChild(this.wrap);
13003 renderElements : function(n, a, targetNode, bulkRender)
13005 // add some indent caching, this helps performance when rendering a large tree
13006 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13007 var t = n.getOwnerTree();
13008 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13009 if (typeof(n.attributes.html) != 'undefined') {
13010 txt = n.attributes.html;
13012 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13013 var cb = typeof a.checked == 'boolean';
13014 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13015 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13016 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13017 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13018 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13019 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13020 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13021 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13022 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13023 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13026 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13027 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13028 n.nextSibling.ui.getEl(), buf.join(""));
13030 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13033 this.elNode = this.wrap.childNodes[0];
13034 this.ctNode = this.wrap.childNodes[1];
13035 var cs = this.elNode.childNodes;
13036 this.indentNode = cs[0];
13037 this.ecNode = cs[1];
13038 this.iconNode = cs[2];
13041 this.checkbox = cs[3];
13044 this.anchor = cs[index];
13045 this.textNode = cs[index].firstChild;
13048 getAnchor : function(){
13049 return this.anchor;
13052 getTextEl : function(){
13053 return this.textNode;
13056 getIconEl : function(){
13057 return this.iconNode;
13060 isChecked : function(){
13061 return this.checkbox ? this.checkbox.checked : false;
13064 updateExpandIcon : function(){
13066 var n = this.node, c1, c2;
13067 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13068 var hasChild = n.hasChildNodes();
13072 c1 = "x-tree-node-collapsed";
13073 c2 = "x-tree-node-expanded";
13076 c1 = "x-tree-node-expanded";
13077 c2 = "x-tree-node-collapsed";
13080 this.removeClass("x-tree-node-leaf");
13081 this.wasLeaf = false;
13083 if(this.c1 != c1 || this.c2 != c2){
13084 Roo.fly(this.elNode).replaceClass(c1, c2);
13085 this.c1 = c1; this.c2 = c2;
13088 // this changes non-leafs into leafs if they have no children.
13089 // it's not very rational behaviour..
13091 if(!this.wasLeaf && this.node.leaf){
13092 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13095 this.wasLeaf = true;
13098 var ecc = "x-tree-ec-icon "+cls;
13099 if(this.ecc != ecc){
13100 this.ecNode.className = ecc;
13106 getChildIndent : function(){
13107 if(!this.childIndent){
13111 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13113 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13115 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13120 this.childIndent = buf.join("");
13122 return this.childIndent;
13125 renderIndent : function(){
13128 var p = this.node.parentNode;
13130 indent = p.ui.getChildIndent();
13132 if(this.indentMarkup != indent){ // don't rerender if not required
13133 this.indentNode.innerHTML = indent;
13134 this.indentMarkup = indent;
13136 this.updateExpandIcon();
13141 Roo.tree.RootTreeNodeUI = function(){
13142 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13144 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13145 render : function(){
13146 if(!this.rendered){
13147 var targetNode = this.node.ownerTree.innerCt.dom;
13148 this.node.expanded = true;
13149 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13150 this.wrap = this.ctNode = targetNode.firstChild;
13153 collapse : function(){
13155 expand : function(){
13159 * Ext JS Library 1.1.1
13160 * Copyright(c) 2006-2007, Ext JS, LLC.
13162 * Originally Released Under LGPL - original licence link has changed is not relivant.
13165 * <script type="text/javascript">
13168 * @class Roo.tree.TreeLoader
13169 * @extends Roo.util.Observable
13170 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13171 * nodes from a specified URL. The response must be a javascript Array definition
13172 * who's elements are node definition objects. eg:
13177 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13178 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13185 * The old style respose with just an array is still supported, but not recommended.
13188 * A server request is sent, and child nodes are loaded only when a node is expanded.
13189 * The loading node's id is passed to the server under the parameter name "node" to
13190 * enable the server to produce the correct child nodes.
13192 * To pass extra parameters, an event handler may be attached to the "beforeload"
13193 * event, and the parameters specified in the TreeLoader's baseParams property:
13195 myTreeLoader.on("beforeload", function(treeLoader, node) {
13196 this.baseParams.category = node.attributes.category;
13201 * This would pass an HTTP parameter called "category" to the server containing
13202 * the value of the Node's "category" attribute.
13204 * Creates a new Treeloader.
13205 * @param {Object} config A config object containing config properties.
13207 Roo.tree.TreeLoader = function(config){
13208 this.baseParams = {};
13209 this.requestMethod = "POST";
13210 Roo.apply(this, config);
13215 * @event beforeload
13216 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13217 * @param {Object} This TreeLoader object.
13218 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13219 * @param {Object} callback The callback function specified in the {@link #load} call.
13224 * Fires when the node has been successfuly loaded.
13225 * @param {Object} This TreeLoader object.
13226 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13227 * @param {Object} response The response object containing the data from the server.
13231 * @event loadexception
13232 * Fires if the network request failed.
13233 * @param {Object} This TreeLoader object.
13234 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13235 * @param {Object} response The response object containing the data from the server.
13237 loadexception : true,
13240 * Fires before a node is created, enabling you to return custom Node types
13241 * @param {Object} This TreeLoader object.
13242 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13247 Roo.tree.TreeLoader.superclass.constructor.call(this);
13250 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13252 * @cfg {String} dataUrl The URL from which to request a Json string which
13253 * specifies an array of node definition object representing the child nodes
13257 * @cfg {String} requestMethod either GET or POST
13258 * defaults to POST (due to BC)
13262 * @cfg {Object} baseParams (optional) An object containing properties which
13263 * specify HTTP parameters to be passed to each request for child nodes.
13266 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13267 * created by this loader. If the attributes sent by the server have an attribute in this object,
13268 * they take priority.
13271 * @cfg {Object} uiProviders (optional) An object containing properties which
13273 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13274 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13275 * <i>uiProvider</i> attribute of a returned child node is a string rather
13276 * than a reference to a TreeNodeUI implementation, this that string value
13277 * is used as a property name in the uiProviders object. You can define the provider named
13278 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13283 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13284 * child nodes before loading.
13286 clearOnLoad : true,
13289 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13290 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13291 * Grid query { data : [ .....] }
13296 * @cfg {String} queryParam (optional)
13297 * Name of the query as it will be passed on the querystring (defaults to 'node')
13298 * eg. the request will be ?node=[id]
13305 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13306 * This is called automatically when a node is expanded, but may be used to reload
13307 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13308 * @param {Roo.tree.TreeNode} node
13309 * @param {Function} callback
13311 load : function(node, callback){
13312 if(this.clearOnLoad){
13313 while(node.firstChild){
13314 node.removeChild(node.firstChild);
13317 if(node.attributes.children){ // preloaded json children
13318 var cs = node.attributes.children;
13319 for(var i = 0, len = cs.length; i < len; i++){
13320 node.appendChild(this.createNode(cs[i]));
13322 if(typeof callback == "function"){
13325 }else if(this.dataUrl){
13326 this.requestData(node, callback);
13330 getParams: function(node){
13331 var buf = [], bp = this.baseParams;
13332 for(var key in bp){
13333 if(typeof bp[key] != "function"){
13334 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13337 var n = this.queryParam === false ? 'node' : this.queryParam;
13338 buf.push(n + "=", encodeURIComponent(node.id));
13339 return buf.join("");
13342 requestData : function(node, callback){
13343 if(this.fireEvent("beforeload", this, node, callback) !== false){
13344 this.transId = Roo.Ajax.request({
13345 method:this.requestMethod,
13346 url: this.dataUrl||this.url,
13347 success: this.handleResponse,
13348 failure: this.handleFailure,
13350 argument: {callback: callback, node: node},
13351 params: this.getParams(node)
13354 // if the load is cancelled, make sure we notify
13355 // the node that we are done
13356 if(typeof callback == "function"){
13362 isLoading : function(){
13363 return this.transId ? true : false;
13366 abort : function(){
13367 if(this.isLoading()){
13368 Roo.Ajax.abort(this.transId);
13373 createNode : function(attr)
13375 // apply baseAttrs, nice idea Corey!
13376 if(this.baseAttrs){
13377 Roo.applyIf(attr, this.baseAttrs);
13379 if(this.applyLoader !== false){
13380 attr.loader = this;
13382 // uiProvider = depreciated..
13384 if(typeof(attr.uiProvider) == 'string'){
13385 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13386 /** eval:var:attr */ eval(attr.uiProvider);
13388 if(typeof(this.uiProviders['default']) != 'undefined') {
13389 attr.uiProvider = this.uiProviders['default'];
13392 this.fireEvent('create', this, attr);
13394 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13396 new Roo.tree.TreeNode(attr) :
13397 new Roo.tree.AsyncTreeNode(attr));
13400 processResponse : function(response, node, callback)
13402 var json = response.responseText;
13405 var o = Roo.decode(json);
13407 if (this.root === false && typeof(o.success) != undefined) {
13408 this.root = 'data'; // the default behaviour for list like data..
13411 if (this.root !== false && !o.success) {
13412 // it's a failure condition.
13413 var a = response.argument;
13414 this.fireEvent("loadexception", this, a.node, response);
13415 Roo.log("Load failed - should have a handler really");
13421 if (this.root !== false) {
13425 for(var i = 0, len = o.length; i < len; i++){
13426 var n = this.createNode(o[i]);
13428 node.appendChild(n);
13431 if(typeof callback == "function"){
13432 callback(this, node);
13435 this.handleFailure(response);
13439 handleResponse : function(response){
13440 this.transId = false;
13441 var a = response.argument;
13442 this.processResponse(response, a.node, a.callback);
13443 this.fireEvent("load", this, a.node, response);
13446 handleFailure : function(response)
13448 // should handle failure better..
13449 this.transId = false;
13450 var a = response.argument;
13451 this.fireEvent("loadexception", this, a.node, response);
13452 if(typeof a.callback == "function"){
13453 a.callback(this, a.node);
13458 * Ext JS Library 1.1.1
13459 * Copyright(c) 2006-2007, Ext JS, LLC.
13461 * Originally Released Under LGPL - original licence link has changed is not relivant.
13464 * <script type="text/javascript">
13468 * @class Roo.tree.TreeFilter
13469 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13470 * @param {TreePanel} tree
13471 * @param {Object} config (optional)
13473 Roo.tree.TreeFilter = function(tree, config){
13475 this.filtered = {};
13476 Roo.apply(this, config);
13479 Roo.tree.TreeFilter.prototype = {
13486 * Filter the data by a specific attribute.
13487 * @param {String/RegExp} value Either string that the attribute value
13488 * should start with or a RegExp to test against the attribute
13489 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13490 * @param {TreeNode} startNode (optional) The node to start the filter at.
13492 filter : function(value, attr, startNode){
13493 attr = attr || "text";
13495 if(typeof value == "string"){
13496 var vlen = value.length;
13497 // auto clear empty filter
13498 if(vlen == 0 && this.clearBlank){
13502 value = value.toLowerCase();
13504 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13506 }else if(value.exec){ // regex?
13508 return value.test(n.attributes[attr]);
13511 throw 'Illegal filter type, must be string or regex';
13513 this.filterBy(f, null, startNode);
13517 * Filter by a function. The passed function will be called with each
13518 * node in the tree (or from the startNode). If the function returns true, the node is kept
13519 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13520 * @param {Function} fn The filter function
13521 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13523 filterBy : function(fn, scope, startNode){
13524 startNode = startNode || this.tree.root;
13525 if(this.autoClear){
13528 var af = this.filtered, rv = this.reverse;
13529 var f = function(n){
13530 if(n == startNode){
13536 var m = fn.call(scope || n, n);
13544 startNode.cascade(f);
13547 if(typeof id != "function"){
13549 if(n && n.parentNode){
13550 n.parentNode.removeChild(n);
13558 * Clears the current filter. Note: with the "remove" option
13559 * set a filter cannot be cleared.
13561 clear : function(){
13563 var af = this.filtered;
13565 if(typeof id != "function"){
13572 this.filtered = {};
13577 * Ext JS Library 1.1.1
13578 * Copyright(c) 2006-2007, Ext JS, LLC.
13580 * Originally Released Under LGPL - original licence link has changed is not relivant.
13583 * <script type="text/javascript">
13588 * @class Roo.tree.TreeSorter
13589 * Provides sorting of nodes in a TreePanel
13591 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13592 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13593 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13594 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13595 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13596 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13598 * @param {TreePanel} tree
13599 * @param {Object} config
13601 Roo.tree.TreeSorter = function(tree, config){
13602 Roo.apply(this, config);
13603 tree.on("beforechildrenrendered", this.doSort, this);
13604 tree.on("append", this.updateSort, this);
13605 tree.on("insert", this.updateSort, this);
13607 var dsc = this.dir && this.dir.toLowerCase() == "desc";
13608 var p = this.property || "text";
13609 var sortType = this.sortType;
13610 var fs = this.folderSort;
13611 var cs = this.caseSensitive === true;
13612 var leafAttr = this.leafAttr || 'leaf';
13614 this.sortFn = function(n1, n2){
13616 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13619 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13623 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13624 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13626 return dsc ? +1 : -1;
13628 return dsc ? -1 : +1;
13635 Roo.tree.TreeSorter.prototype = {
13636 doSort : function(node){
13637 node.sort(this.sortFn);
13640 compareNodes : function(n1, n2){
13641 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13644 updateSort : function(tree, node){
13645 if(node.childrenRendered){
13646 this.doSort.defer(1, this, [node]);
13651 * Ext JS Library 1.1.1
13652 * Copyright(c) 2006-2007, Ext JS, LLC.
13654 * Originally Released Under LGPL - original licence link has changed is not relivant.
13657 * <script type="text/javascript">
13660 if(Roo.dd.DropZone){
13662 Roo.tree.TreeDropZone = function(tree, config){
13663 this.allowParentInsert = false;
13664 this.allowContainerDrop = false;
13665 this.appendOnly = false;
13666 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13668 this.lastInsertClass = "x-tree-no-status";
13669 this.dragOverData = {};
13672 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13673 ddGroup : "TreeDD",
13676 expandDelay : 1000,
13678 expandNode : function(node){
13679 if(node.hasChildNodes() && !node.isExpanded()){
13680 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13684 queueExpand : function(node){
13685 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13688 cancelExpand : function(){
13689 if(this.expandProcId){
13690 clearTimeout(this.expandProcId);
13691 this.expandProcId = false;
13695 isValidDropPoint : function(n, pt, dd, e, data){
13696 if(!n || !data){ return false; }
13697 var targetNode = n.node;
13698 var dropNode = data.node;
13699 // default drop rules
13700 if(!(targetNode && targetNode.isTarget && pt)){
13703 if(pt == "append" && targetNode.allowChildren === false){
13706 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13709 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13712 // reuse the object
13713 var overEvent = this.dragOverData;
13714 overEvent.tree = this.tree;
13715 overEvent.target = targetNode;
13716 overEvent.data = data;
13717 overEvent.point = pt;
13718 overEvent.source = dd;
13719 overEvent.rawEvent = e;
13720 overEvent.dropNode = dropNode;
13721 overEvent.cancel = false;
13722 var result = this.tree.fireEvent("nodedragover", overEvent);
13723 return overEvent.cancel === false && result !== false;
13726 getDropPoint : function(e, n, dd)
13730 return tn.allowChildren !== false ? "append" : false; // always append for root
13732 var dragEl = n.ddel;
13733 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13734 var y = Roo.lib.Event.getPageY(e);
13735 //var noAppend = tn.allowChildren === false || tn.isLeaf();
13737 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13738 var noAppend = tn.allowChildren === false;
13739 if(this.appendOnly || tn.parentNode.allowChildren === false){
13740 return noAppend ? false : "append";
13742 var noBelow = false;
13743 if(!this.allowParentInsert){
13744 noBelow = tn.hasChildNodes() && tn.isExpanded();
13746 var q = (b - t) / (noAppend ? 2 : 3);
13747 if(y >= t && y < (t + q)){
13749 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13756 onNodeEnter : function(n, dd, e, data)
13758 this.cancelExpand();
13761 onNodeOver : function(n, dd, e, data)
13764 var pt = this.getDropPoint(e, n, dd);
13767 // auto node expand check
13768 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13769 this.queueExpand(node);
13770 }else if(pt != "append"){
13771 this.cancelExpand();
13774 // set the insert point style on the target node
13775 var returnCls = this.dropNotAllowed;
13776 if(this.isValidDropPoint(n, pt, dd, e, data)){
13781 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13782 cls = "x-tree-drag-insert-above";
13783 }else if(pt == "below"){
13784 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13785 cls = "x-tree-drag-insert-below";
13787 returnCls = "x-tree-drop-ok-append";
13788 cls = "x-tree-drag-append";
13790 if(this.lastInsertClass != cls){
13791 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13792 this.lastInsertClass = cls;
13799 onNodeOut : function(n, dd, e, data){
13801 this.cancelExpand();
13802 this.removeDropIndicators(n);
13805 onNodeDrop : function(n, dd, e, data){
13806 var point = this.getDropPoint(e, n, dd);
13807 var targetNode = n.node;
13808 targetNode.ui.startDrop();
13809 if(!this.isValidDropPoint(n, point, dd, e, data)){
13810 targetNode.ui.endDrop();
13813 // first try to find the drop node
13814 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13817 target: targetNode,
13822 dropNode: dropNode,
13825 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13826 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13827 targetNode.ui.endDrop();
13830 // allow target changing
13831 targetNode = dropEvent.target;
13832 if(point == "append" && !targetNode.isExpanded()){
13833 targetNode.expand(false, null, function(){
13834 this.completeDrop(dropEvent);
13835 }.createDelegate(this));
13837 this.completeDrop(dropEvent);
13842 completeDrop : function(de){
13843 var ns = de.dropNode, p = de.point, t = de.target;
13844 if(!(ns instanceof Array)){
13848 for(var i = 0, len = ns.length; i < len; i++){
13851 t.parentNode.insertBefore(n, t);
13852 }else if(p == "below"){
13853 t.parentNode.insertBefore(n, t.nextSibling);
13859 if(this.tree.hlDrop){
13863 this.tree.fireEvent("nodedrop", de);
13866 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13867 if(this.tree.hlDrop){
13868 dropNode.ui.focus();
13869 dropNode.ui.highlight();
13871 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13874 getTree : function(){
13878 removeDropIndicators : function(n){
13881 Roo.fly(el).removeClass([
13882 "x-tree-drag-insert-above",
13883 "x-tree-drag-insert-below",
13884 "x-tree-drag-append"]);
13885 this.lastInsertClass = "_noclass";
13889 beforeDragDrop : function(target, e, id){
13890 this.cancelExpand();
13894 afterRepair : function(data){
13895 if(data && Roo.enableFx){
13896 data.node.ui.highlight();
13906 * Ext JS Library 1.1.1
13907 * Copyright(c) 2006-2007, Ext JS, LLC.
13909 * Originally Released Under LGPL - original licence link has changed is not relivant.
13912 * <script type="text/javascript">
13916 if(Roo.dd.DragZone){
13917 Roo.tree.TreeDragZone = function(tree, config){
13918 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13922 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13923 ddGroup : "TreeDD",
13925 onBeforeDrag : function(data, e){
13927 return n && n.draggable && !n.disabled;
13931 onInitDrag : function(e){
13932 var data = this.dragData;
13933 this.tree.getSelectionModel().select(data.node);
13934 this.proxy.update("");
13935 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13936 this.tree.fireEvent("startdrag", this.tree, data.node, e);
13939 getRepairXY : function(e, data){
13940 return data.node.ui.getDDRepairXY();
13943 onEndDrag : function(data, e){
13944 this.tree.fireEvent("enddrag", this.tree, data.node, e);
13949 onValidDrop : function(dd, e, id){
13950 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13954 beforeInvalidDrop : function(e, id){
13955 // this scrolls the original position back into view
13956 var sm = this.tree.getSelectionModel();
13957 sm.clearSelections();
13958 sm.select(this.dragData.node);
13963 * Ext JS Library 1.1.1
13964 * Copyright(c) 2006-2007, Ext JS, LLC.
13966 * Originally Released Under LGPL - original licence link has changed is not relivant.
13969 * <script type="text/javascript">
13972 * @class Roo.tree.TreeEditor
13973 * @extends Roo.Editor
13974 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
13975 * as the editor field.
13977 * @param {Object} config (used to be the tree panel.)
13978 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13980 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13981 * @cfg {Roo.form.TextField} field [required] The field configuration
13985 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13988 if (oldconfig) { // old style..
13989 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13992 tree = config.tree;
13993 config.field = config.field || {};
13994 config.field.xtype = 'TextField';
13995 field = Roo.factory(config.field, Roo.form);
13997 config = config || {};
14002 * @event beforenodeedit
14003 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14004 * false from the handler of this event.
14005 * @param {Editor} this
14006 * @param {Roo.tree.Node} node
14008 "beforenodeedit" : true
14012 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14016 tree.on('beforeclick', this.beforeNodeClick, this);
14017 tree.getTreeEl().on('mousedown', this.hide, this);
14018 this.on('complete', this.updateNode, this);
14019 this.on('beforestartedit', this.fitToTree, this);
14020 this.on('startedit', this.bindScroll, this, {delay:10});
14021 this.on('specialkey', this.onSpecialKey, this);
14024 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14026 * @cfg {String} alignment
14027 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14033 * @cfg {Boolean} hideEl
14034 * True to hide the bound element while the editor is displayed (defaults to false)
14038 * @cfg {String} cls
14039 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14041 cls: "x-small-editor x-tree-editor",
14043 * @cfg {Boolean} shim
14044 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14050 * @cfg {Number} maxWidth
14051 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14052 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14053 * scroll and client offsets into account prior to each edit.
14060 fitToTree : function(ed, el){
14061 var td = this.tree.getTreeEl().dom, nd = el.dom;
14062 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14063 td.scrollLeft = nd.offsetLeft;
14067 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14068 this.setSize(w, '');
14070 return this.fireEvent('beforenodeedit', this, this.editNode);
14075 triggerEdit : function(node){
14076 this.completeEdit();
14077 this.editNode = node;
14078 this.startEdit(node.ui.textNode, node.text);
14082 bindScroll : function(){
14083 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14087 beforeNodeClick : function(node, e){
14088 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14089 this.lastClick = new Date();
14090 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14092 this.triggerEdit(node);
14099 updateNode : function(ed, value){
14100 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14101 this.editNode.setText(value);
14105 onHide : function(){
14106 Roo.tree.TreeEditor.superclass.onHide.call(this);
14108 this.editNode.ui.focus();
14113 onSpecialKey : function(field, e){
14114 var k = e.getKey();
14118 }else if(k == e.ENTER && !e.hasModifier()){
14120 this.completeEdit();
14123 });//<Script type="text/javascript">
14126 * Ext JS Library 1.1.1
14127 * Copyright(c) 2006-2007, Ext JS, LLC.
14129 * Originally Released Under LGPL - original licence link has changed is not relivant.
14132 * <script type="text/javascript">
14136 * Not documented??? - probably should be...
14139 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14140 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14142 renderElements : function(n, a, targetNode, bulkRender){
14143 //consel.log("renderElements?");
14144 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14146 var t = n.getOwnerTree();
14147 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14149 var cols = t.columns;
14150 var bw = t.borderWidth;
14152 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14153 var cb = typeof a.checked == "boolean";
14154 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14155 var colcls = 'x-t-' + tid + '-c0';
14157 '<li class="x-tree-node">',
14160 '<div class="x-tree-node-el ', a.cls,'">',
14162 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14165 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14166 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14167 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14168 (a.icon ? ' x-tree-node-inline-icon' : ''),
14169 (a.iconCls ? ' '+a.iconCls : ''),
14170 '" unselectable="on" />',
14171 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14172 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14174 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14175 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14176 '<span unselectable="on" qtip="' + tx + '">',
14180 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14181 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14183 for(var i = 1, len = cols.length; i < len; i++){
14185 colcls = 'x-t-' + tid + '-c' +i;
14186 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14187 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14188 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14194 '<div class="x-clear"></div></div>',
14195 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14198 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14199 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14200 n.nextSibling.ui.getEl(), buf.join(""));
14202 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14204 var el = this.wrap.firstChild;
14206 this.elNode = el.firstChild;
14207 this.ranchor = el.childNodes[1];
14208 this.ctNode = this.wrap.childNodes[1];
14209 var cs = el.firstChild.childNodes;
14210 this.indentNode = cs[0];
14211 this.ecNode = cs[1];
14212 this.iconNode = cs[2];
14215 this.checkbox = cs[3];
14218 this.anchor = cs[index];
14220 this.textNode = cs[index].firstChild;
14222 //el.on("click", this.onClick, this);
14223 //el.on("dblclick", this.onDblClick, this);
14226 // console.log(this);
14228 initEvents : function(){
14229 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14232 var a = this.ranchor;
14234 var el = Roo.get(a);
14236 if(Roo.isOpera){ // opera render bug ignores the CSS
14237 el.setStyle("text-decoration", "none");
14240 el.on("click", this.onClick, this);
14241 el.on("dblclick", this.onDblClick, this);
14242 el.on("contextmenu", this.onContextMenu, this);
14246 /*onSelectedChange : function(state){
14249 this.addClass("x-tree-selected");
14252 this.removeClass("x-tree-selected");
14255 addClass : function(cls){
14257 Roo.fly(this.elRow).addClass(cls);
14263 removeClass : function(cls){
14265 Roo.fly(this.elRow).removeClass(cls);
14271 });//<Script type="text/javascript">
14275 * Ext JS Library 1.1.1
14276 * Copyright(c) 2006-2007, Ext JS, LLC.
14278 * Originally Released Under LGPL - original licence link has changed is not relivant.
14281 * <script type="text/javascript">
14286 * @class Roo.tree.ColumnTree
14287 * @extends Roo.data.TreePanel
14288 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14289 * @cfg {int} borderWidth compined right/left border allowance
14291 * @param {String/HTMLElement/Element} el The container element
14292 * @param {Object} config
14294 Roo.tree.ColumnTree = function(el, config)
14296 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14300 * Fire this event on a container when it resizes
14301 * @param {int} w Width
14302 * @param {int} h Height
14306 this.on('resize', this.onResize, this);
14309 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14313 borderWidth: Roo.isBorderBox ? 0 : 2,
14316 render : function(){
14317 // add the header.....
14319 Roo.tree.ColumnTree.superclass.render.apply(this);
14321 this.el.addClass('x-column-tree');
14323 this.headers = this.el.createChild(
14324 {cls:'x-tree-headers'},this.innerCt.dom);
14326 var cols = this.columns, c;
14327 var totalWidth = 0;
14329 var len = cols.length;
14330 for(var i = 0; i < len; i++){
14332 totalWidth += c.width;
14333 this.headEls.push(this.headers.createChild({
14334 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14336 cls:'x-tree-hd-text',
14339 style:'width:'+(c.width-this.borderWidth)+'px;'
14342 this.headers.createChild({cls:'x-clear'});
14343 // prevent floats from wrapping when clipped
14344 this.headers.setWidth(totalWidth);
14345 //this.innerCt.setWidth(totalWidth);
14346 this.innerCt.setStyle({ overflow: 'auto' });
14347 this.onResize(this.width, this.height);
14351 onResize : function(w,h)
14356 this.innerCt.setWidth(this.width);
14357 this.innerCt.setHeight(this.height-20);
14360 var cols = this.columns, c;
14361 var totalWidth = 0;
14363 var len = cols.length;
14364 for(var i = 0; i < len; i++){
14366 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14367 // it's the expander..
14368 expEl = this.headEls[i];
14371 totalWidth += c.width;
14375 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14377 this.headers.setWidth(w-20);
14386 * Ext JS Library 1.1.1
14387 * Copyright(c) 2006-2007, Ext JS, LLC.
14389 * Originally Released Under LGPL - original licence link has changed is not relivant.
14392 * <script type="text/javascript">
14396 * @class Roo.menu.Menu
14397 * @extends Roo.util.Observable
14398 * @children Roo.menu.BaseItem
14399 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14400 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14402 * Creates a new Menu
14403 * @param {Object} config Configuration options
14405 Roo.menu.Menu = function(config){
14407 Roo.menu.Menu.superclass.constructor.call(this, config);
14409 this.id = this.id || Roo.id();
14412 * @event beforeshow
14413 * Fires before this menu is displayed
14414 * @param {Roo.menu.Menu} this
14418 * @event beforehide
14419 * Fires before this menu is hidden
14420 * @param {Roo.menu.Menu} this
14425 * Fires after this menu is displayed
14426 * @param {Roo.menu.Menu} this
14431 * Fires after this menu is hidden
14432 * @param {Roo.menu.Menu} this
14437 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14438 * @param {Roo.menu.Menu} this
14439 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14440 * @param {Roo.EventObject} e
14445 * Fires when the mouse is hovering over this menu
14446 * @param {Roo.menu.Menu} this
14447 * @param {Roo.EventObject} e
14448 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14453 * Fires when the mouse exits this menu
14454 * @param {Roo.menu.Menu} this
14455 * @param {Roo.EventObject} e
14456 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14461 * Fires when a menu item contained in this menu is clicked
14462 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14463 * @param {Roo.EventObject} e
14467 if (this.registerMenu) {
14468 Roo.menu.MenuMgr.register(this);
14471 var mis = this.items;
14472 this.items = new Roo.util.MixedCollection();
14474 this.add.apply(this, mis);
14478 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14480 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14484 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14485 * for bottom-right shadow (defaults to "sides")
14489 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14490 * this menu (defaults to "tl-tr?")
14492 subMenuAlign : "tl-tr?",
14494 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14495 * relative to its element of origin (defaults to "tl-bl?")
14497 defaultAlign : "tl-bl?",
14499 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14501 allowOtherMenus : false,
14503 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14505 registerMenu : true,
14510 render : function(){
14514 var el = this.el = new Roo.Layer({
14516 shadow:this.shadow,
14518 parentEl: this.parentEl || document.body,
14522 this.keyNav = new Roo.menu.MenuNav(this);
14525 el.addClass("x-menu-plain");
14528 el.addClass(this.cls);
14530 // generic focus element
14531 this.focusEl = el.createChild({
14532 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14534 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14535 //disabling touch- as it's causing issues ..
14536 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14537 ul.on('click' , this.onClick, this);
14540 ul.on("mouseover", this.onMouseOver, this);
14541 ul.on("mouseout", this.onMouseOut, this);
14542 this.items.each(function(item){
14547 var li = document.createElement("li");
14548 li.className = "x-menu-list-item";
14549 ul.dom.appendChild(li);
14550 item.render(li, this);
14557 autoWidth : function(){
14558 var el = this.el, ul = this.ul;
14562 var w = this.width;
14565 }else if(Roo.isIE){
14566 el.setWidth(this.minWidth);
14567 var t = el.dom.offsetWidth; // force recalc
14568 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14573 delayAutoWidth : function(){
14576 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14578 this.awTask.delay(20);
14583 findTargetItem : function(e){
14584 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14585 if(t && t.menuItemId){
14586 return this.items.get(t.menuItemId);
14591 onClick : function(e){
14592 Roo.log("menu.onClick");
14593 var t = this.findTargetItem(e);
14598 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14599 if(t == this.activeItem && t.shouldDeactivate(e)){
14600 this.activeItem.deactivate();
14601 delete this.activeItem;
14605 this.setActiveItem(t, true);
14613 this.fireEvent("click", this, t, e);
14617 setActiveItem : function(item, autoExpand){
14618 if(item != this.activeItem){
14619 if(this.activeItem){
14620 this.activeItem.deactivate();
14622 this.activeItem = item;
14623 item.activate(autoExpand);
14624 }else if(autoExpand){
14630 tryActivate : function(start, step){
14631 var items = this.items;
14632 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14633 var item = items.get(i);
14634 if(!item.disabled && item.canActivate){
14635 this.setActiveItem(item, false);
14643 onMouseOver : function(e){
14645 if(t = this.findTargetItem(e)){
14646 if(t.canActivate && !t.disabled){
14647 this.setActiveItem(t, true);
14650 this.fireEvent("mouseover", this, e, t);
14654 onMouseOut : function(e){
14656 if(t = this.findTargetItem(e)){
14657 if(t == this.activeItem && t.shouldDeactivate(e)){
14658 this.activeItem.deactivate();
14659 delete this.activeItem;
14662 this.fireEvent("mouseout", this, e, t);
14666 * Read-only. Returns true if the menu is currently displayed, else false.
14669 isVisible : function(){
14670 return this.el && !this.hidden;
14674 * Displays this menu relative to another element
14675 * @param {String/HTMLElement/Roo.Element} element The element to align to
14676 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14677 * the element (defaults to this.defaultAlign)
14678 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14680 show : function(el, pos, parentMenu){
14681 this.parentMenu = parentMenu;
14685 this.fireEvent("beforeshow", this);
14686 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14690 * Displays this menu at a specific xy position
14691 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14692 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14694 showAt : function(xy, parentMenu, /* private: */_e){
14695 this.parentMenu = parentMenu;
14700 this.fireEvent("beforeshow", this);
14701 xy = this.el.adjustForConstraints(xy);
14705 this.hidden = false;
14707 this.fireEvent("show", this);
14710 focus : function(){
14712 this.doFocus.defer(50, this);
14716 doFocus : function(){
14718 this.focusEl.focus();
14723 * Hides this menu and optionally all parent menus
14724 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14726 hide : function(deep){
14727 if(this.el && this.isVisible()){
14728 this.fireEvent("beforehide", this);
14729 if(this.activeItem){
14730 this.activeItem.deactivate();
14731 this.activeItem = null;
14734 this.hidden = true;
14735 this.fireEvent("hide", this);
14737 if(deep === true && this.parentMenu){
14738 this.parentMenu.hide(true);
14743 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14744 * Any of the following are valid:
14746 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14747 * <li>An HTMLElement object which will be converted to a menu item</li>
14748 * <li>A menu item config object that will be created as a new menu item</li>
14749 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14750 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14755 var menu = new Roo.menu.Menu();
14757 // Create a menu item to add by reference
14758 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14760 // Add a bunch of items at once using different methods.
14761 // Only the last item added will be returned.
14762 var item = menu.add(
14763 menuItem, // add existing item by ref
14764 'Dynamic Item', // new TextItem
14765 '-', // new separator
14766 { text: 'Config Item' } // new item by config
14769 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14770 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14773 var a = arguments, l = a.length, item;
14774 for(var i = 0; i < l; i++){
14776 if ((typeof(el) == "object") && el.xtype && el.xns) {
14777 el = Roo.factory(el, Roo.menu);
14780 if(el.render){ // some kind of Item
14781 item = this.addItem(el);
14782 }else if(typeof el == "string"){ // string
14783 if(el == "separator" || el == "-"){
14784 item = this.addSeparator();
14786 item = this.addText(el);
14788 }else if(el.tagName || el.el){ // element
14789 item = this.addElement(el);
14790 }else if(typeof el == "object"){ // must be menu item config?
14791 item = this.addMenuItem(el);
14798 * Returns this menu's underlying {@link Roo.Element} object
14799 * @return {Roo.Element} The element
14801 getEl : function(){
14809 * Adds a separator bar to the menu
14810 * @return {Roo.menu.Item} The menu item that was added
14812 addSeparator : function(){
14813 return this.addItem(new Roo.menu.Separator());
14817 * Adds an {@link Roo.Element} object to the menu
14818 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14819 * @return {Roo.menu.Item} The menu item that was added
14821 addElement : function(el){
14822 return this.addItem(new Roo.menu.BaseItem(el));
14826 * Adds an existing object based on {@link Roo.menu.Item} to the menu
14827 * @param {Roo.menu.Item} item The menu item to add
14828 * @return {Roo.menu.Item} The menu item that was added
14830 addItem : function(item){
14831 this.items.add(item);
14833 var li = document.createElement("li");
14834 li.className = "x-menu-list-item";
14835 this.ul.dom.appendChild(li);
14836 item.render(li, this);
14837 this.delayAutoWidth();
14843 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14844 * @param {Object} config A MenuItem config object
14845 * @return {Roo.menu.Item} The menu item that was added
14847 addMenuItem : function(config){
14848 if(!(config instanceof Roo.menu.Item)){
14849 if(typeof config.checked == "boolean"){ // must be check menu item config?
14850 config = new Roo.menu.CheckItem(config);
14852 config = new Roo.menu.Item(config);
14855 return this.addItem(config);
14859 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14860 * @param {String} text The text to display in the menu item
14861 * @return {Roo.menu.Item} The menu item that was added
14863 addText : function(text){
14864 return this.addItem(new Roo.menu.TextItem({ text : text }));
14868 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14869 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14870 * @param {Roo.menu.Item} item The menu item to add
14871 * @return {Roo.menu.Item} The menu item that was added
14873 insert : function(index, item){
14874 this.items.insert(index, item);
14876 var li = document.createElement("li");
14877 li.className = "x-menu-list-item";
14878 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14879 item.render(li, this);
14880 this.delayAutoWidth();
14886 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14887 * @param {Roo.menu.Item} item The menu item to remove
14889 remove : function(item){
14890 this.items.removeKey(item.id);
14895 * Removes and destroys all items in the menu
14897 removeAll : function(){
14899 while(f = this.items.first()){
14905 // MenuNav is a private utility class used internally by the Menu
14906 Roo.menu.MenuNav = function(menu){
14907 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14908 this.scope = this.menu = menu;
14911 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14912 doRelay : function(e, h){
14913 var k = e.getKey();
14914 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14915 this.menu.tryActivate(0, 1);
14918 return h.call(this.scope || this, e, this.menu);
14921 up : function(e, m){
14922 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14923 m.tryActivate(m.items.length-1, -1);
14927 down : function(e, m){
14928 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14929 m.tryActivate(0, 1);
14933 right : function(e, m){
14935 m.activeItem.expandMenu(true);
14939 left : function(e, m){
14941 if(m.parentMenu && m.parentMenu.activeItem){
14942 m.parentMenu.activeItem.activate();
14946 enter : function(e, m){
14948 e.stopPropagation();
14949 m.activeItem.onClick(e);
14950 m.fireEvent("click", this, m.activeItem);
14956 * Ext JS Library 1.1.1
14957 * Copyright(c) 2006-2007, Ext JS, LLC.
14959 * Originally Released Under LGPL - original licence link has changed is not relivant.
14962 * <script type="text/javascript">
14966 * @class Roo.menu.MenuMgr
14967 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14970 Roo.menu.MenuMgr = function(){
14971 var menus, active, groups = {}, attached = false, lastShow = new Date();
14973 // private - called when first menu is created
14976 active = new Roo.util.MixedCollection();
14977 Roo.get(document).addKeyListener(27, function(){
14978 if(active.length > 0){
14985 function hideAll(){
14986 if(active && active.length > 0){
14987 var c = active.clone();
14988 c.each(function(m){
14995 function onHide(m){
14997 if(active.length < 1){
14998 Roo.get(document).un("mousedown", onMouseDown);
15004 function onShow(m){
15005 var last = active.last();
15006 lastShow = new Date();
15009 Roo.get(document).on("mousedown", onMouseDown);
15013 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15014 m.parentMenu.activeChild = m;
15015 }else if(last && last.isVisible()){
15016 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15021 function onBeforeHide(m){
15023 m.activeChild.hide();
15025 if(m.autoHideTimer){
15026 clearTimeout(m.autoHideTimer);
15027 delete m.autoHideTimer;
15032 function onBeforeShow(m){
15033 var pm = m.parentMenu;
15034 if(!pm && !m.allowOtherMenus){
15036 }else if(pm && pm.activeChild && active != m){
15037 pm.activeChild.hide();
15042 function onMouseDown(e){
15043 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15049 function onBeforeCheck(mi, state){
15051 var g = groups[mi.group];
15052 for(var i = 0, l = g.length; i < l; i++){
15054 g[i].setChecked(false);
15063 * Hides all menus that are currently visible
15065 hideAll : function(){
15070 register : function(menu){
15074 menus[menu.id] = menu;
15075 menu.on("beforehide", onBeforeHide);
15076 menu.on("hide", onHide);
15077 menu.on("beforeshow", onBeforeShow);
15078 menu.on("show", onShow);
15079 var g = menu.group;
15080 if(g && menu.events["checkchange"]){
15084 groups[g].push(menu);
15085 menu.on("checkchange", onCheck);
15090 * Returns a {@link Roo.menu.Menu} object
15091 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15092 * be used to generate and return a new Menu instance.
15094 get : function(menu){
15095 if(typeof menu == "string"){ // menu id
15096 return menus[menu];
15097 }else if(menu.events){ // menu instance
15099 }else if(typeof menu.length == 'number'){ // array of menu items?
15100 return new Roo.menu.Menu({items:menu});
15101 }else{ // otherwise, must be a config
15102 return new Roo.menu.Menu(menu);
15107 unregister : function(menu){
15108 delete menus[menu.id];
15109 menu.un("beforehide", onBeforeHide);
15110 menu.un("hide", onHide);
15111 menu.un("beforeshow", onBeforeShow);
15112 menu.un("show", onShow);
15113 var g = menu.group;
15114 if(g && menu.events["checkchange"]){
15115 groups[g].remove(menu);
15116 menu.un("checkchange", onCheck);
15121 registerCheckable : function(menuItem){
15122 var g = menuItem.group;
15127 groups[g].push(menuItem);
15128 menuItem.on("beforecheckchange", onBeforeCheck);
15133 unregisterCheckable : function(menuItem){
15134 var g = menuItem.group;
15136 groups[g].remove(menuItem);
15137 menuItem.un("beforecheckchange", onBeforeCheck);
15143 * Ext JS Library 1.1.1
15144 * Copyright(c) 2006-2007, Ext JS, LLC.
15146 * Originally Released Under LGPL - original licence link has changed is not relivant.
15149 * <script type="text/javascript">
15154 * @class Roo.menu.BaseItem
15155 * @extends Roo.Component
15157 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15158 * management and base configuration options shared by all menu components.
15160 * Creates a new BaseItem
15161 * @param {Object} config Configuration options
15163 Roo.menu.BaseItem = function(config){
15164 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15169 * Fires when this item is clicked
15170 * @param {Roo.menu.BaseItem} this
15171 * @param {Roo.EventObject} e
15176 * Fires when this item is activated
15177 * @param {Roo.menu.BaseItem} this
15181 * @event deactivate
15182 * Fires when this item is deactivated
15183 * @param {Roo.menu.BaseItem} this
15189 this.on("click", this.handler, this.scope, true);
15193 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15195 * @cfg {Function} handler
15196 * A function that will handle the click event of this menu item (defaults to undefined)
15199 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15201 canActivate : false,
15204 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15209 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15211 activeClass : "x-menu-item-active",
15213 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15215 hideOnClick : true,
15217 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15222 ctype: "Roo.menu.BaseItem",
15225 actionMode : "container",
15228 render : function(container, parentMenu){
15229 this.parentMenu = parentMenu;
15230 Roo.menu.BaseItem.superclass.render.call(this, container);
15231 this.container.menuItemId = this.id;
15235 onRender : function(container, position){
15236 this.el = Roo.get(this.el);
15237 container.dom.appendChild(this.el.dom);
15241 onClick : function(e){
15242 if(!this.disabled && this.fireEvent("click", this, e) !== false
15243 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15244 this.handleClick(e);
15251 activate : function(){
15255 var li = this.container;
15256 li.addClass(this.activeClass);
15257 this.region = li.getRegion().adjust(2, 2, -2, -2);
15258 this.fireEvent("activate", this);
15263 deactivate : function(){
15264 this.container.removeClass(this.activeClass);
15265 this.fireEvent("deactivate", this);
15269 shouldDeactivate : function(e){
15270 return !this.region || !this.region.contains(e.getPoint());
15274 handleClick : function(e){
15275 if(this.hideOnClick){
15276 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15281 expandMenu : function(autoActivate){
15286 hideMenu : function(){
15291 * Ext JS Library 1.1.1
15292 * Copyright(c) 2006-2007, Ext JS, LLC.
15294 * Originally Released Under LGPL - original licence link has changed is not relivant.
15297 * <script type="text/javascript">
15301 * @class Roo.menu.Adapter
15302 * @extends Roo.menu.BaseItem
15304 * 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.
15305 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15307 * Creates a new Adapter
15308 * @param {Object} config Configuration options
15310 Roo.menu.Adapter = function(component, config){
15311 Roo.menu.Adapter.superclass.constructor.call(this, config);
15312 this.component = component;
15314 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15316 canActivate : true,
15319 onRender : function(container, position){
15320 this.component.render(container);
15321 this.el = this.component.getEl();
15325 activate : function(){
15329 this.component.focus();
15330 this.fireEvent("activate", this);
15335 deactivate : function(){
15336 this.fireEvent("deactivate", this);
15340 disable : function(){
15341 this.component.disable();
15342 Roo.menu.Adapter.superclass.disable.call(this);
15346 enable : function(){
15347 this.component.enable();
15348 Roo.menu.Adapter.superclass.enable.call(this);
15352 * Ext JS Library 1.1.1
15353 * Copyright(c) 2006-2007, Ext JS, LLC.
15355 * Originally Released Under LGPL - original licence link has changed is not relivant.
15358 * <script type="text/javascript">
15362 * @class Roo.menu.TextItem
15363 * @extends Roo.menu.BaseItem
15364 * Adds a static text string to a menu, usually used as either a heading or group separator.
15365 * Note: old style constructor with text is still supported.
15368 * Creates a new TextItem
15369 * @param {Object} cfg Configuration
15371 Roo.menu.TextItem = function(cfg){
15372 if (typeof(cfg) == 'string') {
15375 Roo.apply(this,cfg);
15378 Roo.menu.TextItem.superclass.constructor.call(this);
15381 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15383 * @cfg {String} text Text to show on item.
15388 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15390 hideOnClick : false,
15392 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15394 itemCls : "x-menu-text",
15397 onRender : function(){
15398 var s = document.createElement("span");
15399 s.className = this.itemCls;
15400 s.innerHTML = this.text;
15402 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15406 * Ext JS Library 1.1.1
15407 * Copyright(c) 2006-2007, Ext JS, LLC.
15409 * Originally Released Under LGPL - original licence link has changed is not relivant.
15412 * <script type="text/javascript">
15416 * @class Roo.menu.Separator
15417 * @extends Roo.menu.BaseItem
15418 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15419 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15421 * @param {Object} config Configuration options
15423 Roo.menu.Separator = function(config){
15424 Roo.menu.Separator.superclass.constructor.call(this, config);
15427 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15429 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15431 itemCls : "x-menu-sep",
15433 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15435 hideOnClick : false,
15438 onRender : function(li){
15439 var s = document.createElement("span");
15440 s.className = this.itemCls;
15441 s.innerHTML = " ";
15443 li.addClass("x-menu-sep-li");
15444 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15448 * Ext JS Library 1.1.1
15449 * Copyright(c) 2006-2007, Ext JS, LLC.
15451 * Originally Released Under LGPL - original licence link has changed is not relivant.
15454 * <script type="text/javascript">
15457 * @class Roo.menu.Item
15458 * @extends Roo.menu.BaseItem
15459 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15460 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15461 * activation and click handling.
15463 * Creates a new Item
15464 * @param {Object} config Configuration options
15466 Roo.menu.Item = function(config){
15467 Roo.menu.Item.superclass.constructor.call(this, config);
15469 this.menu = Roo.menu.MenuMgr.get(this.menu);
15472 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15474 * @cfg {Roo.menu.Menu} menu
15478 * @cfg {String} text
15479 * The text to show on the menu item.
15483 * @cfg {String} HTML to render in menu
15484 * The text to show on the menu item (HTML version).
15488 * @cfg {String} icon
15489 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15493 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15495 itemCls : "x-menu-item",
15497 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15499 canActivate : true,
15501 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15504 // doc'd in BaseItem
15508 ctype: "Roo.menu.Item",
15511 onRender : function(container, position){
15512 var el = document.createElement("a");
15513 el.hideFocus = true;
15514 el.unselectable = "on";
15515 el.href = this.href || "#";
15516 if(this.hrefTarget){
15517 el.target = this.hrefTarget;
15519 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15521 var html = this.html.length ? this.html : String.format('{0}',this.text);
15523 el.innerHTML = String.format(
15524 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15525 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15527 Roo.menu.Item.superclass.onRender.call(this, container, position);
15531 * Sets the text to display in this menu item
15532 * @param {String} text The text to display
15533 * @param {Boolean} isHTML true to indicate text is pure html.
15535 setText : function(text, isHTML){
15543 var html = this.html.length ? this.html : String.format('{0}',this.text);
15545 this.el.update(String.format(
15546 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15547 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15548 this.parentMenu.autoWidth();
15553 handleClick : function(e){
15554 if(!this.href){ // if no link defined, stop the event automatically
15557 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15561 activate : function(autoExpand){
15562 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15572 shouldDeactivate : function(e){
15573 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15574 if(this.menu && this.menu.isVisible()){
15575 return !this.menu.getEl().getRegion().contains(e.getPoint());
15583 deactivate : function(){
15584 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15589 expandMenu : function(autoActivate){
15590 if(!this.disabled && this.menu){
15591 clearTimeout(this.hideTimer);
15592 delete this.hideTimer;
15593 if(!this.menu.isVisible() && !this.showTimer){
15594 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15595 }else if (this.menu.isVisible() && autoActivate){
15596 this.menu.tryActivate(0, 1);
15602 deferExpand : function(autoActivate){
15603 delete this.showTimer;
15604 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15606 this.menu.tryActivate(0, 1);
15611 hideMenu : function(){
15612 clearTimeout(this.showTimer);
15613 delete this.showTimer;
15614 if(!this.hideTimer && this.menu && this.menu.isVisible()){
15615 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15620 deferHide : function(){
15621 delete this.hideTimer;
15626 * Ext JS Library 1.1.1
15627 * Copyright(c) 2006-2007, Ext JS, LLC.
15629 * Originally Released Under LGPL - original licence link has changed is not relivant.
15632 * <script type="text/javascript">
15636 * @class Roo.menu.CheckItem
15637 * @extends Roo.menu.Item
15638 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15640 * Creates a new CheckItem
15641 * @param {Object} config Configuration options
15643 Roo.menu.CheckItem = function(config){
15644 Roo.menu.CheckItem.superclass.constructor.call(this, config);
15647 * @event beforecheckchange
15648 * Fires before the checked value is set, providing an opportunity to cancel if needed
15649 * @param {Roo.menu.CheckItem} this
15650 * @param {Boolean} checked The new checked value that will be set
15652 "beforecheckchange" : true,
15654 * @event checkchange
15655 * Fires after the checked value has been set
15656 * @param {Roo.menu.CheckItem} this
15657 * @param {Boolean} checked The checked value that was set
15659 "checkchange" : true
15661 if(this.checkHandler){
15662 this.on('checkchange', this.checkHandler, this.scope);
15665 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15667 * @cfg {String} group
15668 * All check items with the same group name will automatically be grouped into a single-select
15669 * radio button group (defaults to '')
15672 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15674 itemCls : "x-menu-item x-menu-check-item",
15676 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15678 groupClass : "x-menu-group-item",
15681 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
15682 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15683 * initialized with checked = true will be rendered as checked.
15688 ctype: "Roo.menu.CheckItem",
15691 onRender : function(c){
15692 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15694 this.el.addClass(this.groupClass);
15696 Roo.menu.MenuMgr.registerCheckable(this);
15698 this.checked = false;
15699 this.setChecked(true, true);
15704 destroy : function(){
15706 Roo.menu.MenuMgr.unregisterCheckable(this);
15708 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15712 * Set the checked state of this item
15713 * @param {Boolean} checked The new checked value
15714 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15716 setChecked : function(state, suppressEvent){
15717 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15718 if(this.container){
15719 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15721 this.checked = state;
15722 if(suppressEvent !== true){
15723 this.fireEvent("checkchange", this, state);
15729 handleClick : function(e){
15730 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15731 this.setChecked(!this.checked);
15733 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15737 * Ext JS Library 1.1.1
15738 * Copyright(c) 2006-2007, Ext JS, LLC.
15740 * Originally Released Under LGPL - original licence link has changed is not relivant.
15743 * <script type="text/javascript">
15747 * @class Roo.menu.DateItem
15748 * @extends Roo.menu.Adapter
15749 * A menu item that wraps the {@link Roo.DatPicker} component.
15751 * Creates a new DateItem
15752 * @param {Object} config Configuration options
15754 Roo.menu.DateItem = function(config){
15755 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15756 /** The Roo.DatePicker object @type Roo.DatePicker */
15757 this.picker = this.component;
15758 this.addEvents({select: true});
15760 this.picker.on("render", function(picker){
15761 picker.getEl().swallowEvent("click");
15762 picker.container.addClass("x-menu-date-item");
15765 this.picker.on("select", this.onSelect, this);
15768 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15770 onSelect : function(picker, date){
15771 this.fireEvent("select", this, date, picker);
15772 Roo.menu.DateItem.superclass.handleClick.call(this);
15776 * Ext JS Library 1.1.1
15777 * Copyright(c) 2006-2007, Ext JS, LLC.
15779 * Originally Released Under LGPL - original licence link has changed is not relivant.
15782 * <script type="text/javascript">
15786 * @class Roo.menu.ColorItem
15787 * @extends Roo.menu.Adapter
15788 * A menu item that wraps the {@link Roo.ColorPalette} component.
15790 * Creates a new ColorItem
15791 * @param {Object} config Configuration options
15793 Roo.menu.ColorItem = function(config){
15794 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15795 /** The Roo.ColorPalette object @type Roo.ColorPalette */
15796 this.palette = this.component;
15797 this.relayEvents(this.palette, ["select"]);
15798 if(this.selectHandler){
15799 this.on('select', this.selectHandler, this.scope);
15802 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15804 * Ext JS Library 1.1.1
15805 * Copyright(c) 2006-2007, Ext JS, LLC.
15807 * Originally Released Under LGPL - original licence link has changed is not relivant.
15810 * <script type="text/javascript">
15815 * @class Roo.menu.DateMenu
15816 * @extends Roo.menu.Menu
15817 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15819 * Creates a new DateMenu
15820 * @param {Object} config Configuration options
15822 Roo.menu.DateMenu = function(config){
15823 Roo.menu.DateMenu.superclass.constructor.call(this, config);
15825 var di = new Roo.menu.DateItem(config);
15828 * The {@link Roo.DatePicker} instance for this DateMenu
15831 this.picker = di.picker;
15834 * @param {DatePicker} picker
15835 * @param {Date} date
15837 this.relayEvents(di, ["select"]);
15838 this.on('beforeshow', function(){
15840 this.picker.hideMonthPicker(false);
15844 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15848 * Ext JS Library 1.1.1
15849 * Copyright(c) 2006-2007, Ext JS, LLC.
15851 * Originally Released Under LGPL - original licence link has changed is not relivant.
15854 * <script type="text/javascript">
15859 * @class Roo.menu.ColorMenu
15860 * @extends Roo.menu.Menu
15861 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15863 * Creates a new ColorMenu
15864 * @param {Object} config Configuration options
15866 Roo.menu.ColorMenu = function(config){
15867 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15869 var ci = new Roo.menu.ColorItem(config);
15872 * The {@link Roo.ColorPalette} instance for this ColorMenu
15873 * @type ColorPalette
15875 this.palette = ci.palette;
15878 * @param {ColorPalette} palette
15879 * @param {String} color
15881 this.relayEvents(ci, ["select"]);
15883 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15885 * Ext JS Library 1.1.1
15886 * Copyright(c) 2006-2007, Ext JS, LLC.
15888 * Originally Released Under LGPL - original licence link has changed is not relivant.
15891 * <script type="text/javascript">
15895 * @class Roo.form.TextItem
15896 * @extends Roo.BoxComponent
15897 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15899 * Creates a new TextItem
15900 * @param {Object} config Configuration options
15902 Roo.form.TextItem = function(config){
15903 Roo.form.TextItem.superclass.constructor.call(this, config);
15906 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
15909 * @cfg {String} tag the tag for this item (default div)
15913 * @cfg {String} html the content for this item
15917 getAutoCreate : function()
15930 onRender : function(ct, position)
15932 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15935 var cfg = this.getAutoCreate();
15937 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15939 if (!cfg.name.length) {
15942 this.el = ct.createChild(cfg, position);
15947 * @param {String} html update the Contents of the element.
15949 setHTML : function(html)
15951 this.fieldEl.dom.innerHTML = html;
15956 * Ext JS Library 1.1.1
15957 * Copyright(c) 2006-2007, Ext JS, LLC.
15959 * Originally Released Under LGPL - original licence link has changed is not relivant.
15962 * <script type="text/javascript">
15966 * @class Roo.form.Field
15967 * @extends Roo.BoxComponent
15968 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15970 * Creates a new Field
15971 * @param {Object} config Configuration options
15973 Roo.form.Field = function(config){
15974 Roo.form.Field.superclass.constructor.call(this, config);
15977 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
15979 * @cfg {String} fieldLabel Label to use when rendering a form.
15982 * @cfg {String} qtip Mouse over tip
15986 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15988 invalidClass : "x-form-invalid",
15990 * @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")
15992 invalidText : "The value in this field is invalid",
15994 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15996 focusClass : "x-form-focus",
15998 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15999 automatic validation (defaults to "keyup").
16001 validationEvent : "keyup",
16003 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16005 validateOnBlur : true,
16007 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16009 validationDelay : 250,
16011 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16012 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16014 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16016 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16018 fieldClass : "x-form-field",
16020 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16023 ----------- ----------------------------------------------------------------------
16024 qtip Display a quick tip when the user hovers over the field
16025 title Display a default browser title attribute popup
16026 under Add a block div beneath the field containing the error text
16027 side Add an error icon to the right of the field with a popup on hover
16028 [element id] Add the error text directly to the innerHTML of the specified element
16031 msgTarget : 'qtip',
16033 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16038 * @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.
16043 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16048 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16050 inputType : undefined,
16053 * @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).
16055 tabIndex : undefined,
16058 isFormField : true,
16063 * @property {Roo.Element} fieldEl
16064 * Element Containing the rendered Field (with label etc.)
16067 * @cfg {Mixed} value A value to initialize this field with.
16072 * @cfg {String} name The field's HTML name attribute.
16075 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16078 loadedValue : false,
16082 initComponent : function(){
16083 Roo.form.Field.superclass.initComponent.call(this);
16087 * Fires when this field receives input focus.
16088 * @param {Roo.form.Field} this
16093 * Fires when this field loses input focus.
16094 * @param {Roo.form.Field} this
16098 * @event specialkey
16099 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16100 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16101 * @param {Roo.form.Field} this
16102 * @param {Roo.EventObject} e The event object
16107 * Fires just before the field blurs if the field value has changed.
16108 * @param {Roo.form.Field} this
16109 * @param {Mixed} newValue The new value
16110 * @param {Mixed} oldValue The original value
16115 * Fires after the field has been marked as invalid.
16116 * @param {Roo.form.Field} this
16117 * @param {String} msg The validation message
16122 * Fires after the field has been validated with no errors.
16123 * @param {Roo.form.Field} this
16128 * Fires after the key up
16129 * @param {Roo.form.Field} this
16130 * @param {Roo.EventObject} e The event Object
16137 * Returns the name attribute of the field if available
16138 * @return {String} name The field name
16140 getName: function(){
16141 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16145 onRender : function(ct, position){
16146 Roo.form.Field.superclass.onRender.call(this, ct, position);
16148 var cfg = this.getAutoCreate();
16150 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16152 if (!cfg.name.length) {
16155 if(this.inputType){
16156 cfg.type = this.inputType;
16158 this.el = ct.createChild(cfg, position);
16160 var type = this.el.dom.type;
16162 if(type == 'password'){
16165 this.el.addClass('x-form-'+type);
16168 this.el.dom.readOnly = true;
16170 if(this.tabIndex !== undefined){
16171 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16174 this.el.addClass([this.fieldClass, this.cls]);
16179 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16180 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16181 * @return {Roo.form.Field} this
16183 applyTo : function(target){
16184 this.allowDomMove = false;
16185 this.el = Roo.get(target);
16186 this.render(this.el.dom.parentNode);
16191 initValue : function(){
16192 if(this.value !== undefined){
16193 this.setValue(this.value);
16194 }else if(this.el.dom.value.length > 0){
16195 this.setValue(this.el.dom.value);
16200 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16201 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16203 isDirty : function() {
16204 if(this.disabled) {
16207 return String(this.getValue()) !== String(this.originalValue);
16211 * stores the current value in loadedValue
16213 resetHasChanged : function()
16215 this.loadedValue = String(this.getValue());
16218 * checks the current value against the 'loaded' value.
16219 * Note - will return false if 'resetHasChanged' has not been called first.
16221 hasChanged : function()
16223 if(this.disabled || this.readOnly) {
16226 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16232 afterRender : function(){
16233 Roo.form.Field.superclass.afterRender.call(this);
16238 fireKey : function(e){
16239 //Roo.log('field ' + e.getKey());
16240 if(e.isNavKeyPress()){
16241 this.fireEvent("specialkey", this, e);
16246 * Resets the current field value to the originally loaded value and clears any validation messages
16248 reset : function(){
16249 this.setValue(this.resetValue);
16250 this.originalValue = this.getValue();
16251 this.clearInvalid();
16255 initEvents : function(){
16256 // safari killled keypress - so keydown is now used..
16257 this.el.on("keydown" , this.fireKey, this);
16258 this.el.on("focus", this.onFocus, this);
16259 this.el.on("blur", this.onBlur, this);
16260 this.el.relayEvent('keyup', this);
16262 // reference to original value for reset
16263 this.originalValue = this.getValue();
16264 this.resetValue = this.getValue();
16268 onFocus : function(){
16269 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16270 this.el.addClass(this.focusClass);
16272 if(!this.hasFocus){
16273 this.hasFocus = true;
16274 this.startValue = this.getValue();
16275 this.fireEvent("focus", this);
16279 beforeBlur : Roo.emptyFn,
16282 onBlur : function(){
16284 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16285 this.el.removeClass(this.focusClass);
16287 this.hasFocus = false;
16288 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16291 var v = this.getValue();
16292 if(String(v) !== String(this.startValue)){
16293 this.fireEvent('change', this, v, this.startValue);
16295 this.fireEvent("blur", this);
16299 * Returns whether or not the field value is currently valid
16300 * @param {Boolean} preventMark True to disable marking the field invalid
16301 * @return {Boolean} True if the value is valid, else false
16303 isValid : function(preventMark){
16307 var restore = this.preventMark;
16308 this.preventMark = preventMark === true;
16309 var v = this.validateValue(this.processValue(this.getRawValue()));
16310 this.preventMark = restore;
16315 * Validates the field value
16316 * @return {Boolean} True if the value is valid, else false
16318 validate : function(){
16319 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16320 this.clearInvalid();
16326 processValue : function(value){
16331 // Subclasses should provide the validation implementation by overriding this
16332 validateValue : function(value){
16337 * Mark this field as invalid
16338 * @param {String} msg The validation message
16340 markInvalid : function(msg){
16341 if(!this.rendered || this.preventMark){ // not rendered
16345 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16347 obj.el.addClass(this.invalidClass);
16348 msg = msg || this.invalidText;
16349 switch(this.msgTarget){
16351 obj.el.dom.qtip = msg;
16352 obj.el.dom.qclass = 'x-form-invalid-tip';
16353 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16354 Roo.QuickTips.enable();
16358 this.el.dom.title = msg;
16362 var elp = this.el.findParent('.x-form-element', 5, true);
16363 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16364 this.errorEl.setWidth(elp.getWidth(true)-20);
16366 this.errorEl.update(msg);
16367 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16370 if(!this.errorIcon){
16371 var elp = this.el.findParent('.x-form-element', 5, true);
16372 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16374 this.alignErrorIcon();
16375 this.errorIcon.dom.qtip = msg;
16376 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16377 this.errorIcon.show();
16378 this.on('resize', this.alignErrorIcon, this);
16381 var t = Roo.getDom(this.msgTarget);
16383 t.style.display = this.msgDisplay;
16386 this.fireEvent('invalid', this, msg);
16390 alignErrorIcon : function(){
16391 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16395 * Clear any invalid styles/messages for this field
16397 clearInvalid : function(){
16398 if(!this.rendered || this.preventMark){ // not rendered
16401 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16403 obj.el.removeClass(this.invalidClass);
16404 switch(this.msgTarget){
16406 obj.el.dom.qtip = '';
16409 this.el.dom.title = '';
16413 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16417 if(this.errorIcon){
16418 this.errorIcon.dom.qtip = '';
16419 this.errorIcon.hide();
16420 this.un('resize', this.alignErrorIcon, this);
16424 var t = Roo.getDom(this.msgTarget);
16426 t.style.display = 'none';
16429 this.fireEvent('valid', this);
16433 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16434 * @return {Mixed} value The field value
16436 getRawValue : function(){
16437 var v = this.el.getValue();
16443 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16444 * @return {Mixed} value The field value
16446 getValue : function(){
16447 var v = this.el.getValue();
16453 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16454 * @param {Mixed} value The value to set
16456 setRawValue : function(v){
16457 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16461 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16462 * @param {Mixed} value The value to set
16464 setValue : function(v){
16467 this.el.dom.value = (v === null || v === undefined ? '' : v);
16472 adjustSize : function(w, h){
16473 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16474 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16478 adjustWidth : function(tag, w){
16479 tag = tag.toLowerCase();
16480 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16481 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16482 if(tag == 'input'){
16485 if(tag == 'textarea'){
16488 }else if(Roo.isOpera){
16489 if(tag == 'input'){
16492 if(tag == 'textarea'){
16502 // anything other than normal should be considered experimental
16503 Roo.form.Field.msgFx = {
16505 show: function(msgEl, f){
16506 msgEl.setDisplayed('block');
16509 hide : function(msgEl, f){
16510 msgEl.setDisplayed(false).update('');
16515 show: function(msgEl, f){
16516 msgEl.slideIn('t', {stopFx:true});
16519 hide : function(msgEl, f){
16520 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16525 show: function(msgEl, f){
16526 msgEl.fixDisplay();
16527 msgEl.alignTo(f.el, 'tl-tr');
16528 msgEl.slideIn('l', {stopFx:true});
16531 hide : function(msgEl, f){
16532 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16537 * Ext JS Library 1.1.1
16538 * Copyright(c) 2006-2007, Ext JS, LLC.
16540 * Originally Released Under LGPL - original licence link has changed is not relivant.
16543 * <script type="text/javascript">
16548 * @class Roo.form.TextField
16549 * @extends Roo.form.Field
16550 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16551 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16553 * Creates a new TextField
16554 * @param {Object} config Configuration options
16556 Roo.form.TextField = function(config){
16557 Roo.form.TextField.superclass.constructor.call(this, config);
16561 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16562 * according to the default logic, but this event provides a hook for the developer to apply additional
16563 * logic at runtime to resize the field if needed.
16564 * @param {Roo.form.Field} this This text field
16565 * @param {Number} width The new field width
16571 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16573 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16577 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16581 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16585 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16589 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16593 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16595 disableKeyFilter : false,
16597 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16601 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16605 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16607 maxLength : Number.MAX_VALUE,
16609 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16611 minLengthText : "The minimum length for this field is {0}",
16613 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16615 maxLengthText : "The maximum length for this field is {0}",
16617 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16619 selectOnFocus : false,
16621 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
16623 allowLeadingSpace : false,
16625 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16627 blankText : "This field is required",
16629 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16630 * If available, this function will be called only after the basic validators all return true, and will be passed the
16631 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16635 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16636 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16637 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
16641 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16645 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16651 initEvents : function()
16653 if (this.emptyText) {
16654 this.el.attr('placeholder', this.emptyText);
16657 Roo.form.TextField.superclass.initEvents.call(this);
16658 if(this.validationEvent == 'keyup'){
16659 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16660 this.el.on('keyup', this.filterValidation, this);
16662 else if(this.validationEvent !== false){
16663 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16666 if(this.selectOnFocus){
16667 this.on("focus", this.preFocus, this);
16669 if (!this.allowLeadingSpace) {
16670 this.on('blur', this.cleanLeadingSpace, this);
16673 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16674 this.el.on("keypress", this.filterKeys, this);
16677 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
16678 this.el.on("click", this.autoSize, this);
16680 if(this.el.is('input[type=password]') && Roo.isSafari){
16681 this.el.on('keydown', this.SafariOnKeyDown, this);
16685 processValue : function(value){
16686 if(this.stripCharsRe){
16687 var newValue = value.replace(this.stripCharsRe, '');
16688 if(newValue !== value){
16689 this.setRawValue(newValue);
16696 filterValidation : function(e){
16697 if(!e.isNavKeyPress()){
16698 this.validationTask.delay(this.validationDelay);
16703 onKeyUp : function(e){
16704 if(!e.isNavKeyPress()){
16708 // private - clean the leading white space
16709 cleanLeadingSpace : function(e)
16711 if ( this.inputType == 'file') {
16715 this.setValue((this.getValue() + '').replace(/^\s+/,''));
16718 * Resets the current field value to the originally-loaded value and clears any validation messages.
16721 reset : function(){
16722 Roo.form.TextField.superclass.reset.call(this);
16726 preFocus : function(){
16728 if(this.selectOnFocus){
16729 this.el.dom.select();
16735 filterKeys : function(e){
16736 var k = e.getKey();
16737 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16740 var c = e.getCharCode(), cc = String.fromCharCode(c);
16741 if(Roo.isIE && (e.isSpecialKey() || !cc)){
16744 if(!this.maskRe.test(cc)){
16749 setValue : function(v){
16751 Roo.form.TextField.superclass.setValue.apply(this, arguments);
16757 * Validates a value according to the field's validation rules and marks the field as invalid
16758 * if the validation fails
16759 * @param {Mixed} value The value to validate
16760 * @return {Boolean} True if the value is valid, else false
16762 validateValue : function(value){
16763 if(value.length < 1) { // if it's blank
16764 if(this.allowBlank){
16765 this.clearInvalid();
16768 this.markInvalid(this.blankText);
16772 if(value.length < this.minLength){
16773 this.markInvalid(String.format(this.minLengthText, this.minLength));
16776 if(value.length > this.maxLength){
16777 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16781 var vt = Roo.form.VTypes;
16782 if(!vt[this.vtype](value, this)){
16783 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16787 if(typeof this.validator == "function"){
16788 var msg = this.validator(value);
16790 this.markInvalid(msg);
16794 if(this.regex && !this.regex.test(value)){
16795 this.markInvalid(this.regexText);
16802 * Selects text in this field
16803 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16804 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16806 selectText : function(start, end){
16807 var v = this.getRawValue();
16809 start = start === undefined ? 0 : start;
16810 end = end === undefined ? v.length : end;
16811 var d = this.el.dom;
16812 if(d.setSelectionRange){
16813 d.setSelectionRange(start, end);
16814 }else if(d.createTextRange){
16815 var range = d.createTextRange();
16816 range.moveStart("character", start);
16817 range.moveEnd("character", v.length-end);
16824 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16825 * This only takes effect if grow = true, and fires the autosize event.
16827 autoSize : function(){
16828 if(!this.grow || !this.rendered){
16832 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16835 var v = el.dom.value;
16836 var d = document.createElement('div');
16837 d.appendChild(document.createTextNode(v));
16841 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16842 this.el.setWidth(w);
16843 this.fireEvent("autosize", this, w);
16847 SafariOnKeyDown : function(event)
16849 // this is a workaround for a password hang bug on chrome/ webkit.
16851 var isSelectAll = false;
16853 if(this.el.dom.selectionEnd > 0){
16854 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16856 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16857 event.preventDefault();
16862 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16864 event.preventDefault();
16865 // this is very hacky as keydown always get's upper case.
16867 var cc = String.fromCharCode(event.getCharCode());
16870 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
16878 * Ext JS Library 1.1.1
16879 * Copyright(c) 2006-2007, Ext JS, LLC.
16881 * Originally Released Under LGPL - original licence link has changed is not relivant.
16884 * <script type="text/javascript">
16888 * @class Roo.form.Hidden
16889 * @extends Roo.form.TextField
16890 * Simple Hidden element used on forms
16892 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16895 * Creates a new Hidden form element.
16896 * @param {Object} config Configuration options
16901 // easy hidden field...
16902 Roo.form.Hidden = function(config){
16903 Roo.form.Hidden.superclass.constructor.call(this, config);
16906 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16908 inputType: 'hidden',
16911 labelSeparator: '',
16913 itemCls : 'x-form-item-display-none'
16921 * Ext JS Library 1.1.1
16922 * Copyright(c) 2006-2007, Ext JS, LLC.
16924 * Originally Released Under LGPL - original licence link has changed is not relivant.
16927 * <script type="text/javascript">
16931 * @class Roo.form.TriggerField
16932 * @extends Roo.form.TextField
16933 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16934 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16935 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16936 * for which you can provide a custom implementation. For example:
16938 var trigger = new Roo.form.TriggerField();
16939 trigger.onTriggerClick = myTriggerFn;
16940 trigger.applyTo('my-field');
16943 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16944 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16945 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
16946 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16948 * Create a new TriggerField.
16949 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16950 * to the base TextField)
16952 Roo.form.TriggerField = function(config){
16953 this.mimicing = false;
16954 Roo.form.TriggerField.superclass.constructor.call(this, config);
16957 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
16959 * @cfg {String} triggerClass A CSS class to apply to the trigger
16962 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16963 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16965 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16967 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16971 /** @cfg {Boolean} grow @hide */
16972 /** @cfg {Number} growMin @hide */
16973 /** @cfg {Number} growMax @hide */
16979 autoSize: Roo.emptyFn,
16983 deferHeight : true,
16986 actionMode : 'wrap',
16988 onResize : function(w, h){
16989 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16990 if(typeof w == 'number'){
16991 var x = w - this.trigger.getWidth();
16992 this.el.setWidth(this.adjustWidth('input', x));
16993 this.trigger.setStyle('left', x+'px');
16998 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17001 getResizeEl : function(){
17006 getPositionEl : function(){
17011 alignErrorIcon : function(){
17012 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17016 onRender : function(ct, position){
17017 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17018 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17019 this.trigger = this.wrap.createChild(this.triggerConfig ||
17020 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17021 if(this.hideTrigger){
17022 this.trigger.setDisplayed(false);
17024 this.initTrigger();
17026 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17031 initTrigger : function(){
17032 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17033 this.trigger.addClassOnOver('x-form-trigger-over');
17034 this.trigger.addClassOnClick('x-form-trigger-click');
17038 onDestroy : function(){
17040 this.trigger.removeAllListeners();
17041 this.trigger.remove();
17044 this.wrap.remove();
17046 Roo.form.TriggerField.superclass.onDestroy.call(this);
17050 onFocus : function(){
17051 Roo.form.TriggerField.superclass.onFocus.call(this);
17052 if(!this.mimicing){
17053 this.wrap.addClass('x-trigger-wrap-focus');
17054 this.mimicing = true;
17055 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17056 if(this.monitorTab){
17057 this.el.on("keydown", this.checkTab, this);
17063 checkTab : function(e){
17064 if(e.getKey() == e.TAB){
17065 this.triggerBlur();
17070 onBlur : function(){
17075 mimicBlur : function(e, t){
17076 if(!this.wrap.contains(t) && this.validateBlur()){
17077 this.triggerBlur();
17082 triggerBlur : function(){
17083 this.mimicing = false;
17084 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17085 if(this.monitorTab){
17086 this.el.un("keydown", this.checkTab, this);
17088 this.wrap.removeClass('x-trigger-wrap-focus');
17089 Roo.form.TriggerField.superclass.onBlur.call(this);
17093 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17094 validateBlur : function(e, t){
17099 onDisable : function(){
17100 Roo.form.TriggerField.superclass.onDisable.call(this);
17102 this.wrap.addClass('x-item-disabled');
17107 onEnable : function(){
17108 Roo.form.TriggerField.superclass.onEnable.call(this);
17110 this.wrap.removeClass('x-item-disabled');
17115 onShow : function(){
17116 var ae = this.getActionEl();
17119 ae.dom.style.display = '';
17120 ae.dom.style.visibility = 'visible';
17126 onHide : function(){
17127 var ae = this.getActionEl();
17128 ae.dom.style.display = 'none';
17132 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17133 * by an implementing function.
17135 * @param {EventObject} e
17137 onTriggerClick : Roo.emptyFn
17140 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17141 // to be extended by an implementing class. For an example of implementing this class, see the custom
17142 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17143 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17144 initComponent : function(){
17145 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17147 this.triggerConfig = {
17148 tag:'span', cls:'x-form-twin-triggers', cn:[
17149 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17150 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17154 getTrigger : function(index){
17155 return this.triggers[index];
17158 initTrigger : function(){
17159 var ts = this.trigger.select('.x-form-trigger', true);
17160 this.wrap.setStyle('overflow', 'hidden');
17161 var triggerField = this;
17162 ts.each(function(t, all, index){
17163 t.hide = function(){
17164 var w = triggerField.wrap.getWidth();
17165 this.dom.style.display = 'none';
17166 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17168 t.show = function(){
17169 var w = triggerField.wrap.getWidth();
17170 this.dom.style.display = '';
17171 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17173 var triggerIndex = 'Trigger'+(index+1);
17175 if(this['hide'+triggerIndex]){
17176 t.dom.style.display = 'none';
17178 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17179 t.addClassOnOver('x-form-trigger-over');
17180 t.addClassOnClick('x-form-trigger-click');
17182 this.triggers = ts.elements;
17185 onTrigger1Click : Roo.emptyFn,
17186 onTrigger2Click : Roo.emptyFn
17189 * Ext JS Library 1.1.1
17190 * Copyright(c) 2006-2007, Ext JS, LLC.
17192 * Originally Released Under LGPL - original licence link has changed is not relivant.
17195 * <script type="text/javascript">
17199 * @class Roo.form.TextArea
17200 * @extends Roo.form.TextField
17201 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17202 * support for auto-sizing.
17204 * Creates a new TextArea
17205 * @param {Object} config Configuration options
17207 Roo.form.TextArea = function(config){
17208 Roo.form.TextArea.superclass.constructor.call(this, config);
17209 // these are provided exchanges for backwards compat
17210 // minHeight/maxHeight were replaced by growMin/growMax to be
17211 // compatible with TextField growing config values
17212 if(this.minHeight !== undefined){
17213 this.growMin = this.minHeight;
17215 if(this.maxHeight !== undefined){
17216 this.growMax = this.maxHeight;
17220 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17222 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17226 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17230 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17231 * in the field (equivalent to setting overflow: hidden, defaults to false)
17233 preventScrollbars: false,
17235 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17236 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17240 onRender : function(ct, position){
17242 this.defaultAutoCreate = {
17244 style:"width:300px;height:60px;",
17245 autocomplete: "new-password"
17248 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17250 this.textSizeEl = Roo.DomHelper.append(document.body, {
17251 tag: "pre", cls: "x-form-grow-sizer"
17253 if(this.preventScrollbars){
17254 this.el.setStyle("overflow", "hidden");
17256 this.el.setHeight(this.growMin);
17260 onDestroy : function(){
17261 if(this.textSizeEl){
17262 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17264 Roo.form.TextArea.superclass.onDestroy.call(this);
17268 onKeyUp : function(e){
17269 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17275 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17276 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17278 autoSize : function(){
17279 if(!this.grow || !this.textSizeEl){
17283 var v = el.dom.value;
17284 var ts = this.textSizeEl;
17287 ts.appendChild(document.createTextNode(v));
17290 Roo.fly(ts).setWidth(this.el.getWidth());
17292 v = "  ";
17295 v = v.replace(/\n/g, '<p> </p>');
17297 v += " \n ";
17300 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17301 if(h != this.lastHeight){
17302 this.lastHeight = h;
17303 this.el.setHeight(h);
17304 this.fireEvent("autosize", this, h);
17309 * Ext JS Library 1.1.1
17310 * Copyright(c) 2006-2007, Ext JS, LLC.
17312 * Originally Released Under LGPL - original licence link has changed is not relivant.
17315 * <script type="text/javascript">
17320 * @class Roo.form.NumberField
17321 * @extends Roo.form.TextField
17322 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17324 * Creates a new NumberField
17325 * @param {Object} config Configuration options
17327 Roo.form.NumberField = function(config){
17328 Roo.form.NumberField.superclass.constructor.call(this, config);
17331 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17333 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17335 fieldClass: "x-form-field x-form-num-field",
17337 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17339 allowDecimals : true,
17341 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17343 decimalSeparator : ".",
17345 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17347 decimalPrecision : 2,
17349 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17351 allowNegative : true,
17353 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17355 minValue : Number.NEGATIVE_INFINITY,
17357 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17359 maxValue : Number.MAX_VALUE,
17361 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17363 minText : "The minimum value for this field is {0}",
17365 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17367 maxText : "The maximum value for this field is {0}",
17369 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17370 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17372 nanText : "{0} is not a valid number",
17375 initEvents : function(){
17376 Roo.form.NumberField.superclass.initEvents.call(this);
17377 var allowed = "0123456789";
17378 if(this.allowDecimals){
17379 allowed += this.decimalSeparator;
17381 if(this.allowNegative){
17384 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17385 var keyPress = function(e){
17386 var k = e.getKey();
17387 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17390 var c = e.getCharCode();
17391 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17395 this.el.on("keypress", keyPress, this);
17399 validateValue : function(value){
17400 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17403 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17406 var num = this.parseValue(value);
17408 this.markInvalid(String.format(this.nanText, value));
17411 if(num < this.minValue){
17412 this.markInvalid(String.format(this.minText, this.minValue));
17415 if(num > this.maxValue){
17416 this.markInvalid(String.format(this.maxText, this.maxValue));
17422 getValue : function(){
17423 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17427 parseValue : function(value){
17428 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17429 return isNaN(value) ? '' : value;
17433 fixPrecision : function(value){
17434 var nan = isNaN(value);
17435 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17436 return nan ? '' : value;
17438 return parseFloat(value).toFixed(this.decimalPrecision);
17441 setValue : function(v){
17442 v = this.fixPrecision(v);
17443 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17447 decimalPrecisionFcn : function(v){
17448 return Math.floor(v);
17451 beforeBlur : function(){
17452 var v = this.parseValue(this.getRawValue());
17459 * Ext JS Library 1.1.1
17460 * Copyright(c) 2006-2007, Ext JS, LLC.
17462 * Originally Released Under LGPL - original licence link has changed is not relivant.
17465 * <script type="text/javascript">
17469 * @class Roo.form.DateField
17470 * @extends Roo.form.TriggerField
17471 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17473 * Create a new DateField
17474 * @param {Object} config
17476 Roo.form.DateField = function(config)
17478 Roo.form.DateField.superclass.constructor.call(this, config);
17484 * Fires when a date is selected
17485 * @param {Roo.form.DateField} combo This combo box
17486 * @param {Date} date The date selected
17493 if(typeof this.minValue == "string") {
17494 this.minValue = this.parseDate(this.minValue);
17496 if(typeof this.maxValue == "string") {
17497 this.maxValue = this.parseDate(this.maxValue);
17499 this.ddMatch = null;
17500 if(this.disabledDates){
17501 var dd = this.disabledDates;
17503 for(var i = 0; i < dd.length; i++){
17505 if(i != dd.length-1) {
17509 this.ddMatch = new RegExp(re + ")");
17513 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17515 * @cfg {String} format
17516 * The default date format string which can be overriden for localization support. The format must be
17517 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17521 * @cfg {String} altFormats
17522 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17523 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17525 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17527 * @cfg {Array} disabledDays
17528 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17530 disabledDays : null,
17532 * @cfg {String} disabledDaysText
17533 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17535 disabledDaysText : "Disabled",
17537 * @cfg {Array} disabledDates
17538 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17539 * expression so they are very powerful. Some examples:
17541 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17542 * <li>["03/08", "09/16"] would disable those days for every year</li>
17543 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17544 * <li>["03/../2006"] would disable every day in March 2006</li>
17545 * <li>["^03"] would disable every day in every March</li>
17547 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17548 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17550 disabledDates : null,
17552 * @cfg {String} disabledDatesText
17553 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17555 disabledDatesText : "Disabled",
17557 * @cfg {Date/String} minValue
17558 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17559 * valid format (defaults to null).
17563 * @cfg {Date/String} maxValue
17564 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17565 * valid format (defaults to null).
17569 * @cfg {String} minText
17570 * The error text to display when the date in the cell is before minValue (defaults to
17571 * 'The date in this field must be after {minValue}').
17573 minText : "The date in this field must be equal to or after {0}",
17575 * @cfg {String} maxText
17576 * The error text to display when the date in the cell is after maxValue (defaults to
17577 * 'The date in this field must be before {maxValue}').
17579 maxText : "The date in this field must be equal to or before {0}",
17581 * @cfg {String} invalidText
17582 * The error text to display when the date in the field is invalid (defaults to
17583 * '{value} is not a valid date - it must be in the format {format}').
17585 invalidText : "{0} is not a valid date - it must be in the format {1}",
17587 * @cfg {String} triggerClass
17588 * An additional CSS class used to style the trigger button. The trigger will always get the
17589 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17590 * which displays a calendar icon).
17592 triggerClass : 'x-form-date-trigger',
17596 * @cfg {Boolean} useIso
17597 * if enabled, then the date field will use a hidden field to store the
17598 * real value as iso formated date. default (false)
17602 * @cfg {String/Object} autoCreate
17603 * A DomHelper element spec, or true for a default element spec (defaults to
17604 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17607 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17610 hiddenField: false,
17612 onRender : function(ct, position)
17614 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17616 //this.el.dom.removeAttribute('name');
17617 Roo.log("Changing name?");
17618 this.el.dom.setAttribute('name', this.name + '____hidden___' );
17619 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17621 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17622 // prevent input submission
17623 this.hiddenName = this.name;
17630 validateValue : function(value)
17632 value = this.formatDate(value);
17633 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17634 Roo.log('super failed');
17637 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17640 var svalue = value;
17641 value = this.parseDate(value);
17643 Roo.log('parse date failed' + svalue);
17644 this.markInvalid(String.format(this.invalidText, svalue, this.format));
17647 var time = value.getTime();
17648 if(this.minValue && time < this.minValue.getTime()){
17649 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17652 if(this.maxValue && time > this.maxValue.getTime()){
17653 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17656 if(this.disabledDays){
17657 var day = value.getDay();
17658 for(var i = 0; i < this.disabledDays.length; i++) {
17659 if(day === this.disabledDays[i]){
17660 this.markInvalid(this.disabledDaysText);
17665 var fvalue = this.formatDate(value);
17666 if(this.ddMatch && this.ddMatch.test(fvalue)){
17667 this.markInvalid(String.format(this.disabledDatesText, fvalue));
17674 // Provides logic to override the default TriggerField.validateBlur which just returns true
17675 validateBlur : function(){
17676 return !this.menu || !this.menu.isVisible();
17679 getName: function()
17681 // returns hidden if it's set..
17682 if (!this.rendered) {return ''};
17683 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
17688 * Returns the current date value of the date field.
17689 * @return {Date} The date value
17691 getValue : function(){
17693 return this.hiddenField ?
17694 this.hiddenField.value :
17695 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17699 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
17700 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17701 * (the default format used is "m/d/y").
17704 //All of these calls set the same date value (May 4, 2006)
17706 //Pass a date object:
17707 var dt = new Date('5/4/06');
17708 dateField.setValue(dt);
17710 //Pass a date string (default format):
17711 dateField.setValue('5/4/06');
17713 //Pass a date string (custom format):
17714 dateField.format = 'Y-m-d';
17715 dateField.setValue('2006-5-4');
17717 * @param {String/Date} date The date or valid date string
17719 setValue : function(date){
17720 if (this.hiddenField) {
17721 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17723 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17724 // make sure the value field is always stored as a date..
17725 this.value = this.parseDate(date);
17731 parseDate : function(value){
17732 if(!value || value instanceof Date){
17735 var v = Date.parseDate(value, this.format);
17736 if (!v && this.useIso) {
17737 v = Date.parseDate(value, 'Y-m-d');
17739 if(!v && this.altFormats){
17740 if(!this.altFormatsArray){
17741 this.altFormatsArray = this.altFormats.split("|");
17743 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17744 v = Date.parseDate(value, this.altFormatsArray[i]);
17751 formatDate : function(date, fmt){
17752 return (!date || !(date instanceof Date)) ?
17753 date : date.dateFormat(fmt || this.format);
17758 select: function(m, d){
17761 this.fireEvent('select', this, d);
17763 show : function(){ // retain focus styling
17767 this.focus.defer(10, this);
17768 var ml = this.menuListeners;
17769 this.menu.un("select", ml.select, this);
17770 this.menu.un("show", ml.show, this);
17771 this.menu.un("hide", ml.hide, this);
17776 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17777 onTriggerClick : function(){
17781 if(this.menu == null){
17782 this.menu = new Roo.menu.DateMenu();
17784 Roo.apply(this.menu.picker, {
17785 showClear: this.allowBlank,
17786 minDate : this.minValue,
17787 maxDate : this.maxValue,
17788 disabledDatesRE : this.ddMatch,
17789 disabledDatesText : this.disabledDatesText,
17790 disabledDays : this.disabledDays,
17791 disabledDaysText : this.disabledDaysText,
17792 format : this.useIso ? 'Y-m-d' : this.format,
17793 minText : String.format(this.minText, this.formatDate(this.minValue)),
17794 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17796 this.menu.on(Roo.apply({}, this.menuListeners, {
17799 this.menu.picker.setValue(this.getValue() || new Date());
17800 this.menu.show(this.el, "tl-bl?");
17803 beforeBlur : function(){
17804 var v = this.parseDate(this.getRawValue());
17814 isDirty : function() {
17815 if(this.disabled) {
17819 if(typeof(this.startValue) === 'undefined'){
17823 return String(this.getValue()) !== String(this.startValue);
17827 cleanLeadingSpace : function(e)
17834 * Ext JS Library 1.1.1
17835 * Copyright(c) 2006-2007, Ext JS, LLC.
17837 * Originally Released Under LGPL - original licence link has changed is not relivant.
17840 * <script type="text/javascript">
17844 * @class Roo.form.MonthField
17845 * @extends Roo.form.TriggerField
17846 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17848 * Create a new MonthField
17849 * @param {Object} config
17851 Roo.form.MonthField = function(config){
17853 Roo.form.MonthField.superclass.constructor.call(this, config);
17859 * Fires when a date is selected
17860 * @param {Roo.form.MonthFieeld} combo This combo box
17861 * @param {Date} date The date selected
17868 if(typeof this.minValue == "string") {
17869 this.minValue = this.parseDate(this.minValue);
17871 if(typeof this.maxValue == "string") {
17872 this.maxValue = this.parseDate(this.maxValue);
17874 this.ddMatch = null;
17875 if(this.disabledDates){
17876 var dd = this.disabledDates;
17878 for(var i = 0; i < dd.length; i++){
17880 if(i != dd.length-1) {
17884 this.ddMatch = new RegExp(re + ")");
17888 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
17890 * @cfg {String} format
17891 * The default date format string which can be overriden for localization support. The format must be
17892 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17896 * @cfg {String} altFormats
17897 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17898 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17900 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17902 * @cfg {Array} disabledDays
17903 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17905 disabledDays : [0,1,2,3,4,5,6],
17907 * @cfg {String} disabledDaysText
17908 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17910 disabledDaysText : "Disabled",
17912 * @cfg {Array} disabledDates
17913 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17914 * expression so they are very powerful. Some examples:
17916 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17917 * <li>["03/08", "09/16"] would disable those days for every year</li>
17918 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17919 * <li>["03/../2006"] would disable every day in March 2006</li>
17920 * <li>["^03"] would disable every day in every March</li>
17922 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17923 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17925 disabledDates : null,
17927 * @cfg {String} disabledDatesText
17928 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17930 disabledDatesText : "Disabled",
17932 * @cfg {Date/String} minValue
17933 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17934 * valid format (defaults to null).
17938 * @cfg {Date/String} maxValue
17939 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17940 * valid format (defaults to null).
17944 * @cfg {String} minText
17945 * The error text to display when the date in the cell is before minValue (defaults to
17946 * 'The date in this field must be after {minValue}').
17948 minText : "The date in this field must be equal to or after {0}",
17950 * @cfg {String} maxTextf
17951 * The error text to display when the date in the cell is after maxValue (defaults to
17952 * 'The date in this field must be before {maxValue}').
17954 maxText : "The date in this field must be equal to or before {0}",
17956 * @cfg {String} invalidText
17957 * The error text to display when the date in the field is invalid (defaults to
17958 * '{value} is not a valid date - it must be in the format {format}').
17960 invalidText : "{0} is not a valid date - it must be in the format {1}",
17962 * @cfg {String} triggerClass
17963 * An additional CSS class used to style the trigger button. The trigger will always get the
17964 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17965 * which displays a calendar icon).
17967 triggerClass : 'x-form-date-trigger',
17971 * @cfg {Boolean} useIso
17972 * if enabled, then the date field will use a hidden field to store the
17973 * real value as iso formated date. default (true)
17977 * @cfg {String/Object} autoCreate
17978 * A DomHelper element spec, or true for a default element spec (defaults to
17979 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17982 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17985 hiddenField: false,
17987 hideMonthPicker : false,
17989 onRender : function(ct, position)
17991 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17993 this.el.dom.removeAttribute('name');
17994 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17996 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17997 // prevent input submission
17998 this.hiddenName = this.name;
18005 validateValue : function(value)
18007 value = this.formatDate(value);
18008 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18011 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18014 var svalue = value;
18015 value = this.parseDate(value);
18017 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18020 var time = value.getTime();
18021 if(this.minValue && time < this.minValue.getTime()){
18022 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18025 if(this.maxValue && time > this.maxValue.getTime()){
18026 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18029 /*if(this.disabledDays){
18030 var day = value.getDay();
18031 for(var i = 0; i < this.disabledDays.length; i++) {
18032 if(day === this.disabledDays[i]){
18033 this.markInvalid(this.disabledDaysText);
18039 var fvalue = this.formatDate(value);
18040 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18041 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18049 // Provides logic to override the default TriggerField.validateBlur which just returns true
18050 validateBlur : function(){
18051 return !this.menu || !this.menu.isVisible();
18055 * Returns the current date value of the date field.
18056 * @return {Date} The date value
18058 getValue : function(){
18062 return this.hiddenField ?
18063 this.hiddenField.value :
18064 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18068 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18069 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18070 * (the default format used is "m/d/y").
18073 //All of these calls set the same date value (May 4, 2006)
18075 //Pass a date object:
18076 var dt = new Date('5/4/06');
18077 monthField.setValue(dt);
18079 //Pass a date string (default format):
18080 monthField.setValue('5/4/06');
18082 //Pass a date string (custom format):
18083 monthField.format = 'Y-m-d';
18084 monthField.setValue('2006-5-4');
18086 * @param {String/Date} date The date or valid date string
18088 setValue : function(date){
18089 Roo.log('month setValue' + date);
18090 // can only be first of month..
18092 var val = this.parseDate(date);
18094 if (this.hiddenField) {
18095 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18097 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18098 this.value = this.parseDate(date);
18102 parseDate : function(value){
18103 if(!value || value instanceof Date){
18104 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18107 var v = Date.parseDate(value, this.format);
18108 if (!v && this.useIso) {
18109 v = Date.parseDate(value, 'Y-m-d');
18113 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18117 if(!v && this.altFormats){
18118 if(!this.altFormatsArray){
18119 this.altFormatsArray = this.altFormats.split("|");
18121 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18122 v = Date.parseDate(value, this.altFormatsArray[i]);
18129 formatDate : function(date, fmt){
18130 return (!date || !(date instanceof Date)) ?
18131 date : date.dateFormat(fmt || this.format);
18136 select: function(m, d){
18138 this.fireEvent('select', this, d);
18140 show : function(){ // retain focus styling
18144 this.focus.defer(10, this);
18145 var ml = this.menuListeners;
18146 this.menu.un("select", ml.select, this);
18147 this.menu.un("show", ml.show, this);
18148 this.menu.un("hide", ml.hide, this);
18152 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18153 onTriggerClick : function(){
18157 if(this.menu == null){
18158 this.menu = new Roo.menu.DateMenu();
18162 Roo.apply(this.menu.picker, {
18164 showClear: this.allowBlank,
18165 minDate : this.minValue,
18166 maxDate : this.maxValue,
18167 disabledDatesRE : this.ddMatch,
18168 disabledDatesText : this.disabledDatesText,
18170 format : this.useIso ? 'Y-m-d' : this.format,
18171 minText : String.format(this.minText, this.formatDate(this.minValue)),
18172 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18175 this.menu.on(Roo.apply({}, this.menuListeners, {
18183 // hide month picker get's called when we called by 'before hide';
18185 var ignorehide = true;
18186 p.hideMonthPicker = function(disableAnim){
18190 if(this.monthPicker){
18191 Roo.log("hideMonthPicker called");
18192 if(disableAnim === true){
18193 this.monthPicker.hide();
18195 this.monthPicker.slideOut('t', {duration:.2});
18196 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18197 p.fireEvent("select", this, this.value);
18203 Roo.log('picker set value');
18204 Roo.log(this.getValue());
18205 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18206 m.show(this.el, 'tl-bl?');
18207 ignorehide = false;
18208 // this will trigger hideMonthPicker..
18211 // hidden the day picker
18212 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18218 p.showMonthPicker.defer(100, p);
18224 beforeBlur : function(){
18225 var v = this.parseDate(this.getRawValue());
18231 /** @cfg {Boolean} grow @hide */
18232 /** @cfg {Number} growMin @hide */
18233 /** @cfg {Number} growMax @hide */
18240 * Ext JS Library 1.1.1
18241 * Copyright(c) 2006-2007, Ext JS, LLC.
18243 * Originally Released Under LGPL - original licence link has changed is not relivant.
18246 * <script type="text/javascript">
18251 * @class Roo.form.ComboBox
18252 * @extends Roo.form.TriggerField
18253 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18255 * Create a new ComboBox.
18256 * @param {Object} config Configuration options
18258 Roo.form.ComboBox = function(config){
18259 Roo.form.ComboBox.superclass.constructor.call(this, config);
18263 * Fires when the dropdown list is expanded
18264 * @param {Roo.form.ComboBox} combo This combo box
18269 * Fires when the dropdown list is collapsed
18270 * @param {Roo.form.ComboBox} combo This combo box
18274 * @event beforeselect
18275 * Fires before a list item is selected. Return false to cancel the selection.
18276 * @param {Roo.form.ComboBox} combo This combo box
18277 * @param {Roo.data.Record} record The data record returned from the underlying store
18278 * @param {Number} index The index of the selected item in the dropdown list
18280 'beforeselect' : true,
18283 * Fires when a list item is selected
18284 * @param {Roo.form.ComboBox} combo This combo box
18285 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18286 * @param {Number} index The index of the selected item in the dropdown list
18290 * @event beforequery
18291 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18292 * The event object passed has these properties:
18293 * @param {Roo.form.ComboBox} combo This combo box
18294 * @param {String} query The query
18295 * @param {Boolean} forceAll true to force "all" query
18296 * @param {Boolean} cancel true to cancel the query
18297 * @param {Object} e The query event object
18299 'beforequery': true,
18302 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18303 * @param {Roo.form.ComboBox} combo This combo box
18308 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18309 * @param {Roo.form.ComboBox} combo This combo box
18310 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18316 if(this.transform){
18317 this.allowDomMove = false;
18318 var s = Roo.getDom(this.transform);
18319 if(!this.hiddenName){
18320 this.hiddenName = s.name;
18323 this.mode = 'local';
18324 var d = [], opts = s.options;
18325 for(var i = 0, len = opts.length;i < len; i++){
18327 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18329 this.value = value;
18331 d.push([value, o.text]);
18333 this.store = new Roo.data.SimpleStore({
18335 fields: ['value', 'text'],
18338 this.valueField = 'value';
18339 this.displayField = 'text';
18341 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18342 if(!this.lazyRender){
18343 this.target = true;
18344 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18345 s.parentNode.removeChild(s); // remove it
18346 this.render(this.el.parentNode);
18348 s.parentNode.removeChild(s); // remove it
18353 this.store = Roo.factory(this.store, Roo.data);
18356 this.selectedIndex = -1;
18357 if(this.mode == 'local'){
18358 if(config.queryDelay === undefined){
18359 this.queryDelay = 10;
18361 if(config.minChars === undefined){
18367 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18369 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18372 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18373 * rendering into an Roo.Editor, defaults to false)
18376 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18377 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18380 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18383 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18384 * the dropdown list (defaults to undefined, with no header element)
18388 * @cfg {String/Roo.Template} tpl The template to use to render the output
18392 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18394 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18396 listWidth: undefined,
18398 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18399 * mode = 'remote' or 'text' if mode = 'local')
18401 displayField: undefined,
18403 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18404 * mode = 'remote' or 'value' if mode = 'local').
18405 * Note: use of a valueField requires the user make a selection
18406 * in order for a value to be mapped.
18408 valueField: undefined,
18412 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18413 * field's data value (defaults to the underlying DOM element's name)
18415 hiddenName: undefined,
18417 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18421 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18423 selectedClass: 'x-combo-selected',
18425 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18426 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18427 * which displays a downward arrow icon).
18429 triggerClass : 'x-form-arrow-trigger',
18431 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18435 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18436 * anchor positions (defaults to 'tl-bl')
18438 listAlign: 'tl-bl?',
18440 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18444 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18445 * query specified by the allQuery config option (defaults to 'query')
18447 triggerAction: 'query',
18449 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18450 * (defaults to 4, does not apply if editable = false)
18454 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18455 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18459 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18460 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18464 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18465 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18469 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18470 * when editable = true (defaults to false)
18472 selectOnFocus:false,
18474 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18476 queryParam: 'query',
18478 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18479 * when mode = 'remote' (defaults to 'Loading...')
18481 loadingText: 'Loading...',
18483 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18487 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18491 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18492 * traditional select (defaults to true)
18496 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18500 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18504 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18505 * listWidth has a higher value)
18509 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18510 * allow the user to set arbitrary text into the field (defaults to false)
18512 forceSelection:false,
18514 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18515 * if typeAhead = true (defaults to 250)
18517 typeAheadDelay : 250,
18519 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18520 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18522 valueNotFoundText : undefined,
18524 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18526 blockFocus : false,
18529 * @cfg {Boolean} disableClear Disable showing of clear button.
18531 disableClear : false,
18533 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18535 alwaysQuery : false,
18541 // element that contains real text value.. (when hidden is used..)
18544 onRender : function(ct, position)
18546 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18548 if(this.hiddenName){
18549 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18551 this.hiddenField.value =
18552 this.hiddenValue !== undefined ? this.hiddenValue :
18553 this.value !== undefined ? this.value : '';
18555 // prevent input submission
18556 this.el.dom.removeAttribute('name');
18562 this.el.dom.setAttribute('autocomplete', 'off');
18565 var cls = 'x-combo-list';
18567 this.list = new Roo.Layer({
18568 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18571 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18572 this.list.setWidth(lw);
18573 this.list.swallowEvent('mousewheel');
18574 this.assetHeight = 0;
18577 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18578 this.assetHeight += this.header.getHeight();
18581 this.innerList = this.list.createChild({cls:cls+'-inner'});
18582 this.innerList.on('mouseover', this.onViewOver, this);
18583 this.innerList.on('mousemove', this.onViewMove, this);
18584 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18586 if(this.allowBlank && !this.pageSize && !this.disableClear){
18587 this.footer = this.list.createChild({cls:cls+'-ft'});
18588 this.pageTb = new Roo.Toolbar(this.footer);
18592 this.footer = this.list.createChild({cls:cls+'-ft'});
18593 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18594 {pageSize: this.pageSize});
18598 if (this.pageTb && this.allowBlank && !this.disableClear) {
18600 this.pageTb.add(new Roo.Toolbar.Fill(), {
18601 cls: 'x-btn-icon x-btn-clear',
18603 handler: function()
18606 _this.clearValue();
18607 _this.onSelect(false, -1);
18612 this.assetHeight += this.footer.getHeight();
18617 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18620 this.view = new Roo.View(this.innerList, this.tpl, {
18623 selectedClass: this.selectedClass
18626 this.view.on('click', this.onViewClick, this);
18628 this.store.on('beforeload', this.onBeforeLoad, this);
18629 this.store.on('load', this.onLoad, this);
18630 this.store.on('loadexception', this.onLoadException, this);
18632 if(this.resizable){
18633 this.resizer = new Roo.Resizable(this.list, {
18634 pinned:true, handles:'se'
18636 this.resizer.on('resize', function(r, w, h){
18637 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18638 this.listWidth = w;
18639 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18640 this.restrictHeight();
18642 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18644 if(!this.editable){
18645 this.editable = true;
18646 this.setEditable(false);
18650 if (typeof(this.events.add.listeners) != 'undefined') {
18652 this.addicon = this.wrap.createChild(
18653 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
18655 this.addicon.on('click', function(e) {
18656 this.fireEvent('add', this);
18659 if (typeof(this.events.edit.listeners) != 'undefined') {
18661 this.editicon = this.wrap.createChild(
18662 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
18663 if (this.addicon) {
18664 this.editicon.setStyle('margin-left', '40px');
18666 this.editicon.on('click', function(e) {
18668 // we fire even if inothing is selected..
18669 this.fireEvent('edit', this, this.lastData );
18679 initEvents : function(){
18680 Roo.form.ComboBox.superclass.initEvents.call(this);
18682 this.keyNav = new Roo.KeyNav(this.el, {
18683 "up" : function(e){
18684 this.inKeyMode = true;
18688 "down" : function(e){
18689 if(!this.isExpanded()){
18690 this.onTriggerClick();
18692 this.inKeyMode = true;
18697 "enter" : function(e){
18698 this.onViewClick();
18702 "esc" : function(e){
18706 "tab" : function(e){
18707 this.onViewClick(false);
18708 this.fireEvent("specialkey", this, e);
18714 doRelay : function(foo, bar, hname){
18715 if(hname == 'down' || this.scope.isExpanded()){
18716 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18723 this.queryDelay = Math.max(this.queryDelay || 10,
18724 this.mode == 'local' ? 10 : 250);
18725 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18726 if(this.typeAhead){
18727 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18729 if(this.editable !== false){
18730 this.el.on("keyup", this.onKeyUp, this);
18732 if(this.forceSelection){
18733 this.on('blur', this.doForce, this);
18737 onDestroy : function(){
18739 this.view.setStore(null);
18740 this.view.el.removeAllListeners();
18741 this.view.el.remove();
18742 this.view.purgeListeners();
18745 this.list.destroy();
18748 this.store.un('beforeload', this.onBeforeLoad, this);
18749 this.store.un('load', this.onLoad, this);
18750 this.store.un('loadexception', this.onLoadException, this);
18752 Roo.form.ComboBox.superclass.onDestroy.call(this);
18756 fireKey : function(e){
18757 if(e.isNavKeyPress() && !this.list.isVisible()){
18758 this.fireEvent("specialkey", this, e);
18763 onResize: function(w, h){
18764 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18766 if(typeof w != 'number'){
18767 // we do not handle it!?!?
18770 var tw = this.trigger.getWidth();
18771 tw += this.addicon ? this.addicon.getWidth() : 0;
18772 tw += this.editicon ? this.editicon.getWidth() : 0;
18774 this.el.setWidth( this.adjustWidth('input', x));
18776 this.trigger.setStyle('left', x+'px');
18778 if(this.list && this.listWidth === undefined){
18779 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18780 this.list.setWidth(lw);
18781 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18789 * Allow or prevent the user from directly editing the field text. If false is passed,
18790 * the user will only be able to select from the items defined in the dropdown list. This method
18791 * is the runtime equivalent of setting the 'editable' config option at config time.
18792 * @param {Boolean} value True to allow the user to directly edit the field text
18794 setEditable : function(value){
18795 if(value == this.editable){
18798 this.editable = value;
18800 this.el.dom.setAttribute('readOnly', true);
18801 this.el.on('mousedown', this.onTriggerClick, this);
18802 this.el.addClass('x-combo-noedit');
18804 this.el.dom.setAttribute('readOnly', false);
18805 this.el.un('mousedown', this.onTriggerClick, this);
18806 this.el.removeClass('x-combo-noedit');
18811 onBeforeLoad : function(){
18812 if(!this.hasFocus){
18815 this.innerList.update(this.loadingText ?
18816 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18817 this.restrictHeight();
18818 this.selectedIndex = -1;
18822 onLoad : function(){
18823 if(!this.hasFocus){
18826 if(this.store.getCount() > 0){
18828 this.restrictHeight();
18829 if(this.lastQuery == this.allQuery){
18831 this.el.dom.select();
18833 if(!this.selectByValue(this.value, true)){
18834 this.select(0, true);
18838 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18839 this.taTask.delay(this.typeAheadDelay);
18843 this.onEmptyResults();
18848 onLoadException : function()
18851 Roo.log(this.store.reader.jsonData);
18852 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18853 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18859 onTypeAhead : function(){
18860 if(this.store.getCount() > 0){
18861 var r = this.store.getAt(0);
18862 var newValue = r.data[this.displayField];
18863 var len = newValue.length;
18864 var selStart = this.getRawValue().length;
18865 if(selStart != len){
18866 this.setRawValue(newValue);
18867 this.selectText(selStart, newValue.length);
18873 onSelect : function(record, index){
18874 if(this.fireEvent('beforeselect', this, record, index) !== false){
18875 this.setFromData(index > -1 ? record.data : false);
18877 this.fireEvent('select', this, record, index);
18882 * Returns the currently selected field value or empty string if no value is set.
18883 * @return {String} value The selected value
18885 getValue : function(){
18886 if(this.valueField){
18887 return typeof this.value != 'undefined' ? this.value : '';
18889 return Roo.form.ComboBox.superclass.getValue.call(this);
18893 * Clears any text/value currently set in the field
18895 clearValue : function(){
18896 if(this.hiddenField){
18897 this.hiddenField.value = '';
18900 this.setRawValue('');
18901 this.lastSelectionText = '';
18906 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18907 * will be displayed in the field. If the value does not match the data value of an existing item,
18908 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18909 * Otherwise the field will be blank (although the value will still be set).
18910 * @param {String} value The value to match
18912 setValue : function(v){
18914 if(this.valueField){
18915 var r = this.findRecord(this.valueField, v);
18917 text = r.data[this.displayField];
18918 }else if(this.valueNotFoundText !== undefined){
18919 text = this.valueNotFoundText;
18922 this.lastSelectionText = text;
18923 if(this.hiddenField){
18924 this.hiddenField.value = v;
18926 Roo.form.ComboBox.superclass.setValue.call(this, text);
18930 * @property {Object} the last set data for the element
18935 * Sets the value of the field based on a object which is related to the record format for the store.
18936 * @param {Object} value the value to set as. or false on reset?
18938 setFromData : function(o){
18939 var dv = ''; // display value
18940 var vv = ''; // value value..
18942 if (this.displayField) {
18943 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18945 // this is an error condition!!!
18946 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18949 if(this.valueField){
18950 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18952 if(this.hiddenField){
18953 this.hiddenField.value = vv;
18955 this.lastSelectionText = dv;
18956 Roo.form.ComboBox.superclass.setValue.call(this, dv);
18960 // no hidden field.. - we store the value in 'value', but still display
18961 // display field!!!!
18962 this.lastSelectionText = dv;
18963 Roo.form.ComboBox.superclass.setValue.call(this, dv);
18969 reset : function(){
18970 // overridden so that last data is reset..
18971 this.setValue(this.resetValue);
18972 this.originalValue = this.getValue();
18973 this.clearInvalid();
18974 this.lastData = false;
18976 this.view.clearSelections();
18980 findRecord : function(prop, value){
18982 if(this.store.getCount() > 0){
18983 this.store.each(function(r){
18984 if(r.data[prop] == value){
18994 getName: function()
18996 // returns hidden if it's set..
18997 if (!this.rendered) {return ''};
18998 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19002 onViewMove : function(e, t){
19003 this.inKeyMode = false;
19007 onViewOver : function(e, t){
19008 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19011 var item = this.view.findItemFromChild(t);
19013 var index = this.view.indexOf(item);
19014 this.select(index, false);
19019 onViewClick : function(doFocus)
19021 var index = this.view.getSelectedIndexes()[0];
19022 var r = this.store.getAt(index);
19024 this.onSelect(r, index);
19026 if(doFocus !== false && !this.blockFocus){
19032 restrictHeight : function(){
19033 this.innerList.dom.style.height = '';
19034 var inner = this.innerList.dom;
19035 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19036 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19037 this.list.beginUpdate();
19038 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19039 this.list.alignTo(this.el, this.listAlign);
19040 this.list.endUpdate();
19044 onEmptyResults : function(){
19049 * Returns true if the dropdown list is expanded, else false.
19051 isExpanded : function(){
19052 return this.list.isVisible();
19056 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19057 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19058 * @param {String} value The data value of the item to select
19059 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19060 * selected item if it is not currently in view (defaults to true)
19061 * @return {Boolean} True if the value matched an item in the list, else false
19063 selectByValue : function(v, scrollIntoView){
19064 if(v !== undefined && v !== null){
19065 var r = this.findRecord(this.valueField || this.displayField, v);
19067 this.select(this.store.indexOf(r), scrollIntoView);
19075 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19076 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19077 * @param {Number} index The zero-based index of the list item to select
19078 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19079 * selected item if it is not currently in view (defaults to true)
19081 select : function(index, scrollIntoView){
19082 this.selectedIndex = index;
19083 this.view.select(index);
19084 if(scrollIntoView !== false){
19085 var el = this.view.getNode(index);
19087 this.innerList.scrollChildIntoView(el, false);
19093 selectNext : function(){
19094 var ct = this.store.getCount();
19096 if(this.selectedIndex == -1){
19098 }else if(this.selectedIndex < ct-1){
19099 this.select(this.selectedIndex+1);
19105 selectPrev : function(){
19106 var ct = this.store.getCount();
19108 if(this.selectedIndex == -1){
19110 }else if(this.selectedIndex != 0){
19111 this.select(this.selectedIndex-1);
19117 onKeyUp : function(e){
19118 if(this.editable !== false && !e.isSpecialKey()){
19119 this.lastKey = e.getKey();
19120 this.dqTask.delay(this.queryDelay);
19125 validateBlur : function(){
19126 return !this.list || !this.list.isVisible();
19130 initQuery : function(){
19131 this.doQuery(this.getRawValue());
19135 doForce : function(){
19136 if(this.el.dom.value.length > 0){
19137 this.el.dom.value =
19138 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19144 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19145 * query allowing the query action to be canceled if needed.
19146 * @param {String} query The SQL query to execute
19147 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19148 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19149 * saved in the current store (defaults to false)
19151 doQuery : function(q, forceAll){
19152 if(q === undefined || q === null){
19157 forceAll: forceAll,
19161 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19165 forceAll = qe.forceAll;
19166 if(forceAll === true || (q.length >= this.minChars)){
19167 if(this.lastQuery != q || this.alwaysQuery){
19168 this.lastQuery = q;
19169 if(this.mode == 'local'){
19170 this.selectedIndex = -1;
19172 this.store.clearFilter();
19174 this.store.filter(this.displayField, q);
19178 this.store.baseParams[this.queryParam] = q;
19180 params: this.getParams(q)
19185 this.selectedIndex = -1;
19192 getParams : function(q){
19194 //p[this.queryParam] = q;
19197 p.limit = this.pageSize;
19203 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19205 collapse : function(){
19206 if(!this.isExpanded()){
19210 Roo.get(document).un('mousedown', this.collapseIf, this);
19211 Roo.get(document).un('mousewheel', this.collapseIf, this);
19212 if (!this.editable) {
19213 Roo.get(document).un('keydown', this.listKeyPress, this);
19215 this.fireEvent('collapse', this);
19219 collapseIf : function(e){
19220 if(!e.within(this.wrap) && !e.within(this.list)){
19226 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19228 expand : function(){
19229 if(this.isExpanded() || !this.hasFocus){
19232 this.list.alignTo(this.el, this.listAlign);
19234 Roo.get(document).on('mousedown', this.collapseIf, this);
19235 Roo.get(document).on('mousewheel', this.collapseIf, this);
19236 if (!this.editable) {
19237 Roo.get(document).on('keydown', this.listKeyPress, this);
19240 this.fireEvent('expand', this);
19244 // Implements the default empty TriggerField.onTriggerClick function
19245 onTriggerClick : function(){
19249 if(this.isExpanded()){
19251 if (!this.blockFocus) {
19256 this.hasFocus = true;
19257 if(this.triggerAction == 'all') {
19258 this.doQuery(this.allQuery, true);
19260 this.doQuery(this.getRawValue());
19262 if (!this.blockFocus) {
19267 listKeyPress : function(e)
19269 //Roo.log('listkeypress');
19270 // scroll to first matching element based on key pres..
19271 if (e.isSpecialKey()) {
19274 var k = String.fromCharCode(e.getKey()).toUpperCase();
19277 var csel = this.view.getSelectedNodes();
19278 var cselitem = false;
19280 var ix = this.view.indexOf(csel[0]);
19281 cselitem = this.store.getAt(ix);
19282 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19288 this.store.each(function(v) {
19290 // start at existing selection.
19291 if (cselitem.id == v.id) {
19297 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19298 match = this.store.indexOf(v);
19303 if (match === false) {
19304 return true; // no more action?
19307 this.view.select(match);
19308 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19309 sn.scrollIntoView(sn.dom.parentNode, false);
19313 * @cfg {Boolean} grow
19317 * @cfg {Number} growMin
19321 * @cfg {Number} growMax
19329 * Copyright(c) 2010-2012, Roo J Solutions Limited
19336 * @class Roo.form.ComboBoxArray
19337 * @extends Roo.form.TextField
19338 * A facebook style adder... for lists of email / people / countries etc...
19339 * pick multiple items from a combo box, and shows each one.
19341 * Fred [x] Brian [x] [Pick another |v]
19344 * For this to work: it needs various extra information
19345 * - normal combo problay has
19347 * + displayField, valueField
19349 * For our purpose...
19352 * If we change from 'extends' to wrapping...
19359 * Create a new ComboBoxArray.
19360 * @param {Object} config Configuration options
19364 Roo.form.ComboBoxArray = function(config)
19368 * @event beforeremove
19369 * Fires before remove the value from the list
19370 * @param {Roo.form.ComboBoxArray} _self This combo box array
19371 * @param {Roo.form.ComboBoxArray.Item} item removed item
19373 'beforeremove' : true,
19376 * Fires when remove the value from the list
19377 * @param {Roo.form.ComboBoxArray} _self This combo box array
19378 * @param {Roo.form.ComboBoxArray.Item} item removed item
19385 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19387 this.items = new Roo.util.MixedCollection(false);
19389 // construct the child combo...
19399 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19402 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19407 // behavies liek a hiddne field
19408 inputType: 'hidden',
19410 * @cfg {Number} width The width of the box that displays the selected element
19417 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19421 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19423 hiddenName : false,
19425 * @cfg {String} seperator The value seperator normally ','
19429 // private the array of items that are displayed..
19431 // private - the hidden field el.
19433 // private - the filed el..
19436 //validateValue : function() { return true; }, // all values are ok!
19437 //onAddClick: function() { },
19439 onRender : function(ct, position)
19442 // create the standard hidden element
19443 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19446 // give fake names to child combo;
19447 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19448 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19450 this.combo = Roo.factory(this.combo, Roo.form);
19451 this.combo.onRender(ct, position);
19452 if (typeof(this.combo.width) != 'undefined') {
19453 this.combo.onResize(this.combo.width,0);
19456 this.combo.initEvents();
19458 // assigned so form know we need to do this..
19459 this.store = this.combo.store;
19460 this.valueField = this.combo.valueField;
19461 this.displayField = this.combo.displayField ;
19464 this.combo.wrap.addClass('x-cbarray-grp');
19466 var cbwrap = this.combo.wrap.createChild(
19467 {tag: 'div', cls: 'x-cbarray-cb'},
19472 this.hiddenEl = this.combo.wrap.createChild({
19473 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19475 this.el = this.combo.wrap.createChild({
19476 tag: 'input', type:'hidden' , name: this.name, value : ''
19478 // this.el.dom.removeAttribute("name");
19481 this.outerWrap = this.combo.wrap;
19482 this.wrap = cbwrap;
19484 this.outerWrap.setWidth(this.width);
19485 this.outerWrap.dom.removeChild(this.el.dom);
19487 this.wrap.dom.appendChild(this.el.dom);
19488 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19489 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19491 this.combo.trigger.setStyle('position','relative');
19492 this.combo.trigger.setStyle('left', '0px');
19493 this.combo.trigger.setStyle('top', '2px');
19495 this.combo.el.setStyle('vertical-align', 'text-bottom');
19497 //this.trigger.setStyle('vertical-align', 'top');
19499 // this should use the code from combo really... on('add' ....)
19503 this.adder = this.outerWrap.createChild(
19504 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19506 this.adder.on('click', function(e) {
19507 _t.fireEvent('adderclick', this, e);
19511 //this.adder.on('click', this.onAddClick, _t);
19514 this.combo.on('select', function(cb, rec, ix) {
19515 this.addItem(rec.data);
19518 cb.el.dom.value = '';
19519 //cb.lastData = rec.data;
19528 getName: function()
19530 // returns hidden if it's set..
19531 if (!this.rendered) {return ''};
19532 return this.hiddenName ? this.hiddenName : this.name;
19537 onResize: function(w, h){
19540 // not sure if this is needed..
19541 //this.combo.onResize(w,h);
19543 if(typeof w != 'number'){
19544 // we do not handle it!?!?
19547 var tw = this.combo.trigger.getWidth();
19548 tw += this.addicon ? this.addicon.getWidth() : 0;
19549 tw += this.editicon ? this.editicon.getWidth() : 0;
19551 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19553 this.combo.trigger.setStyle('left', '0px');
19555 if(this.list && this.listWidth === undefined){
19556 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19557 this.list.setWidth(lw);
19558 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19565 addItem: function(rec)
19567 var valueField = this.combo.valueField;
19568 var displayField = this.combo.displayField;
19570 if (this.items.indexOfKey(rec[valueField]) > -1) {
19571 //console.log("GOT " + rec.data.id);
19575 var x = new Roo.form.ComboBoxArray.Item({
19576 //id : rec[this.idField],
19578 displayField : displayField ,
19579 tipField : displayField ,
19583 this.items.add(rec[valueField],x);
19584 // add it before the element..
19585 this.updateHiddenEl();
19586 x.render(this.outerWrap, this.wrap.dom);
19587 // add the image handler..
19590 updateHiddenEl : function()
19593 if (!this.hiddenEl) {
19597 var idField = this.combo.valueField;
19599 this.items.each(function(f) {
19600 ar.push(f.data[idField]);
19602 this.hiddenEl.dom.value = ar.join(this.seperator);
19608 this.items.clear();
19610 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19614 this.el.dom.value = '';
19615 if (this.hiddenEl) {
19616 this.hiddenEl.dom.value = '';
19620 getValue: function()
19622 return this.hiddenEl ? this.hiddenEl.dom.value : '';
19624 setValue: function(v) // not a valid action - must use addItems..
19629 if (this.store.isLocal && (typeof(v) == 'string')) {
19630 // then we can use the store to find the values..
19631 // comma seperated at present.. this needs to allow JSON based encoding..
19632 this.hiddenEl.value = v;
19634 Roo.each(v.split(this.seperator), function(k) {
19635 Roo.log("CHECK " + this.valueField + ',' + k);
19636 var li = this.store.query(this.valueField, k);
19641 add[this.valueField] = k;
19642 add[this.displayField] = li.item(0).data[this.displayField];
19648 if (typeof(v) == 'object' ) {
19649 // then let's assume it's an array of objects..
19650 Roo.each(v, function(l) {
19652 if (typeof(l) == 'string') {
19654 add[this.valueField] = l;
19655 add[this.displayField] = l
19664 setFromData: function(v)
19666 // this recieves an object, if setValues is called.
19668 this.el.dom.value = v[this.displayField];
19669 this.hiddenEl.dom.value = v[this.valueField];
19670 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19673 var kv = v[this.valueField];
19674 var dv = v[this.displayField];
19675 kv = typeof(kv) != 'string' ? '' : kv;
19676 dv = typeof(dv) != 'string' ? '' : dv;
19679 var keys = kv.split(this.seperator);
19680 var display = dv.split(this.seperator);
19681 for (var i = 0 ; i < keys.length; i++) {
19683 add[this.valueField] = keys[i];
19684 add[this.displayField] = display[i];
19692 * Validates the combox array value
19693 * @return {Boolean} True if the value is valid, else false
19695 validate : function(){
19696 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19697 this.clearInvalid();
19703 validateValue : function(value){
19704 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19712 isDirty : function() {
19713 if(this.disabled) {
19718 var d = Roo.decode(String(this.originalValue));
19720 return String(this.getValue()) !== String(this.originalValue);
19723 var originalValue = [];
19725 for (var i = 0; i < d.length; i++){
19726 originalValue.push(d[i][this.valueField]);
19729 return String(this.getValue()) !== String(originalValue.join(this.seperator));
19738 * @class Roo.form.ComboBoxArray.Item
19739 * @extends Roo.BoxComponent
19740 * A selected item in the list
19741 * Fred [x] Brian [x] [Pick another |v]
19744 * Create a new item.
19745 * @param {Object} config Configuration options
19748 Roo.form.ComboBoxArray.Item = function(config) {
19749 config.id = Roo.id();
19750 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19753 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19756 displayField : false,
19760 defaultAutoCreate : {
19762 cls: 'x-cbarray-item',
19769 src : Roo.BLANK_IMAGE_URL ,
19777 onRender : function(ct, position)
19779 Roo.form.Field.superclass.onRender.call(this, ct, position);
19782 var cfg = this.getAutoCreate();
19783 this.el = ct.createChild(cfg, position);
19786 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19788 this.el.child('div').dom.innerHTML = this.cb.renderer ?
19789 this.cb.renderer(this.data) :
19790 String.format('{0}',this.data[this.displayField]);
19793 this.el.child('div').dom.setAttribute('qtip',
19794 String.format('{0}',this.data[this.tipField])
19797 this.el.child('img').on('click', this.remove, this);
19801 remove : function()
19803 if(this.cb.disabled){
19807 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19808 this.cb.items.remove(this);
19809 this.el.child('img').un('click', this.remove, this);
19811 this.cb.updateHiddenEl();
19813 this.cb.fireEvent('remove', this.cb, this);
19818 * RooJS Library 1.1.1
19819 * Copyright(c) 2008-2011 Alan Knowles
19826 * @class Roo.form.ComboNested
19827 * @extends Roo.form.ComboBox
19828 * A combobox for that allows selection of nested items in a list,
19843 * Create a new ComboNested
19844 * @param {Object} config Configuration options
19846 Roo.form.ComboNested = function(config){
19847 Roo.form.ComboCheck.superclass.constructor.call(this, config);
19848 // should verify some data...
19850 // hiddenName = required..
19851 // displayField = required
19852 // valudField == required
19853 var req= [ 'hiddenName', 'displayField', 'valueField' ];
19855 Roo.each(req, function(e) {
19856 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19857 throw "Roo.form.ComboNested : missing value for: " + e;
19864 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19867 * @config {Number} max Number of columns to show
19872 list : null, // the outermost div..
19873 innerLists : null, // the
19877 loadingChildren : false,
19879 onRender : function(ct, position)
19881 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19883 if(this.hiddenName){
19884 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
19886 this.hiddenField.value =
19887 this.hiddenValue !== undefined ? this.hiddenValue :
19888 this.value !== undefined ? this.value : '';
19890 // prevent input submission
19891 this.el.dom.removeAttribute('name');
19897 this.el.dom.setAttribute('autocomplete', 'off');
19900 var cls = 'x-combo-list';
19902 this.list = new Roo.Layer({
19903 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19906 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19907 this.list.setWidth(lw);
19908 this.list.swallowEvent('mousewheel');
19909 this.assetHeight = 0;
19912 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19913 this.assetHeight += this.header.getHeight();
19915 this.innerLists = [];
19918 for (var i =0 ; i < this.maxColumns; i++) {
19919 this.onRenderList( cls, i);
19922 // always needs footer, as we are going to have an 'OK' button.
19923 this.footer = this.list.createChild({cls:cls+'-ft'});
19924 this.pageTb = new Roo.Toolbar(this.footer);
19929 handler: function()
19935 if ( this.allowBlank && !this.disableClear) {
19937 this.pageTb.add(new Roo.Toolbar.Fill(), {
19938 cls: 'x-btn-icon x-btn-clear',
19940 handler: function()
19943 _this.clearValue();
19944 _this.onSelect(false, -1);
19949 this.assetHeight += this.footer.getHeight();
19953 onRenderList : function ( cls, i)
19956 var lw = Math.floor(
19957 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19960 this.list.setWidth(lw); // default to '1'
19962 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19963 //il.on('mouseover', this.onViewOver, this, { list: i });
19964 //il.on('mousemove', this.onViewMove, this, { list: i });
19966 il.setStyle({ 'overflow-x' : 'hidden'});
19969 this.tpl = new Roo.Template({
19970 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19971 isEmpty: function (value, allValues) {
19973 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19974 return dl ? 'has-children' : 'no-children'
19979 var store = this.store;
19981 store = new Roo.data.SimpleStore({
19982 //fields : this.store.reader.meta.fields,
19983 reader : this.store.reader,
19987 this.stores[i] = store;
19989 var view = this.views[i] = new Roo.View(
19995 selectedClass: this.selectedClass
19998 view.getEl().setWidth(lw);
19999 view.getEl().setStyle({
20000 position: i < 1 ? 'relative' : 'absolute',
20002 left: (i * lw ) + 'px',
20003 display : i > 0 ? 'none' : 'block'
20005 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20006 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20007 //view.on('click', this.onViewClick, this, { list : i });
20009 store.on('beforeload', this.onBeforeLoad, this);
20010 store.on('load', this.onLoad, this, { list : i});
20011 store.on('loadexception', this.onLoadException, this);
20013 // hide the other vies..
20019 restrictHeight : function()
20022 Roo.each(this.innerLists, function(il,i) {
20023 var el = this.views[i].getEl();
20024 el.dom.style.height = '';
20025 var inner = el.dom;
20026 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20027 // only adjust heights on other ones..
20028 mh = Math.max(h, mh);
20031 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20032 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20039 this.list.beginUpdate();
20040 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20041 this.list.alignTo(this.el, this.listAlign);
20042 this.list.endUpdate();
20047 // -- store handlers..
20049 onBeforeLoad : function()
20051 if(!this.hasFocus){
20054 this.innerLists[0].update(this.loadingText ?
20055 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20056 this.restrictHeight();
20057 this.selectedIndex = -1;
20060 onLoad : function(a,b,c,d)
20062 if (!this.loadingChildren) {
20063 // then we are loading the top level. - hide the children
20064 for (var i = 1;i < this.views.length; i++) {
20065 this.views[i].getEl().setStyle({ display : 'none' });
20067 var lw = Math.floor(
20068 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20071 this.list.setWidth(lw); // default to '1'
20075 if(!this.hasFocus){
20079 if(this.store.getCount() > 0) {
20081 this.restrictHeight();
20083 this.onEmptyResults();
20086 if (!this.loadingChildren) {
20087 this.selectActive();
20090 this.stores[1].loadData([]);
20091 this.stores[2].loadData([]);
20100 onLoadException : function()
20103 Roo.log(this.store.reader.jsonData);
20104 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20105 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20110 // no cleaning of leading spaces on blur here.
20111 cleanLeadingSpace : function(e) { },
20114 onSelectChange : function (view, sels, opts )
20116 var ix = view.getSelectedIndexes();
20118 if (opts.list > this.maxColumns - 2) {
20119 if (view.store.getCount()< 1) {
20120 this.views[opts.list ].getEl().setStyle({ display : 'none' });
20124 // used to clear ?? but if we are loading unselected
20125 this.setFromData(view.store.getAt(ix[0]).data);
20134 // this get's fired when trigger opens..
20135 // this.setFromData({});
20136 var str = this.stores[opts.list+1];
20137 str.data.clear(); // removeall wihtout the fire events..
20141 var rec = view.store.getAt(ix[0]);
20143 this.setFromData(rec.data);
20144 this.fireEvent('select', this, rec, ix[0]);
20146 var lw = Math.floor(
20148 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20149 ) / this.maxColumns
20151 this.loadingChildren = true;
20152 this.stores[opts.list+1].loadDataFromChildren( rec );
20153 this.loadingChildren = false;
20154 var dl = this.stores[opts.list+1]. getTotalCount();
20156 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20158 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20159 for (var i = opts.list+2; i < this.views.length;i++) {
20160 this.views[i].getEl().setStyle({ display : 'none' });
20163 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20164 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20166 if (this.isLoading) {
20167 // this.selectActive(opts.list);
20175 onDoubleClick : function()
20177 this.collapse(); //??
20185 recordToStack : function(store, prop, value, stack)
20187 var cstore = new Roo.data.SimpleStore({
20188 //fields : this.store.reader.meta.fields, // we need array reader.. for
20189 reader : this.store.reader,
20193 var record = false;
20195 if(store.getCount() < 1){
20198 store.each(function(r){
20199 if(r.data[prop] == value){
20204 if (r.data.cn && r.data.cn.length) {
20205 cstore.loadDataFromChildren( r);
20206 var cret = _this.recordToStack(cstore, prop, value, stack);
20207 if (cret !== false) {
20216 if (record == false) {
20219 stack.unshift(srec);
20224 * find the stack of stores that match our value.
20229 selectActive : function ()
20231 // if store is not loaded, then we will need to wait for that to happen first.
20233 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20234 for (var i = 0; i < stack.length; i++ ) {
20235 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20247 * Ext JS Library 1.1.1
20248 * Copyright(c) 2006-2007, Ext JS, LLC.
20250 * Originally Released Under LGPL - original licence link has changed is not relivant.
20253 * <script type="text/javascript">
20256 * @class Roo.form.Checkbox
20257 * @extends Roo.form.Field
20258 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20260 * Creates a new Checkbox
20261 * @param {Object} config Configuration options
20263 Roo.form.Checkbox = function(config){
20264 Roo.form.Checkbox.superclass.constructor.call(this, config);
20268 * Fires when the checkbox is checked or unchecked.
20269 * @param {Roo.form.Checkbox} this This checkbox
20270 * @param {Boolean} checked The new checked value
20276 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20278 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20280 focusClass : undefined,
20282 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20284 fieldClass: "x-form-field",
20286 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20290 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20291 * {tag: "input", type: "checkbox", autocomplete: "off"})
20293 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20295 * @cfg {String} boxLabel The text that appears beside the checkbox
20299 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20303 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20305 valueOff: '0', // value when not checked..
20307 actionMode : 'viewEl',
20310 itemCls : 'x-menu-check-item x-form-item',
20311 groupClass : 'x-menu-group-item',
20312 inputType : 'hidden',
20315 inSetChecked: false, // check that we are not calling self...
20317 inputElement: false, // real input element?
20318 basedOn: false, // ????
20320 isFormField: true, // not sure where this is needed!!!!
20322 onResize : function(){
20323 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20324 if(!this.boxLabel){
20325 this.el.alignTo(this.wrap, 'c-c');
20329 initEvents : function(){
20330 Roo.form.Checkbox.superclass.initEvents.call(this);
20331 this.el.on("click", this.onClick, this);
20332 this.el.on("change", this.onClick, this);
20336 getResizeEl : function(){
20340 getPositionEl : function(){
20345 onRender : function(ct, position){
20346 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20348 if(this.inputValue !== undefined){
20349 this.el.dom.value = this.inputValue;
20352 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20353 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20354 var viewEl = this.wrap.createChild({
20355 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20356 this.viewEl = viewEl;
20357 this.wrap.on('click', this.onClick, this);
20359 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20360 this.el.on('propertychange', this.setFromHidden, this); //ie
20365 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20366 // viewEl.on('click', this.onClick, this);
20368 //if(this.checked){
20369 this.setChecked(this.checked);
20371 //this.checked = this.el.dom;
20377 initValue : Roo.emptyFn,
20380 * Returns the checked state of the checkbox.
20381 * @return {Boolean} True if checked, else false
20383 getValue : function(){
20385 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20387 return this.valueOff;
20392 onClick : function(){
20393 if (this.disabled) {
20396 this.setChecked(!this.checked);
20398 //if(this.el.dom.checked != this.checked){
20399 // this.setValue(this.el.dom.checked);
20404 * Sets the checked state of the checkbox.
20405 * On is always based on a string comparison between inputValue and the param.
20406 * @param {Boolean/String} value - the value to set
20407 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20409 setValue : function(v,suppressEvent){
20412 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20413 //if(this.el && this.el.dom){
20414 // this.el.dom.checked = this.checked;
20415 // this.el.dom.defaultChecked = this.checked;
20417 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20418 //this.fireEvent("check", this, this.checked);
20421 setChecked : function(state,suppressEvent)
20423 if (this.inSetChecked) {
20424 this.checked = state;
20430 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20432 this.checked = state;
20433 if(suppressEvent !== true){
20434 this.fireEvent('check', this, state);
20436 this.inSetChecked = true;
20437 this.el.dom.value = state ? this.inputValue : this.valueOff;
20438 this.inSetChecked = false;
20441 // handle setting of hidden value by some other method!!?!?
20442 setFromHidden: function()
20447 //console.log("SET FROM HIDDEN");
20448 //alert('setFrom hidden');
20449 this.setValue(this.el.dom.value);
20452 onDestroy : function()
20455 Roo.get(this.viewEl).remove();
20458 Roo.form.Checkbox.superclass.onDestroy.call(this);
20461 setBoxLabel : function(str)
20463 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20468 * Ext JS Library 1.1.1
20469 * Copyright(c) 2006-2007, Ext JS, LLC.
20471 * Originally Released Under LGPL - original licence link has changed is not relivant.
20474 * <script type="text/javascript">
20478 * @class Roo.form.Radio
20479 * @extends Roo.form.Checkbox
20480 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20481 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20483 * Creates a new Radio
20484 * @param {Object} config Configuration options
20486 Roo.form.Radio = function(){
20487 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20489 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20490 inputType: 'radio',
20493 * If this radio is part of a group, it will return the selected value
20496 getGroupValue : function(){
20497 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20501 onRender : function(ct, position){
20502 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20504 if(this.inputValue !== undefined){
20505 this.el.dom.value = this.inputValue;
20508 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20509 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20510 //var viewEl = this.wrap.createChild({
20511 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20512 //this.viewEl = viewEl;
20513 //this.wrap.on('click', this.onClick, this);
20515 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20516 //this.el.on('propertychange', this.setFromHidden, this); //ie
20521 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20522 // viewEl.on('click', this.onClick, this);
20525 this.el.dom.checked = 'checked' ;
20531 });//<script type="text/javascript">
20534 * Based Ext JS Library 1.1.1
20535 * Copyright(c) 2006-2007, Ext JS, LLC.
20541 * @class Roo.HtmlEditorCore
20542 * @extends Roo.Component
20543 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20545 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20548 Roo.HtmlEditorCore = function(config){
20551 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20556 * @event initialize
20557 * Fires when the editor is fully initialized (including the iframe)
20558 * @param {Roo.HtmlEditorCore} this
20563 * Fires when the editor is first receives the focus. Any insertion must wait
20564 * until after this event.
20565 * @param {Roo.HtmlEditorCore} this
20569 * @event beforesync
20570 * Fires before the textarea is updated with content from the editor iframe. Return false
20571 * to cancel the sync.
20572 * @param {Roo.HtmlEditorCore} this
20573 * @param {String} html
20577 * @event beforepush
20578 * Fires before the iframe editor is updated with content from the textarea. Return false
20579 * to cancel the push.
20580 * @param {Roo.HtmlEditorCore} this
20581 * @param {String} html
20586 * Fires when the textarea is updated with content from the editor iframe.
20587 * @param {Roo.HtmlEditorCore} this
20588 * @param {String} html
20593 * Fires when the iframe editor is updated with content from the textarea.
20594 * @param {Roo.HtmlEditorCore} this
20595 * @param {String} html
20600 * @event editorevent
20601 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20602 * @param {Roo.HtmlEditorCore} this
20608 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20610 // defaults : white / black...
20611 this.applyBlacklists();
20618 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20622 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20628 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20633 * @cfg {Number} height (in pixels)
20637 * @cfg {Number} width (in pixels)
20642 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20645 stylesheets: false,
20648 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20650 allowComments: false,
20654 // private properties
20655 validationEvent : false,
20657 initialized : false,
20659 sourceEditMode : false,
20660 onFocus : Roo.emptyFn,
20662 hideMode:'offsets',
20666 // blacklist + whitelisted elements..
20673 * Protected method that will not generally be called directly. It
20674 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20675 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20677 getDocMarkup : function(){
20681 // inherit styels from page...??
20682 if (this.stylesheets === false) {
20684 Roo.get(document.head).select('style').each(function(node) {
20685 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20688 Roo.get(document.head).select('link').each(function(node) {
20689 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20692 } else if (!this.stylesheets.length) {
20694 st = '<style type="text/css">' +
20695 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20698 for (var i in this.stylesheets) {
20699 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20704 st += '<style type="text/css">' +
20705 'IMG { cursor: pointer } ' +
20708 var cls = 'roo-htmleditor-body';
20710 if(this.bodyCls.length){
20711 cls += ' ' + this.bodyCls;
20714 return '<html><head>' + st +
20715 //<style type="text/css">' +
20716 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20718 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
20722 onRender : function(ct, position)
20725 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20726 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20729 this.el.dom.style.border = '0 none';
20730 this.el.dom.setAttribute('tabIndex', -1);
20731 this.el.addClass('x-hidden hide');
20735 if(Roo.isIE){ // fix IE 1px bogus margin
20736 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20740 this.frameId = Roo.id();
20744 var iframe = this.owner.wrap.createChild({
20746 cls: 'form-control', // bootstrap..
20748 name: this.frameId,
20749 frameBorder : 'no',
20750 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20755 this.iframe = iframe.dom;
20757 this.assignDocWin();
20759 this.doc.designMode = 'on';
20762 this.doc.write(this.getDocMarkup());
20766 var task = { // must defer to wait for browser to be ready
20768 //console.log("run task?" + this.doc.readyState);
20769 this.assignDocWin();
20770 if(this.doc.body || this.doc.readyState == 'complete'){
20772 this.doc.designMode="on";
20776 Roo.TaskMgr.stop(task);
20777 this.initEditor.defer(10, this);
20784 Roo.TaskMgr.start(task);
20789 onResize : function(w, h)
20791 Roo.log('resize: ' +w + ',' + h );
20792 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20796 if(typeof w == 'number'){
20798 this.iframe.style.width = w + 'px';
20800 if(typeof h == 'number'){
20802 this.iframe.style.height = h + 'px';
20804 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20811 * Toggles the editor between standard and source edit mode.
20812 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20814 toggleSourceEdit : function(sourceEditMode){
20816 this.sourceEditMode = sourceEditMode === true;
20818 if(this.sourceEditMode){
20820 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20823 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20824 //this.iframe.className = '';
20827 //this.setSize(this.owner.wrap.getSize());
20828 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20835 * Protected method that will not generally be called directly. If you need/want
20836 * custom HTML cleanup, this is the method you should override.
20837 * @param {String} html The HTML to be cleaned
20838 * return {String} The cleaned HTML
20840 cleanHtml : function(html){
20841 html = String(html);
20842 if(html.length > 5){
20843 if(Roo.isSafari){ // strip safari nonsense
20844 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20847 if(html == ' '){
20854 * HTML Editor -> Textarea
20855 * Protected method that will not generally be called directly. Syncs the contents
20856 * of the editor iframe with the textarea.
20858 syncValue : function(){
20859 if(this.initialized){
20860 var bd = (this.doc.body || this.doc.documentElement);
20861 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20862 var html = bd.innerHTML;
20864 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20865 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20867 html = '<div style="'+m[0]+'">' + html + '</div>';
20870 html = this.cleanHtml(html);
20871 // fix up the special chars.. normaly like back quotes in word...
20872 // however we do not want to do this with chinese..
20873 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20875 var cc = match.charCodeAt();
20877 // Get the character value, handling surrogate pairs
20878 if (match.length == 2) {
20879 // It's a surrogate pair, calculate the Unicode code point
20880 var high = match.charCodeAt(0) - 0xD800;
20881 var low = match.charCodeAt(1) - 0xDC00;
20882 cc = (high * 0x400) + low + 0x10000;
20884 (cc >= 0x4E00 && cc < 0xA000 ) ||
20885 (cc >= 0x3400 && cc < 0x4E00 ) ||
20886 (cc >= 0xf900 && cc < 0xfb00 )
20891 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20892 return "&#" + cc + ";";
20899 if(this.owner.fireEvent('beforesync', this, html) !== false){
20900 this.el.dom.value = html;
20901 this.owner.fireEvent('sync', this, html);
20907 * Protected method that will not generally be called directly. Pushes the value of the textarea
20908 * into the iframe editor.
20910 pushValue : function(){
20911 if(this.initialized){
20912 var v = this.el.dom.value.trim();
20914 // if(v.length < 1){
20918 if(this.owner.fireEvent('beforepush', this, v) !== false){
20919 var d = (this.doc.body || this.doc.documentElement);
20921 this.cleanUpPaste();
20922 this.el.dom.value = d.innerHTML;
20923 this.owner.fireEvent('push', this, v);
20929 deferFocus : function(){
20930 this.focus.defer(10, this);
20934 focus : function(){
20935 if(this.win && !this.sourceEditMode){
20942 assignDocWin: function()
20944 var iframe = this.iframe;
20947 this.doc = iframe.contentWindow.document;
20948 this.win = iframe.contentWindow;
20950 // if (!Roo.get(this.frameId)) {
20953 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20954 // this.win = Roo.get(this.frameId).dom.contentWindow;
20956 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20960 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20961 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20966 initEditor : function(){
20967 //console.log("INIT EDITOR");
20968 this.assignDocWin();
20972 this.doc.designMode="on";
20974 this.doc.write(this.getDocMarkup());
20977 var dbody = (this.doc.body || this.doc.documentElement);
20978 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20979 // this copies styles from the containing element into thsi one..
20980 // not sure why we need all of this..
20981 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20983 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20984 //ss['background-attachment'] = 'fixed'; // w3c
20985 dbody.bgProperties = 'fixed'; // ie
20986 //Roo.DomHelper.applyStyles(dbody, ss);
20987 Roo.EventManager.on(this.doc, {
20988 //'mousedown': this.onEditorEvent,
20989 'mouseup': this.onEditorEvent,
20990 'dblclick': this.onEditorEvent,
20991 'click': this.onEditorEvent,
20992 'keyup': this.onEditorEvent,
20997 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20999 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21000 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21002 this.initialized = true;
21004 this.owner.fireEvent('initialize', this);
21009 onDestroy : function(){
21015 //for (var i =0; i < this.toolbars.length;i++) {
21016 // // fixme - ask toolbars for heights?
21017 // this.toolbars[i].onDestroy();
21020 //this.wrap.dom.innerHTML = '';
21021 //this.wrap.remove();
21026 onFirstFocus : function(){
21028 this.assignDocWin();
21031 this.activated = true;
21034 if(Roo.isGecko){ // prevent silly gecko errors
21036 var s = this.win.getSelection();
21037 if(!s.focusNode || s.focusNode.nodeType != 3){
21038 var r = s.getRangeAt(0);
21039 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21044 this.execCmd('useCSS', true);
21045 this.execCmd('styleWithCSS', false);
21048 this.owner.fireEvent('activate', this);
21052 adjustFont: function(btn){
21053 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21054 //if(Roo.isSafari){ // safari
21057 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21058 if(Roo.isSafari){ // safari
21059 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21060 v = (v < 10) ? 10 : v;
21061 v = (v > 48) ? 48 : v;
21062 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21067 v = Math.max(1, v+adjust);
21069 this.execCmd('FontSize', v );
21072 onEditorEvent : function(e)
21074 this.owner.fireEvent('editorevent', this, e);
21075 // this.updateToolbar();
21076 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21079 insertTag : function(tg)
21081 // could be a bit smarter... -> wrap the current selected tRoo..
21082 if (tg.toLowerCase() == 'span' ||
21083 tg.toLowerCase() == 'code' ||
21084 tg.toLowerCase() == 'sup' ||
21085 tg.toLowerCase() == 'sub'
21088 range = this.createRange(this.getSelection());
21089 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21090 wrappingNode.appendChild(range.extractContents());
21091 range.insertNode(wrappingNode);
21098 this.execCmd("formatblock", tg);
21102 insertText : function(txt)
21106 var range = this.createRange();
21107 range.deleteContents();
21108 //alert(Sender.getAttribute('label'));
21110 range.insertNode(this.doc.createTextNode(txt));
21116 * Executes a Midas editor command on the editor document and performs necessary focus and
21117 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21118 * @param {String} cmd The Midas command
21119 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21121 relayCmd : function(cmd, value){
21123 this.execCmd(cmd, value);
21124 this.owner.fireEvent('editorevent', this);
21125 //this.updateToolbar();
21126 this.owner.deferFocus();
21130 * Executes a Midas editor command directly on the editor document.
21131 * For visual commands, you should use {@link #relayCmd} instead.
21132 * <b>This should only be called after the editor is initialized.</b>
21133 * @param {String} cmd The Midas command
21134 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21136 execCmd : function(cmd, value){
21137 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21144 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21146 * @param {String} text | dom node..
21148 insertAtCursor : function(text)
21151 if(!this.activated){
21157 var r = this.doc.selection.createRange();
21168 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21172 // from jquery ui (MIT licenced)
21174 var win = this.win;
21176 if (win.getSelection && win.getSelection().getRangeAt) {
21177 range = win.getSelection().getRangeAt(0);
21178 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21179 range.insertNode(node);
21180 } else if (win.document.selection && win.document.selection.createRange) {
21181 // no firefox support
21182 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21183 win.document.selection.createRange().pasteHTML(txt);
21185 // no firefox support
21186 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21187 this.execCmd('InsertHTML', txt);
21196 mozKeyPress : function(e){
21198 var c = e.getCharCode(), cmd;
21201 c = String.fromCharCode(c).toLowerCase();
21215 this.cleanUpPaste.defer(100, this);
21223 e.preventDefault();
21231 fixKeys : function(){ // load time branching for fastest keydown performance
21233 return function(e){
21234 var k = e.getKey(), r;
21237 r = this.doc.selection.createRange();
21240 r.pasteHTML('    ');
21247 r = this.doc.selection.createRange();
21249 var target = r.parentElement();
21250 if(!target || target.tagName.toLowerCase() != 'li'){
21252 r.pasteHTML('<br />');
21258 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21259 this.cleanUpPaste.defer(100, this);
21265 }else if(Roo.isOpera){
21266 return function(e){
21267 var k = e.getKey();
21271 this.execCmd('InsertHTML','    ');
21274 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21275 this.cleanUpPaste.defer(100, this);
21280 }else if(Roo.isSafari){
21281 return function(e){
21282 var k = e.getKey();
21286 this.execCmd('InsertText','\t');
21290 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21291 this.cleanUpPaste.defer(100, this);
21299 getAllAncestors: function()
21301 var p = this.getSelectedNode();
21304 a.push(p); // push blank onto stack..
21305 p = this.getParentElement();
21309 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21313 a.push(this.doc.body);
21317 lastSelNode : false,
21320 getSelection : function()
21322 this.assignDocWin();
21323 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21326 getSelectedNode: function()
21328 // this may only work on Gecko!!!
21330 // should we cache this!!!!
21335 var range = this.createRange(this.getSelection()).cloneRange();
21338 var parent = range.parentElement();
21340 var testRange = range.duplicate();
21341 testRange.moveToElementText(parent);
21342 if (testRange.inRange(range)) {
21345 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21348 parent = parent.parentElement;
21353 // is ancestor a text element.
21354 var ac = range.commonAncestorContainer;
21355 if (ac.nodeType == 3) {
21356 ac = ac.parentNode;
21359 var ar = ac.childNodes;
21362 var other_nodes = [];
21363 var has_other_nodes = false;
21364 for (var i=0;i<ar.length;i++) {
21365 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21368 // fullly contained node.
21370 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21375 // probably selected..
21376 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21377 other_nodes.push(ar[i]);
21381 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21386 has_other_nodes = true;
21388 if (!nodes.length && other_nodes.length) {
21389 nodes= other_nodes;
21391 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21397 createRange: function(sel)
21399 // this has strange effects when using with
21400 // top toolbar - not sure if it's a great idea.
21401 //this.editor.contentWindow.focus();
21402 if (typeof sel != "undefined") {
21404 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21406 return this.doc.createRange();
21409 return this.doc.createRange();
21412 getParentElement: function()
21415 this.assignDocWin();
21416 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21418 var range = this.createRange(sel);
21421 var p = range.commonAncestorContainer;
21422 while (p.nodeType == 3) { // text node
21433 * Range intersection.. the hard stuff...
21437 * [ -- selected range --- ]
21441 * if end is before start or hits it. fail.
21442 * if start is after end or hits it fail.
21444 * if either hits (but other is outside. - then it's not
21450 // @see http://www.thismuchiknow.co.uk/?p=64.
21451 rangeIntersectsNode : function(range, node)
21453 var nodeRange = node.ownerDocument.createRange();
21455 nodeRange.selectNode(node);
21457 nodeRange.selectNodeContents(node);
21460 var rangeStartRange = range.cloneRange();
21461 rangeStartRange.collapse(true);
21463 var rangeEndRange = range.cloneRange();
21464 rangeEndRange.collapse(false);
21466 var nodeStartRange = nodeRange.cloneRange();
21467 nodeStartRange.collapse(true);
21469 var nodeEndRange = nodeRange.cloneRange();
21470 nodeEndRange.collapse(false);
21472 return rangeStartRange.compareBoundaryPoints(
21473 Range.START_TO_START, nodeEndRange) == -1 &&
21474 rangeEndRange.compareBoundaryPoints(
21475 Range.START_TO_START, nodeStartRange) == 1;
21479 rangeCompareNode : function(range, node)
21481 var nodeRange = node.ownerDocument.createRange();
21483 nodeRange.selectNode(node);
21485 nodeRange.selectNodeContents(node);
21489 range.collapse(true);
21491 nodeRange.collapse(true);
21493 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21494 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21496 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21498 var nodeIsBefore = ss == 1;
21499 var nodeIsAfter = ee == -1;
21501 if (nodeIsBefore && nodeIsAfter) {
21504 if (!nodeIsBefore && nodeIsAfter) {
21505 return 1; //right trailed.
21508 if (nodeIsBefore && !nodeIsAfter) {
21509 return 2; // left trailed.
21515 // private? - in a new class?
21516 cleanUpPaste : function()
21518 // cleans up the whole document..
21519 Roo.log('cleanuppaste');
21521 this.cleanUpChildren(this.doc.body);
21522 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21523 if (clean != this.doc.body.innerHTML) {
21524 this.doc.body.innerHTML = clean;
21529 cleanWordChars : function(input) {// change the chars to hex code
21530 var he = Roo.HtmlEditorCore;
21532 var output = input;
21533 Roo.each(he.swapCodes, function(sw) {
21534 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21536 output = output.replace(swapper, sw[1]);
21543 cleanUpChildren : function (n)
21545 if (!n.childNodes.length) {
21548 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21549 this.cleanUpChild(n.childNodes[i]);
21556 cleanUpChild : function (node)
21559 //console.log(node);
21560 if (node.nodeName == "#text") {
21561 // clean up silly Windows -- stuff?
21564 if (node.nodeName == "#comment") {
21565 if (!this.allowComments) {
21566 node.parentNode.removeChild(node);
21568 // clean up silly Windows -- stuff?
21571 var lcname = node.tagName.toLowerCase();
21572 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21573 // whitelist of tags..
21575 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21577 node.parentNode.removeChild(node);
21582 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21584 // spans with no attributes - just remove them..
21585 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
21586 remove_keep_children = true;
21589 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21590 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21592 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21593 // remove_keep_children = true;
21596 if (remove_keep_children) {
21597 this.cleanUpChildren(node);
21598 // inserts everything just before this node...
21599 while (node.childNodes.length) {
21600 var cn = node.childNodes[0];
21601 node.removeChild(cn);
21602 node.parentNode.insertBefore(cn, node);
21604 node.parentNode.removeChild(node);
21608 if (!node.attributes || !node.attributes.length) {
21613 this.cleanUpChildren(node);
21617 function cleanAttr(n,v)
21620 if (v.match(/^\./) || v.match(/^\//)) {
21623 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21626 if (v.match(/^#/)) {
21629 if (v.match(/^\{/)) { // allow template editing.
21632 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21633 node.removeAttribute(n);
21637 var cwhite = this.cwhite;
21638 var cblack = this.cblack;
21640 function cleanStyle(n,v)
21642 if (v.match(/expression/)) { //XSS?? should we even bother..
21643 node.removeAttribute(n);
21647 var parts = v.split(/;/);
21650 Roo.each(parts, function(p) {
21651 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21655 var l = p.split(':').shift().replace(/\s+/g,'');
21656 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21658 if ( cwhite.length && cblack.indexOf(l) > -1) {
21659 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21660 //node.removeAttribute(n);
21664 // only allow 'c whitelisted system attributes'
21665 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21666 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21667 //node.removeAttribute(n);
21677 if (clean.length) {
21678 node.setAttribute(n, clean.join(';'));
21680 node.removeAttribute(n);
21686 for (var i = node.attributes.length-1; i > -1 ; i--) {
21687 var a = node.attributes[i];
21690 if (a.name.toLowerCase().substr(0,2)=='on') {
21691 node.removeAttribute(a.name);
21694 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21695 node.removeAttribute(a.name);
21698 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21699 cleanAttr(a.name,a.value); // fixme..
21702 if (a.name == 'style') {
21703 cleanStyle(a.name,a.value);
21706 /// clean up MS crap..
21707 // tecnically this should be a list of valid class'es..
21710 if (a.name == 'class') {
21711 if (a.value.match(/^Mso/)) {
21712 node.removeAttribute('class');
21715 if (a.value.match(/^body$/)) {
21716 node.removeAttribute('class');
21727 this.cleanUpChildren(node);
21733 * Clean up MS wordisms...
21735 cleanWord : function(node)
21738 this.cleanWord(this.doc.body);
21743 node.nodeName == 'SPAN' &&
21744 !node.hasAttributes() &&
21745 node.childNodes.length == 1 &&
21746 node.firstChild.nodeName == "#text"
21748 var textNode = node.firstChild;
21749 node.removeChild(textNode);
21750 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21751 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21753 node.parentNode.insertBefore(textNode, node);
21754 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21755 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21757 node.parentNode.removeChild(node);
21760 if (node.nodeName == "#text") {
21761 // clean up silly Windows -- stuff?
21764 if (node.nodeName == "#comment") {
21765 node.parentNode.removeChild(node);
21766 // clean up silly Windows -- stuff?
21770 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21771 node.parentNode.removeChild(node);
21774 //Roo.log(node.tagName);
21775 // remove - but keep children..
21776 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21777 //Roo.log('-- removed');
21778 while (node.childNodes.length) {
21779 var cn = node.childNodes[0];
21780 node.removeChild(cn);
21781 node.parentNode.insertBefore(cn, node);
21782 // move node to parent - and clean it..
21783 this.cleanWord(cn);
21785 node.parentNode.removeChild(node);
21786 /// no need to iterate chidlren = it's got none..
21787 //this.iterateChildren(node, this.cleanWord);
21791 if (node.className.length) {
21793 var cn = node.className.split(/\W+/);
21795 Roo.each(cn, function(cls) {
21796 if (cls.match(/Mso[a-zA-Z]+/)) {
21801 node.className = cna.length ? cna.join(' ') : '';
21803 node.removeAttribute("class");
21807 if (node.hasAttribute("lang")) {
21808 node.removeAttribute("lang");
21811 if (node.hasAttribute("style")) {
21813 var styles = node.getAttribute("style").split(";");
21815 Roo.each(styles, function(s) {
21816 if (!s.match(/:/)) {
21819 var kv = s.split(":");
21820 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21823 // what ever is left... we allow.
21826 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21827 if (!nstyle.length) {
21828 node.removeAttribute('style');
21831 this.iterateChildren(node, this.cleanWord);
21837 * iterateChildren of a Node, calling fn each time, using this as the scole..
21838 * @param {DomNode} node node to iterate children of.
21839 * @param {Function} fn method of this class to call on each item.
21841 iterateChildren : function(node, fn)
21843 if (!node.childNodes.length) {
21846 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21847 fn.call(this, node.childNodes[i])
21853 * cleanTableWidths.
21855 * Quite often pasting from word etc.. results in tables with column and widths.
21856 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21859 cleanTableWidths : function(node)
21864 this.cleanTableWidths(this.doc.body);
21869 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21872 Roo.log(node.tagName);
21873 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21874 this.iterateChildren(node, this.cleanTableWidths);
21877 if (node.hasAttribute('width')) {
21878 node.removeAttribute('width');
21882 if (node.hasAttribute("style")) {
21885 var styles = node.getAttribute("style").split(";");
21887 Roo.each(styles, function(s) {
21888 if (!s.match(/:/)) {
21891 var kv = s.split(":");
21892 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21895 // what ever is left... we allow.
21898 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21899 if (!nstyle.length) {
21900 node.removeAttribute('style');
21904 this.iterateChildren(node, this.cleanTableWidths);
21912 domToHTML : function(currentElement, depth, nopadtext) {
21914 depth = depth || 0;
21915 nopadtext = nopadtext || false;
21917 if (!currentElement) {
21918 return this.domToHTML(this.doc.body);
21921 //Roo.log(currentElement);
21923 var allText = false;
21924 var nodeName = currentElement.nodeName;
21925 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21927 if (nodeName == '#text') {
21929 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21934 if (nodeName != 'BODY') {
21937 // Prints the node tagName, such as <A>, <IMG>, etc
21940 for(i = 0; i < currentElement.attributes.length;i++) {
21942 var aname = currentElement.attributes.item(i).name;
21943 if (!currentElement.attributes.item(i).value.length) {
21946 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21949 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21958 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21961 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21966 // Traverse the tree
21968 var currentElementChild = currentElement.childNodes.item(i);
21969 var allText = true;
21970 var innerHTML = '';
21972 while (currentElementChild) {
21973 // Formatting code (indent the tree so it looks nice on the screen)
21974 var nopad = nopadtext;
21975 if (lastnode == 'SPAN') {
21979 if (currentElementChild.nodeName == '#text') {
21980 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21981 toadd = nopadtext ? toadd : toadd.trim();
21982 if (!nopad && toadd.length > 80) {
21983 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21985 innerHTML += toadd;
21988 currentElementChild = currentElement.childNodes.item(i);
21994 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21996 // Recursively traverse the tree structure of the child node
21997 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21998 lastnode = currentElementChild.nodeName;
22000 currentElementChild=currentElement.childNodes.item(i);
22006 // The remaining code is mostly for formatting the tree
22007 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22012 ret+= "</"+tagName+">";
22018 applyBlacklists : function()
22020 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22021 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22025 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22026 if (b.indexOf(tag) > -1) {
22029 this.white.push(tag);
22033 Roo.each(w, function(tag) {
22034 if (b.indexOf(tag) > -1) {
22037 if (this.white.indexOf(tag) > -1) {
22040 this.white.push(tag);
22045 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22046 if (w.indexOf(tag) > -1) {
22049 this.black.push(tag);
22053 Roo.each(b, function(tag) {
22054 if (w.indexOf(tag) > -1) {
22057 if (this.black.indexOf(tag) > -1) {
22060 this.black.push(tag);
22065 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22066 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22070 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22071 if (b.indexOf(tag) > -1) {
22074 this.cwhite.push(tag);
22078 Roo.each(w, function(tag) {
22079 if (b.indexOf(tag) > -1) {
22082 if (this.cwhite.indexOf(tag) > -1) {
22085 this.cwhite.push(tag);
22090 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22091 if (w.indexOf(tag) > -1) {
22094 this.cblack.push(tag);
22098 Roo.each(b, function(tag) {
22099 if (w.indexOf(tag) > -1) {
22102 if (this.cblack.indexOf(tag) > -1) {
22105 this.cblack.push(tag);
22110 setStylesheets : function(stylesheets)
22112 if(typeof(stylesheets) == 'string'){
22113 Roo.get(this.iframe.contentDocument.head).createChild({
22115 rel : 'stylesheet',
22124 Roo.each(stylesheets, function(s) {
22129 Roo.get(_this.iframe.contentDocument.head).createChild({
22131 rel : 'stylesheet',
22140 removeStylesheets : function()
22144 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22149 setStyle : function(style)
22151 Roo.get(this.iframe.contentDocument.head).createChild({
22160 // hide stuff that is not compatible
22174 * @event specialkey
22178 * @cfg {String} fieldClass @hide
22181 * @cfg {String} focusClass @hide
22184 * @cfg {String} autoCreate @hide
22187 * @cfg {String} inputType @hide
22190 * @cfg {String} invalidClass @hide
22193 * @cfg {String} invalidText @hide
22196 * @cfg {String} msgFx @hide
22199 * @cfg {String} validateOnBlur @hide
22203 Roo.HtmlEditorCore.white = [
22204 'area', 'br', 'img', 'input', 'hr', 'wbr',
22206 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22207 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22208 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22209 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22210 'table', 'ul', 'xmp',
22212 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22215 'dir', 'menu', 'ol', 'ul', 'dl',
22221 Roo.HtmlEditorCore.black = [
22222 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22224 'base', 'basefont', 'bgsound', 'blink', 'body',
22225 'frame', 'frameset', 'head', 'html', 'ilayer',
22226 'iframe', 'layer', 'link', 'meta', 'object',
22227 'script', 'style' ,'title', 'xml' // clean later..
22229 Roo.HtmlEditorCore.clean = [
22230 'script', 'style', 'title', 'xml'
22232 Roo.HtmlEditorCore.remove = [
22237 Roo.HtmlEditorCore.ablack = [
22241 Roo.HtmlEditorCore.aclean = [
22242 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22246 Roo.HtmlEditorCore.pwhite= [
22247 'http', 'https', 'mailto'
22250 // white listed style attributes.
22251 Roo.HtmlEditorCore.cwhite= [
22252 // 'text-align', /// default is to allow most things..
22258 // black listed style attributes.
22259 Roo.HtmlEditorCore.cblack= [
22260 // 'font-size' -- this can be set by the project
22264 Roo.HtmlEditorCore.swapCodes =[
22265 [ 8211, "–" ],
22266 [ 8212, "—" ],
22275 //<script type="text/javascript">
22278 * Ext JS Library 1.1.1
22279 * Copyright(c) 2006-2007, Ext JS, LLC.
22285 Roo.form.HtmlEditor = function(config){
22289 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22291 if (!this.toolbars) {
22292 this.toolbars = [];
22294 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22300 * @class Roo.form.HtmlEditor
22301 * @extends Roo.form.Field
22302 * Provides a lightweight HTML Editor component.
22304 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22306 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22307 * supported by this editor.</b><br/><br/>
22308 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22309 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22311 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22313 * @cfg {Boolean} clearUp
22317 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22322 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22327 * @cfg {Number} height (in pixels)
22331 * @cfg {Number} width (in pixels)
22336 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22339 stylesheets: false,
22343 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22348 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22354 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22359 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22364 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22366 allowComments: false,
22371 // private properties
22372 validationEvent : false,
22374 initialized : false,
22377 onFocus : Roo.emptyFn,
22379 hideMode:'offsets',
22381 actionMode : 'container', // defaults to hiding it...
22383 defaultAutoCreate : { // modified by initCompnoent..
22385 style:"width:500px;height:300px;",
22386 autocomplete: "new-password"
22390 initComponent : function(){
22393 * @event initialize
22394 * Fires when the editor is fully initialized (including the iframe)
22395 * @param {HtmlEditor} this
22400 * Fires when the editor is first receives the focus. Any insertion must wait
22401 * until after this event.
22402 * @param {HtmlEditor} this
22406 * @event beforesync
22407 * Fires before the textarea is updated with content from the editor iframe. Return false
22408 * to cancel the sync.
22409 * @param {HtmlEditor} this
22410 * @param {String} html
22414 * @event beforepush
22415 * Fires before the iframe editor is updated with content from the textarea. Return false
22416 * to cancel the push.
22417 * @param {HtmlEditor} this
22418 * @param {String} html
22423 * Fires when the textarea is updated with content from the editor iframe.
22424 * @param {HtmlEditor} this
22425 * @param {String} html
22430 * Fires when the iframe editor is updated with content from the textarea.
22431 * @param {HtmlEditor} this
22432 * @param {String} html
22436 * @event editmodechange
22437 * Fires when the editor switches edit modes
22438 * @param {HtmlEditor} this
22439 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22441 editmodechange: true,
22443 * @event editorevent
22444 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22445 * @param {HtmlEditor} this
22449 * @event firstfocus
22450 * Fires when on first focus - needed by toolbars..
22451 * @param {HtmlEditor} this
22456 * Auto save the htmlEditor value as a file into Events
22457 * @param {HtmlEditor} this
22461 * @event savedpreview
22462 * preview the saved version of htmlEditor
22463 * @param {HtmlEditor} this
22465 savedpreview: true,
22468 * @event stylesheetsclick
22469 * Fires when press the Sytlesheets button
22470 * @param {Roo.HtmlEditorCore} this
22472 stylesheetsclick: true
22474 this.defaultAutoCreate = {
22476 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22477 autocomplete: "new-password"
22482 * Protected method that will not generally be called directly. It
22483 * is called when the editor creates its toolbar. Override this method if you need to
22484 * add custom toolbar buttons.
22485 * @param {HtmlEditor} editor
22487 createToolbar : function(editor){
22488 Roo.log("create toolbars");
22489 if (!editor.toolbars || !editor.toolbars.length) {
22490 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22493 for (var i =0 ; i < editor.toolbars.length;i++) {
22494 editor.toolbars[i] = Roo.factory(
22495 typeof(editor.toolbars[i]) == 'string' ?
22496 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22497 Roo.form.HtmlEditor);
22498 editor.toolbars[i].init(editor);
22506 onRender : function(ct, position)
22509 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22511 this.wrap = this.el.wrap({
22512 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22515 this.editorcore.onRender(ct, position);
22517 if (this.resizable) {
22518 this.resizeEl = new Roo.Resizable(this.wrap, {
22522 minHeight : this.height,
22523 height: this.height,
22524 handles : this.resizable,
22527 resize : function(r, w, h) {
22528 _t.onResize(w,h); // -something
22534 this.createToolbar(this);
22538 this.setSize(this.wrap.getSize());
22540 if (this.resizeEl) {
22541 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22542 // should trigger onReize..
22545 this.keyNav = new Roo.KeyNav(this.el, {
22547 "tab" : function(e){
22548 e.preventDefault();
22550 var value = this.getValue();
22552 var start = this.el.dom.selectionStart;
22553 var end = this.el.dom.selectionEnd;
22557 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22558 this.el.dom.setSelectionRange(end + 1, end + 1);
22562 var f = value.substring(0, start).split("\t");
22564 if(f.pop().length != 0){
22568 this.setValue(f.join("\t") + value.substring(end));
22569 this.el.dom.setSelectionRange(start - 1, start - 1);
22573 "home" : function(e){
22574 e.preventDefault();
22576 var curr = this.el.dom.selectionStart;
22577 var lines = this.getValue().split("\n");
22584 this.el.dom.setSelectionRange(0, 0);
22590 for (var i = 0; i < lines.length;i++) {
22591 pos += lines[i].length;
22601 pos -= lines[i].length;
22607 this.el.dom.setSelectionRange(pos, pos);
22611 this.el.dom.selectionStart = pos;
22612 this.el.dom.selectionEnd = curr;
22615 "end" : function(e){
22616 e.preventDefault();
22618 var curr = this.el.dom.selectionStart;
22619 var lines = this.getValue().split("\n");
22626 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22632 for (var i = 0; i < lines.length;i++) {
22634 pos += lines[i].length;
22648 this.el.dom.setSelectionRange(pos, pos);
22652 this.el.dom.selectionStart = curr;
22653 this.el.dom.selectionEnd = pos;
22658 doRelay : function(foo, bar, hname){
22659 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22665 // if(this.autosave && this.w){
22666 // this.autoSaveFn = setInterval(this.autosave, 1000);
22671 onResize : function(w, h)
22673 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22678 if(typeof w == 'number'){
22679 var aw = w - this.wrap.getFrameWidth('lr');
22680 this.el.setWidth(this.adjustWidth('textarea', aw));
22683 if(typeof h == 'number'){
22685 for (var i =0; i < this.toolbars.length;i++) {
22686 // fixme - ask toolbars for heights?
22687 tbh += this.toolbars[i].tb.el.getHeight();
22688 if (this.toolbars[i].footer) {
22689 tbh += this.toolbars[i].footer.el.getHeight();
22696 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22697 ah -= 5; // knock a few pixes off for look..
22699 this.el.setHeight(this.adjustWidth('textarea', ah));
22703 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22704 this.editorcore.onResize(ew,eh);
22709 * Toggles the editor between standard and source edit mode.
22710 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22712 toggleSourceEdit : function(sourceEditMode)
22714 this.editorcore.toggleSourceEdit(sourceEditMode);
22716 if(this.editorcore.sourceEditMode){
22717 Roo.log('editor - showing textarea');
22720 // Roo.log(this.syncValue());
22721 this.editorcore.syncValue();
22722 this.el.removeClass('x-hidden');
22723 this.el.dom.removeAttribute('tabIndex');
22726 for (var i = 0; i < this.toolbars.length; i++) {
22727 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22728 this.toolbars[i].tb.hide();
22729 this.toolbars[i].footer.hide();
22734 Roo.log('editor - hiding textarea');
22736 // Roo.log(this.pushValue());
22737 this.editorcore.pushValue();
22739 this.el.addClass('x-hidden');
22740 this.el.dom.setAttribute('tabIndex', -1);
22742 for (var i = 0; i < this.toolbars.length; i++) {
22743 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22744 this.toolbars[i].tb.show();
22745 this.toolbars[i].footer.show();
22749 //this.deferFocus();
22752 this.setSize(this.wrap.getSize());
22753 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22755 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22758 // private (for BoxComponent)
22759 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22761 // private (for BoxComponent)
22762 getResizeEl : function(){
22766 // private (for BoxComponent)
22767 getPositionEl : function(){
22772 initEvents : function(){
22773 this.originalValue = this.getValue();
22777 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22780 markInvalid : Roo.emptyFn,
22782 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22785 clearInvalid : Roo.emptyFn,
22787 setValue : function(v){
22788 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22789 this.editorcore.pushValue();
22794 deferFocus : function(){
22795 this.focus.defer(10, this);
22799 focus : function(){
22800 this.editorcore.focus();
22806 onDestroy : function(){
22812 for (var i =0; i < this.toolbars.length;i++) {
22813 // fixme - ask toolbars for heights?
22814 this.toolbars[i].onDestroy();
22817 this.wrap.dom.innerHTML = '';
22818 this.wrap.remove();
22823 onFirstFocus : function(){
22824 //Roo.log("onFirstFocus");
22825 this.editorcore.onFirstFocus();
22826 for (var i =0; i < this.toolbars.length;i++) {
22827 this.toolbars[i].onFirstFocus();
22833 syncValue : function()
22835 this.editorcore.syncValue();
22838 pushValue : function()
22840 this.editorcore.pushValue();
22843 setStylesheets : function(stylesheets)
22845 this.editorcore.setStylesheets(stylesheets);
22848 removeStylesheets : function()
22850 this.editorcore.removeStylesheets();
22854 // hide stuff that is not compatible
22868 * @event specialkey
22872 * @cfg {String} fieldClass @hide
22875 * @cfg {String} focusClass @hide
22878 * @cfg {String} autoCreate @hide
22881 * @cfg {String} inputType @hide
22884 * @cfg {String} invalidClass @hide
22887 * @cfg {String} invalidText @hide
22890 * @cfg {String} msgFx @hide
22893 * @cfg {String} validateOnBlur @hide
22897 // <script type="text/javascript">
22900 * Ext JS Library 1.1.1
22901 * Copyright(c) 2006-2007, Ext JS, LLC.
22907 * @class Roo.form.HtmlEditorToolbar1
22912 new Roo.form.HtmlEditor({
22915 new Roo.form.HtmlEditorToolbar1({
22916 disable : { fonts: 1 , format: 1, ..., ... , ...],
22922 * @cfg {Object} disable List of elements to disable..
22923 * @cfg {Array} btns List of additional buttons.
22927 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22930 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22933 Roo.apply(this, config);
22935 // default disabled, based on 'good practice'..
22936 this.disable = this.disable || {};
22937 Roo.applyIf(this.disable, {
22940 specialElements : true
22944 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22945 // dont call parent... till later.
22948 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
22955 editorcore : false,
22957 * @cfg {Object} disable List of toolbar elements to disable
22964 * @cfg {String} createLinkText The default text for the create link prompt
22966 createLinkText : 'Please enter the URL for the link:',
22968 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22970 defaultLinkValue : 'http:/'+'/',
22974 * @cfg {Array} fontFamilies An array of available font families
22992 // "á" , ?? a acute?
22997 "°" // , // degrees
22999 // "é" , // e ecute
23000 // "ú" , // u ecute?
23003 specialElements : [
23005 text: "Insert Table",
23008 ihtml : '<table><tr><td>Cell</td></tr></table>'
23012 text: "Insert Image",
23015 ihtml : '<img src="about:blank"/>'
23024 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
23025 "input:submit", "input:button", "select", "textarea", "label" ],
23028 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
23030 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23039 * @cfg {String} defaultFont default font to use.
23041 defaultFont: 'tahoma',
23043 fontSelect : false,
23046 formatCombo : false,
23048 init : function(editor)
23050 this.editor = editor;
23051 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23052 var editorcore = this.editorcore;
23056 var fid = editorcore.frameId;
23058 function btn(id, toggle, handler){
23059 var xid = fid + '-'+ id ;
23063 cls : 'x-btn-icon x-edit-'+id,
23064 enableToggle:toggle !== false,
23065 scope: _t, // was editor...
23066 handler:handler||_t.relayBtnCmd,
23067 clickEvent:'mousedown',
23068 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23075 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23077 // stop form submits
23078 tb.el.on('click', function(e){
23079 e.preventDefault(); // what does this do?
23082 if(!this.disable.font) { // && !Roo.isSafari){
23083 /* why no safari for fonts
23084 editor.fontSelect = tb.el.createChild({
23087 cls:'x-font-select',
23088 html: this.createFontOptions()
23091 editor.fontSelect.on('change', function(){
23092 var font = editor.fontSelect.dom.value;
23093 editor.relayCmd('fontname', font);
23094 editor.deferFocus();
23098 editor.fontSelect.dom,
23104 if(!this.disable.formats){
23105 this.formatCombo = new Roo.form.ComboBox({
23106 store: new Roo.data.SimpleStore({
23109 data : this.formats // from states.js
23113 //autoCreate : {tag: "div", size: "20"},
23114 displayField:'tag',
23118 triggerAction: 'all',
23119 emptyText:'Add tag',
23120 selectOnFocus:true,
23123 'select': function(c, r, i) {
23124 editorcore.insertTag(r.get('tag'));
23130 tb.addField(this.formatCombo);
23134 if(!this.disable.format){
23139 btn('strikethrough')
23142 if(!this.disable.fontSize){
23147 btn('increasefontsize', false, editorcore.adjustFont),
23148 btn('decreasefontsize', false, editorcore.adjustFont)
23153 if(!this.disable.colors){
23156 id:editorcore.frameId +'-forecolor',
23157 cls:'x-btn-icon x-edit-forecolor',
23158 clickEvent:'mousedown',
23159 tooltip: this.buttonTips['forecolor'] || undefined,
23161 menu : new Roo.menu.ColorMenu({
23162 allowReselect: true,
23163 focus: Roo.emptyFn,
23166 selectHandler: function(cp, color){
23167 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23168 editor.deferFocus();
23171 clickEvent:'mousedown'
23174 id:editorcore.frameId +'backcolor',
23175 cls:'x-btn-icon x-edit-backcolor',
23176 clickEvent:'mousedown',
23177 tooltip: this.buttonTips['backcolor'] || undefined,
23179 menu : new Roo.menu.ColorMenu({
23180 focus: Roo.emptyFn,
23183 allowReselect: true,
23184 selectHandler: function(cp, color){
23186 editorcore.execCmd('useCSS', false);
23187 editorcore.execCmd('hilitecolor', color);
23188 editorcore.execCmd('useCSS', true);
23189 editor.deferFocus();
23191 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23192 Roo.isSafari || Roo.isIE ? '#'+color : color);
23193 editor.deferFocus();
23197 clickEvent:'mousedown'
23202 // now add all the items...
23205 if(!this.disable.alignments){
23208 btn('justifyleft'),
23209 btn('justifycenter'),
23210 btn('justifyright')
23214 //if(!Roo.isSafari){
23215 if(!this.disable.links){
23218 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23222 if(!this.disable.lists){
23225 btn('insertorderedlist'),
23226 btn('insertunorderedlist')
23229 if(!this.disable.sourceEdit){
23232 btn('sourceedit', true, function(btn){
23233 this.toggleSourceEdit(btn.pressed);
23240 // special menu.. - needs to be tidied up..
23241 if (!this.disable.special) {
23244 cls: 'x-edit-none',
23250 for (var i =0; i < this.specialChars.length; i++) {
23251 smenu.menu.items.push({
23253 html: this.specialChars[i],
23254 handler: function(a,b) {
23255 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23256 //editor.insertAtCursor(a.html);
23270 if (!this.disable.cleanStyles) {
23272 cls: 'x-btn-icon x-btn-clear',
23278 for (var i =0; i < this.cleanStyles.length; i++) {
23279 cmenu.menu.items.push({
23280 actiontype : this.cleanStyles[i],
23281 html: 'Remove ' + this.cleanStyles[i],
23282 handler: function(a,b) {
23285 var c = Roo.get(editorcore.doc.body);
23286 c.select('[style]').each(function(s) {
23287 s.dom.style.removeProperty(a.actiontype);
23289 editorcore.syncValue();
23294 cmenu.menu.items.push({
23295 actiontype : 'tablewidths',
23296 html: 'Remove Table Widths',
23297 handler: function(a,b) {
23298 editorcore.cleanTableWidths();
23299 editorcore.syncValue();
23303 cmenu.menu.items.push({
23304 actiontype : 'word',
23305 html: 'Remove MS Word Formating',
23306 handler: function(a,b) {
23307 editorcore.cleanWord();
23308 editorcore.syncValue();
23313 cmenu.menu.items.push({
23314 actiontype : 'all',
23315 html: 'Remove All Styles',
23316 handler: function(a,b) {
23318 var c = Roo.get(editorcore.doc.body);
23319 c.select('[style]').each(function(s) {
23320 s.dom.removeAttribute('style');
23322 editorcore.syncValue();
23327 cmenu.menu.items.push({
23328 actiontype : 'all',
23329 html: 'Remove All CSS Classes',
23330 handler: function(a,b) {
23332 var c = Roo.get(editorcore.doc.body);
23333 c.select('[class]').each(function(s) {
23334 s.dom.removeAttribute('class');
23336 editorcore.cleanWord();
23337 editorcore.syncValue();
23342 cmenu.menu.items.push({
23343 actiontype : 'tidy',
23344 html: 'Tidy HTML Source',
23345 handler: function(a,b) {
23346 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23347 editorcore.syncValue();
23356 if (!this.disable.specialElements) {
23359 cls: 'x-edit-none',
23364 for (var i =0; i < this.specialElements.length; i++) {
23365 semenu.menu.items.push(
23367 handler: function(a,b) {
23368 editor.insertAtCursor(this.ihtml);
23370 }, this.specialElements[i])
23382 for(var i =0; i< this.btns.length;i++) {
23383 var b = Roo.factory(this.btns[i],Roo.form);
23384 b.cls = 'x-edit-none';
23386 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23387 b.cls += ' x-init-enable';
23390 b.scope = editorcore;
23398 // disable everything...
23400 this.tb.items.each(function(item){
23403 item.id != editorcore.frameId+ '-sourceedit' &&
23404 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23410 this.rendered = true;
23412 // the all the btns;
23413 editor.on('editorevent', this.updateToolbar, this);
23414 // other toolbars need to implement this..
23415 //editor.on('editmodechange', this.updateToolbar, this);
23419 relayBtnCmd : function(btn) {
23420 this.editorcore.relayCmd(btn.cmd);
23422 // private used internally
23423 createLink : function(){
23424 Roo.log("create link?");
23425 var url = prompt(this.createLinkText, this.defaultLinkValue);
23426 if(url && url != 'http:/'+'/'){
23427 this.editorcore.relayCmd('createlink', url);
23433 * Protected method that will not generally be called directly. It triggers
23434 * a toolbar update by reading the markup state of the current selection in the editor.
23436 updateToolbar: function(){
23438 if(!this.editorcore.activated){
23439 this.editor.onFirstFocus();
23443 var btns = this.tb.items.map,
23444 doc = this.editorcore.doc,
23445 frameId = this.editorcore.frameId;
23447 if(!this.disable.font && !Roo.isSafari){
23449 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23450 if(name != this.fontSelect.dom.value){
23451 this.fontSelect.dom.value = name;
23455 if(!this.disable.format){
23456 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23457 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23458 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23459 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23461 if(!this.disable.alignments){
23462 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23463 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23464 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23466 if(!Roo.isSafari && !this.disable.lists){
23467 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23468 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23471 var ans = this.editorcore.getAllAncestors();
23472 if (this.formatCombo) {
23475 var store = this.formatCombo.store;
23476 this.formatCombo.setValue("");
23477 for (var i =0; i < ans.length;i++) {
23478 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23480 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23488 // hides menus... - so this cant be on a menu...
23489 Roo.menu.MenuMgr.hideAll();
23491 //this.editorsyncValue();
23495 createFontOptions : function(){
23496 var buf = [], fs = this.fontFamilies, ff, lc;
23500 for(var i = 0, len = fs.length; i< len; i++){
23502 lc = ff.toLowerCase();
23504 '<option value="',lc,'" style="font-family:',ff,';"',
23505 (this.defaultFont == lc ? ' selected="true">' : '>'),
23510 return buf.join('');
23513 toggleSourceEdit : function(sourceEditMode){
23515 Roo.log("toolbar toogle");
23516 if(sourceEditMode === undefined){
23517 sourceEditMode = !this.sourceEditMode;
23519 this.sourceEditMode = sourceEditMode === true;
23520 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23521 // just toggle the button?
23522 if(btn.pressed !== this.sourceEditMode){
23523 btn.toggle(this.sourceEditMode);
23527 if(sourceEditMode){
23528 Roo.log("disabling buttons");
23529 this.tb.items.each(function(item){
23530 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23536 Roo.log("enabling buttons");
23537 if(this.editorcore.initialized){
23538 this.tb.items.each(function(item){
23544 Roo.log("calling toggole on editor");
23545 // tell the editor that it's been pressed..
23546 this.editor.toggleSourceEdit(sourceEditMode);
23550 * Object collection of toolbar tooltips for the buttons in the editor. The key
23551 * is the command id associated with that button and the value is a valid QuickTips object.
23556 title: 'Bold (Ctrl+B)',
23557 text: 'Make the selected text bold.',
23558 cls: 'x-html-editor-tip'
23561 title: 'Italic (Ctrl+I)',
23562 text: 'Make the selected text italic.',
23563 cls: 'x-html-editor-tip'
23571 title: 'Bold (Ctrl+B)',
23572 text: 'Make the selected text bold.',
23573 cls: 'x-html-editor-tip'
23576 title: 'Italic (Ctrl+I)',
23577 text: 'Make the selected text italic.',
23578 cls: 'x-html-editor-tip'
23581 title: 'Underline (Ctrl+U)',
23582 text: 'Underline the selected text.',
23583 cls: 'x-html-editor-tip'
23586 title: 'Strikethrough',
23587 text: 'Strikethrough the selected text.',
23588 cls: 'x-html-editor-tip'
23590 increasefontsize : {
23591 title: 'Grow Text',
23592 text: 'Increase the font size.',
23593 cls: 'x-html-editor-tip'
23595 decreasefontsize : {
23596 title: 'Shrink Text',
23597 text: 'Decrease the font size.',
23598 cls: 'x-html-editor-tip'
23601 title: 'Text Highlight Color',
23602 text: 'Change the background color of the selected text.',
23603 cls: 'x-html-editor-tip'
23606 title: 'Font Color',
23607 text: 'Change the color of the selected text.',
23608 cls: 'x-html-editor-tip'
23611 title: 'Align Text Left',
23612 text: 'Align text to the left.',
23613 cls: 'x-html-editor-tip'
23616 title: 'Center Text',
23617 text: 'Center text in the editor.',
23618 cls: 'x-html-editor-tip'
23621 title: 'Align Text Right',
23622 text: 'Align text to the right.',
23623 cls: 'x-html-editor-tip'
23625 insertunorderedlist : {
23626 title: 'Bullet List',
23627 text: 'Start a bulleted list.',
23628 cls: 'x-html-editor-tip'
23630 insertorderedlist : {
23631 title: 'Numbered List',
23632 text: 'Start a numbered list.',
23633 cls: 'x-html-editor-tip'
23636 title: 'Hyperlink',
23637 text: 'Make the selected text a hyperlink.',
23638 cls: 'x-html-editor-tip'
23641 title: 'Source Edit',
23642 text: 'Switch to source editing mode.',
23643 cls: 'x-html-editor-tip'
23647 onDestroy : function(){
23650 this.tb.items.each(function(item){
23652 item.menu.removeAll();
23654 item.menu.el.destroy();
23662 onFirstFocus: function() {
23663 this.tb.items.each(function(item){
23672 // <script type="text/javascript">
23675 * Ext JS Library 1.1.1
23676 * Copyright(c) 2006-2007, Ext JS, LLC.
23683 * @class Roo.form.HtmlEditor.ToolbarContext
23688 new Roo.form.HtmlEditor({
23691 { xtype: 'ToolbarStandard', styles : {} }
23692 { xtype: 'ToolbarContext', disable : {} }
23698 * @config : {Object} disable List of elements to disable.. (not done yet.)
23699 * @config : {Object} styles Map of styles available.
23703 Roo.form.HtmlEditor.ToolbarContext = function(config)
23706 Roo.apply(this, config);
23707 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23708 // dont call parent... till later.
23709 this.styles = this.styles || {};
23714 Roo.form.HtmlEditor.ToolbarContext.types = {
23726 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23792 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23797 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23807 style : 'fontFamily',
23808 displayField: 'display',
23809 optname : 'font-family',
23858 // should we really allow this??
23859 // should this just be
23870 style : 'fontFamily',
23871 displayField: 'display',
23872 optname : 'font-family',
23879 style : 'fontFamily',
23880 displayField: 'display',
23881 optname : 'font-family',
23888 style : 'fontFamily',
23889 displayField: 'display',
23890 optname : 'font-family',
23901 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23902 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23904 Roo.form.HtmlEditor.ToolbarContext.options = {
23906 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23907 [ 'Courier New', 'Courier New'],
23908 [ 'Tahoma', 'Tahoma'],
23909 [ 'Times New Roman,serif', 'Times'],
23910 [ 'Verdana','Verdana' ]
23914 // fixme - these need to be configurable..
23917 //Roo.form.HtmlEditor.ToolbarContext.types
23920 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
23927 editorcore : false,
23929 * @cfg {Object} disable List of toolbar elements to disable
23934 * @cfg {Object} styles List of styles
23935 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
23937 * These must be defined in the page, so they get rendered correctly..
23948 init : function(editor)
23950 this.editor = editor;
23951 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23952 var editorcore = this.editorcore;
23954 var fid = editorcore.frameId;
23956 function btn(id, toggle, handler){
23957 var xid = fid + '-'+ id ;
23961 cls : 'x-btn-icon x-edit-'+id,
23962 enableToggle:toggle !== false,
23963 scope: editorcore, // was editor...
23964 handler:handler||editorcore.relayBtnCmd,
23965 clickEvent:'mousedown',
23966 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23970 // create a new element.
23971 var wdiv = editor.wrap.createChild({
23973 }, editor.wrap.dom.firstChild.nextSibling, true);
23975 // can we do this more than once??
23977 // stop form submits
23980 // disable everything...
23981 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23982 this.toolbars = {};
23984 for (var i in ty) {
23986 this.toolbars[i] = this.buildToolbar(ty[i],i);
23988 this.tb = this.toolbars.BODY;
23990 this.buildFooter();
23991 this.footer.show();
23992 editor.on('hide', function( ) { this.footer.hide() }, this);
23993 editor.on('show', function( ) { this.footer.show() }, this);
23996 this.rendered = true;
23998 // the all the btns;
23999 editor.on('editorevent', this.updateToolbar, this);
24000 // other toolbars need to implement this..
24001 //editor.on('editmodechange', this.updateToolbar, this);
24007 * Protected method that will not generally be called directly. It triggers
24008 * a toolbar update by reading the markup state of the current selection in the editor.
24010 * Note you can force an update by calling on('editorevent', scope, false)
24012 updateToolbar: function(editor,ev,sel){
24015 // capture mouse up - this is handy for selecting images..
24016 // perhaps should go somewhere else...
24017 if(!this.editorcore.activated){
24018 this.editor.onFirstFocus();
24024 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24025 // selectNode - might want to handle IE?
24027 (ev.type == 'mouseup' || ev.type == 'click' ) &&
24028 ev.target && ev.target.tagName == 'IMG') {
24029 // they have click on an image...
24030 // let's see if we can change the selection...
24033 var nodeRange = sel.ownerDocument.createRange();
24035 nodeRange.selectNode(sel);
24037 nodeRange.selectNodeContents(sel);
24039 //nodeRange.collapse(true);
24040 var s = this.editorcore.win.getSelection();
24041 s.removeAllRanges();
24042 s.addRange(nodeRange);
24046 var updateFooter = sel ? false : true;
24049 var ans = this.editorcore.getAllAncestors();
24052 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24055 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
24056 sel = sel ? sel : this.editorcore.doc.body;
24057 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24060 // pick a menu that exists..
24061 var tn = sel.tagName.toUpperCase();
24062 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24064 tn = sel.tagName.toUpperCase();
24066 var lastSel = this.tb.selectedNode;
24068 this.tb.selectedNode = sel;
24070 // if current menu does not match..
24072 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24075 ///console.log("show: " + tn);
24076 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24079 this.tb.items.first().el.innerHTML = tn + ': ';
24082 // update attributes
24083 if (this.tb.fields) {
24084 this.tb.fields.each(function(e) {
24086 e.setValue(sel.style[e.stylename]);
24089 e.setValue(sel.getAttribute(e.attrname));
24093 var hasStyles = false;
24094 for(var i in this.styles) {
24101 var st = this.tb.fields.item(0);
24103 st.store.removeAll();
24106 var cn = sel.className.split(/\s+/);
24109 if (this.styles['*']) {
24111 Roo.each(this.styles['*'], function(v) {
24112 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24115 if (this.styles[tn]) {
24116 Roo.each(this.styles[tn], function(v) {
24117 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24121 st.store.loadData(avs);
24125 // flag our selected Node.
24126 this.tb.selectedNode = sel;
24129 Roo.menu.MenuMgr.hideAll();
24133 if (!updateFooter) {
24134 //this.footDisp.dom.innerHTML = '';
24137 // update the footer
24141 this.footerEls = ans.reverse();
24142 Roo.each(this.footerEls, function(a,i) {
24143 if (!a) { return; }
24144 html += html.length ? ' > ' : '';
24146 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24151 var sz = this.footDisp.up('td').getSize();
24152 this.footDisp.dom.style.width = (sz.width -10) + 'px';
24153 this.footDisp.dom.style.marginLeft = '5px';
24155 this.footDisp.dom.style.overflow = 'hidden';
24157 this.footDisp.dom.innerHTML = html;
24159 //this.editorsyncValue();
24166 onDestroy : function(){
24169 this.tb.items.each(function(item){
24171 item.menu.removeAll();
24173 item.menu.el.destroy();
24181 onFirstFocus: function() {
24182 // need to do this for all the toolbars..
24183 this.tb.items.each(function(item){
24187 buildToolbar: function(tlist, nm)
24189 var editor = this.editor;
24190 var editorcore = this.editorcore;
24191 // create a new element.
24192 var wdiv = editor.wrap.createChild({
24194 }, editor.wrap.dom.firstChild.nextSibling, true);
24197 var tb = new Roo.Toolbar(wdiv);
24200 tb.add(nm+ ": ");
24203 for(var i in this.styles) {
24208 if (styles && styles.length) {
24210 // this needs a multi-select checkbox...
24211 tb.addField( new Roo.form.ComboBox({
24212 store: new Roo.data.SimpleStore({
24214 fields: ['val', 'selected'],
24217 name : '-roo-edit-className',
24218 attrname : 'className',
24219 displayField: 'val',
24223 triggerAction: 'all',
24224 emptyText:'Select Style',
24225 selectOnFocus:true,
24228 'select': function(c, r, i) {
24229 // initial support only for on class per el..
24230 tb.selectedNode.className = r ? r.get('val') : '';
24231 editorcore.syncValue();
24238 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24239 var tbops = tbc.options;
24241 for (var i in tlist) {
24243 var item = tlist[i];
24244 tb.add(item.title + ": ");
24247 //optname == used so you can configure the options available..
24248 var opts = item.opts ? item.opts : false;
24249 if (item.optname) {
24250 opts = tbops[item.optname];
24255 // opts == pulldown..
24256 tb.addField( new Roo.form.ComboBox({
24257 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24259 fields: ['val', 'display'],
24262 name : '-roo-edit-' + i,
24264 stylename : item.style ? item.style : false,
24265 displayField: item.displayField ? item.displayField : 'val',
24266 valueField : 'val',
24268 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24270 triggerAction: 'all',
24271 emptyText:'Select',
24272 selectOnFocus:true,
24273 width: item.width ? item.width : 130,
24275 'select': function(c, r, i) {
24277 tb.selectedNode.style[c.stylename] = r.get('val');
24280 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24289 tb.addField( new Roo.form.TextField({
24292 //allowBlank:false,
24297 tb.addField( new Roo.form.TextField({
24298 name: '-roo-edit-' + i,
24305 'change' : function(f, nv, ov) {
24306 tb.selectedNode.setAttribute(f.attrname, nv);
24307 editorcore.syncValue();
24320 text: 'Stylesheets',
24323 click : function ()
24325 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24333 text: 'Remove Tag',
24336 click : function ()
24339 // undo does not work.
24341 var sn = tb.selectedNode;
24343 var pn = sn.parentNode;
24345 var stn = sn.childNodes[0];
24346 var en = sn.childNodes[sn.childNodes.length - 1 ];
24347 while (sn.childNodes.length) {
24348 var node = sn.childNodes[0];
24349 sn.removeChild(node);
24351 pn.insertBefore(node, sn);
24354 pn.removeChild(sn);
24355 var range = editorcore.createRange();
24357 range.setStart(stn,0);
24358 range.setEnd(en,0); //????
24359 //range.selectNode(sel);
24362 var selection = editorcore.getSelection();
24363 selection.removeAllRanges();
24364 selection.addRange(range);
24368 //_this.updateToolbar(null, null, pn);
24369 _this.updateToolbar(null, null, null);
24370 _this.footDisp.dom.innerHTML = '';
24380 tb.el.on('click', function(e){
24381 e.preventDefault(); // what does this do?
24383 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24386 // dont need to disable them... as they will get hidden
24391 buildFooter : function()
24394 var fel = this.editor.wrap.createChild();
24395 this.footer = new Roo.Toolbar(fel);
24396 // toolbar has scrolly on left / right?
24397 var footDisp= new Roo.Toolbar.Fill();
24403 handler : function() {
24404 _t.footDisp.scrollTo('left',0,true)
24408 this.footer.add( footDisp );
24413 handler : function() {
24415 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24419 var fel = Roo.get(footDisp.el);
24420 fel.addClass('x-editor-context');
24421 this.footDispWrap = fel;
24422 this.footDispWrap.overflow = 'hidden';
24424 this.footDisp = fel.createChild();
24425 this.footDispWrap.on('click', this.onContextClick, this)
24429 onContextClick : function (ev,dom)
24431 ev.preventDefault();
24432 var cn = dom.className;
24434 if (!cn.match(/x-ed-loc-/)) {
24437 var n = cn.split('-').pop();
24438 var ans = this.footerEls;
24442 var range = this.editorcore.createRange();
24444 range.selectNodeContents(sel);
24445 //range.selectNode(sel);
24448 var selection = this.editorcore.getSelection();
24449 selection.removeAllRanges();
24450 selection.addRange(range);
24454 this.updateToolbar(null, null, sel);
24471 * Ext JS Library 1.1.1
24472 * Copyright(c) 2006-2007, Ext JS, LLC.
24474 * Originally Released Under LGPL - original licence link has changed is not relivant.
24477 * <script type="text/javascript">
24481 * @class Roo.form.BasicForm
24482 * @extends Roo.util.Observable
24483 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24485 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24486 * @param {Object} config Configuration options
24488 Roo.form.BasicForm = function(el, config){
24489 this.allItems = [];
24490 this.childForms = [];
24491 Roo.apply(this, config);
24493 * The Roo.form.Field items in this form.
24494 * @type MixedCollection
24498 this.items = new Roo.util.MixedCollection(false, function(o){
24499 return o.id || (o.id = Roo.id());
24503 * @event beforeaction
24504 * Fires before any action is performed. Return false to cancel the action.
24505 * @param {Form} this
24506 * @param {Action} action The action to be performed
24508 beforeaction: true,
24510 * @event actionfailed
24511 * Fires when an action fails.
24512 * @param {Form} this
24513 * @param {Action} action The action that failed
24515 actionfailed : true,
24517 * @event actioncomplete
24518 * Fires when an action is completed.
24519 * @param {Form} this
24520 * @param {Action} action The action that completed
24522 actioncomplete : true
24527 Roo.form.BasicForm.superclass.constructor.call(this);
24529 Roo.form.BasicForm.popover.apply();
24532 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24534 * @cfg {String} method
24535 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24538 * @cfg {DataReader} reader
24539 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24540 * This is optional as there is built-in support for processing JSON.
24543 * @cfg {DataReader} errorReader
24544 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24545 * This is completely optional as there is built-in support for processing JSON.
24548 * @cfg {String} url
24549 * The URL to use for form actions if one isn't supplied in the action options.
24552 * @cfg {Boolean} fileUpload
24553 * Set to true if this form is a file upload.
24557 * @cfg {Object} baseParams
24558 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24563 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24568 activeAction : null,
24571 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24572 * or setValues() data instead of when the form was first created.
24574 trackResetOnLoad : false,
24578 * childForms - used for multi-tab forms
24581 childForms : false,
24584 * allItems - full list of fields.
24590 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24591 * element by passing it or its id or mask the form itself by passing in true.
24594 waitMsgTarget : false,
24599 disableMask : false,
24602 * @cfg {Boolean} errorMask (true|false) default false
24607 * @cfg {Number} maskOffset Default 100
24612 initEl : function(el){
24613 this.el = Roo.get(el);
24614 this.id = this.el.id || Roo.id();
24615 this.el.on('submit', this.onSubmit, this);
24616 this.el.addClass('x-form');
24620 onSubmit : function(e){
24625 * Returns true if client-side validation on the form is successful.
24628 isValid : function(){
24630 var target = false;
24631 this.items.each(function(f){
24638 if(!target && f.el.isVisible(true)){
24643 if(this.errorMask && !valid){
24644 Roo.form.BasicForm.popover.mask(this, target);
24650 * Returns array of invalid form fields.
24654 invalidFields : function()
24657 this.items.each(function(f){
24670 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24673 isDirty : function(){
24675 this.items.each(function(f){
24685 * Returns true if any fields in this form have changed since their original load. (New version)
24689 hasChanged : function()
24692 this.items.each(function(f){
24693 if(f.hasChanged()){
24702 * Resets all hasChanged to 'false' -
24703 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24704 * So hasChanged storage is only to be used for this purpose
24707 resetHasChanged : function()
24709 this.items.each(function(f){
24710 f.resetHasChanged();
24717 * Performs a predefined action (submit or load) or custom actions you define on this form.
24718 * @param {String} actionName The name of the action type
24719 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24720 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24721 * accept other config options):
24723 Property Type Description
24724 ---------------- --------------- ----------------------------------------------------------------------------------
24725 url String The url for the action (defaults to the form's url)
24726 method String The form method to use (defaults to the form's method, or POST if not defined)
24727 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
24728 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
24729 validate the form on the client (defaults to false)
24731 * @return {BasicForm} this
24733 doAction : function(action, options){
24734 if(typeof action == 'string'){
24735 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24737 if(this.fireEvent('beforeaction', this, action) !== false){
24738 this.beforeAction(action);
24739 action.run.defer(100, action);
24745 * Shortcut to do a submit action.
24746 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24747 * @return {BasicForm} this
24749 submit : function(options){
24750 this.doAction('submit', options);
24755 * Shortcut to do a load action.
24756 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24757 * @return {BasicForm} this
24759 load : function(options){
24760 this.doAction('load', options);
24765 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24766 * @param {Record} record The record to edit
24767 * @return {BasicForm} this
24769 updateRecord : function(record){
24770 record.beginEdit();
24771 var fs = record.fields;
24772 fs.each(function(f){
24773 var field = this.findField(f.name);
24775 record.set(f.name, field.getValue());
24783 * Loads an Roo.data.Record into this form.
24784 * @param {Record} record The record to load
24785 * @return {BasicForm} this
24787 loadRecord : function(record){
24788 this.setValues(record.data);
24793 beforeAction : function(action){
24794 var o = action.options;
24796 if(!this.disableMask) {
24797 if(this.waitMsgTarget === true){
24798 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24799 }else if(this.waitMsgTarget){
24800 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24801 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24803 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24811 afterAction : function(action, success){
24812 this.activeAction = null;
24813 var o = action.options;
24815 if(!this.disableMask) {
24816 if(this.waitMsgTarget === true){
24818 }else if(this.waitMsgTarget){
24819 this.waitMsgTarget.unmask();
24821 Roo.MessageBox.updateProgress(1);
24822 Roo.MessageBox.hide();
24830 Roo.callback(o.success, o.scope, [this, action]);
24831 this.fireEvent('actioncomplete', this, action);
24835 // failure condition..
24836 // we have a scenario where updates need confirming.
24837 // eg. if a locking scenario exists..
24838 // we look for { errors : { needs_confirm : true }} in the response.
24840 (typeof(action.result) != 'undefined') &&
24841 (typeof(action.result.errors) != 'undefined') &&
24842 (typeof(action.result.errors.needs_confirm) != 'undefined')
24845 Roo.MessageBox.confirm(
24846 "Change requires confirmation",
24847 action.result.errorMsg,
24852 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
24862 Roo.callback(o.failure, o.scope, [this, action]);
24863 // show an error message if no failed handler is set..
24864 if (!this.hasListener('actionfailed')) {
24865 Roo.MessageBox.alert("Error",
24866 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24867 action.result.errorMsg :
24868 "Saving Failed, please check your entries or try again"
24872 this.fireEvent('actionfailed', this, action);
24878 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24879 * @param {String} id The value to search for
24882 findField : function(id){
24883 var field = this.items.get(id);
24885 this.items.each(function(f){
24886 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24892 return field || null;
24896 * Add a secondary form to this one,
24897 * Used to provide tabbed forms. One form is primary, with hidden values
24898 * which mirror the elements from the other forms.
24900 * @param {Roo.form.Form} form to add.
24903 addForm : function(form)
24906 if (this.childForms.indexOf(form) > -1) {
24910 this.childForms.push(form);
24912 Roo.each(form.allItems, function (fe) {
24914 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24915 if (this.findField(n)) { // already added..
24918 var add = new Roo.form.Hidden({
24921 add.render(this.el);
24928 * Mark fields in this form invalid in bulk.
24929 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24930 * @return {BasicForm} this
24932 markInvalid : function(errors){
24933 if(errors instanceof Array){
24934 for(var i = 0, len = errors.length; i < len; i++){
24935 var fieldError = errors[i];
24936 var f = this.findField(fieldError.id);
24938 f.markInvalid(fieldError.msg);
24944 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24945 field.markInvalid(errors[id]);
24949 Roo.each(this.childForms || [], function (f) {
24950 f.markInvalid(errors);
24957 * Set values for fields in this form in bulk.
24958 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24959 * @return {BasicForm} this
24961 setValues : function(values){
24962 if(values instanceof Array){ // array of objects
24963 for(var i = 0, len = values.length; i < len; i++){
24965 var f = this.findField(v.id);
24967 f.setValue(v.value);
24968 if(this.trackResetOnLoad){
24969 f.originalValue = f.getValue();
24973 }else{ // object hash
24976 if(typeof values[id] != 'function' && (field = this.findField(id))){
24978 if (field.setFromData &&
24979 field.valueField &&
24980 field.displayField &&
24981 // combos' with local stores can
24982 // be queried via setValue()
24983 // to set their value..
24984 (field.store && !field.store.isLocal)
24988 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24989 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24990 field.setFromData(sd);
24993 field.setValue(values[id]);
24997 if(this.trackResetOnLoad){
24998 field.originalValue = field.getValue();
25003 this.resetHasChanged();
25006 Roo.each(this.childForms || [], function (f) {
25007 f.setValues(values);
25008 f.resetHasChanged();
25015 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25016 * they are returned as an array.
25017 * @param {Boolean} asString
25020 getValues : function(asString){
25021 if (this.childForms) {
25022 // copy values from the child forms
25023 Roo.each(this.childForms, function (f) {
25024 this.setValues(f.getValues());
25029 if (typeof(FormData) != 'undefined' && asString !== true) {
25030 // this relies on a 'recent' version of chrome apparently...
25032 var fd = (new FormData(this.el.dom)).entries();
25034 var ent = fd.next();
25035 while (!ent.done) {
25036 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25047 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25048 if(asString === true){
25051 return Roo.urlDecode(fs);
25055 * Returns the fields in this form as an object with key/value pairs.
25056 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25059 getFieldValues : function(with_hidden)
25061 if (this.childForms) {
25062 // copy values from the child forms
25063 // should this call getFieldValues - probably not as we do not currently copy
25064 // hidden fields when we generate..
25065 Roo.each(this.childForms, function (f) {
25066 this.setValues(f.getValues());
25071 this.items.each(function(f){
25072 if (!f.getName()) {
25075 var v = f.getValue();
25076 if (f.inputType =='radio') {
25077 if (typeof(ret[f.getName()]) == 'undefined') {
25078 ret[f.getName()] = ''; // empty..
25081 if (!f.el.dom.checked) {
25085 v = f.el.dom.value;
25089 // not sure if this supported any more..
25090 if ((typeof(v) == 'object') && f.getRawValue) {
25091 v = f.getRawValue() ; // dates..
25093 // combo boxes where name != hiddenName...
25094 if (f.name != f.getName()) {
25095 ret[f.name] = f.getRawValue();
25097 ret[f.getName()] = v;
25104 * Clears all invalid messages in this form.
25105 * @return {BasicForm} this
25107 clearInvalid : function(){
25108 this.items.each(function(f){
25112 Roo.each(this.childForms || [], function (f) {
25121 * Resets this form.
25122 * @return {BasicForm} this
25124 reset : function(){
25125 this.items.each(function(f){
25129 Roo.each(this.childForms || [], function (f) {
25132 this.resetHasChanged();
25138 * Add Roo.form components to this form.
25139 * @param {Field} field1
25140 * @param {Field} field2 (optional)
25141 * @param {Field} etc (optional)
25142 * @return {BasicForm} this
25145 this.items.addAll(Array.prototype.slice.call(arguments, 0));
25151 * Removes a field from the items collection (does NOT remove its markup).
25152 * @param {Field} field
25153 * @return {BasicForm} this
25155 remove : function(field){
25156 this.items.remove(field);
25161 * Looks at the fields in this form, checks them for an id attribute,
25162 * and calls applyTo on the existing dom element with that id.
25163 * @return {BasicForm} this
25165 render : function(){
25166 this.items.each(function(f){
25167 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25175 * Calls {@link Ext#apply} for all fields in this form with the passed object.
25176 * @param {Object} values
25177 * @return {BasicForm} this
25179 applyToFields : function(o){
25180 this.items.each(function(f){
25187 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25188 * @param {Object} values
25189 * @return {BasicForm} this
25191 applyIfToFields : function(o){
25192 this.items.each(function(f){
25200 Roo.BasicForm = Roo.form.BasicForm;
25202 Roo.apply(Roo.form.BasicForm, {
25216 intervalID : false,
25222 if(this.isApplied){
25227 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25228 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25229 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25230 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25233 this.maskEl.top.enableDisplayMode("block");
25234 this.maskEl.left.enableDisplayMode("block");
25235 this.maskEl.bottom.enableDisplayMode("block");
25236 this.maskEl.right.enableDisplayMode("block");
25238 Roo.get(document.body).on('click', function(){
25242 Roo.get(document.body).on('touchstart', function(){
25246 this.isApplied = true
25249 mask : function(form, target)
25253 this.target = target;
25255 if(!this.form.errorMask || !target.el){
25259 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25261 var ot = this.target.el.calcOffsetsTo(scrollable);
25263 var scrollTo = ot[1] - this.form.maskOffset;
25265 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25267 scrollable.scrollTo('top', scrollTo);
25269 var el = this.target.wrap || this.target.el;
25271 var box = el.getBox();
25273 this.maskEl.top.setStyle('position', 'absolute');
25274 this.maskEl.top.setStyle('z-index', 10000);
25275 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25276 this.maskEl.top.setLeft(0);
25277 this.maskEl.top.setTop(0);
25278 this.maskEl.top.show();
25280 this.maskEl.left.setStyle('position', 'absolute');
25281 this.maskEl.left.setStyle('z-index', 10000);
25282 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25283 this.maskEl.left.setLeft(0);
25284 this.maskEl.left.setTop(box.y - this.padding);
25285 this.maskEl.left.show();
25287 this.maskEl.bottom.setStyle('position', 'absolute');
25288 this.maskEl.bottom.setStyle('z-index', 10000);
25289 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25290 this.maskEl.bottom.setLeft(0);
25291 this.maskEl.bottom.setTop(box.bottom + this.padding);
25292 this.maskEl.bottom.show();
25294 this.maskEl.right.setStyle('position', 'absolute');
25295 this.maskEl.right.setStyle('z-index', 10000);
25296 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25297 this.maskEl.right.setLeft(box.right + this.padding);
25298 this.maskEl.right.setTop(box.y - this.padding);
25299 this.maskEl.right.show();
25301 this.intervalID = window.setInterval(function() {
25302 Roo.form.BasicForm.popover.unmask();
25305 window.onwheel = function(){ return false;};
25307 (function(){ this.isMasked = true; }).defer(500, this);
25311 unmask : function()
25313 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25317 this.maskEl.top.setStyle('position', 'absolute');
25318 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25319 this.maskEl.top.hide();
25321 this.maskEl.left.setStyle('position', 'absolute');
25322 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25323 this.maskEl.left.hide();
25325 this.maskEl.bottom.setStyle('position', 'absolute');
25326 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25327 this.maskEl.bottom.hide();
25329 this.maskEl.right.setStyle('position', 'absolute');
25330 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25331 this.maskEl.right.hide();
25333 window.onwheel = function(){ return true;};
25335 if(this.intervalID){
25336 window.clearInterval(this.intervalID);
25337 this.intervalID = false;
25340 this.isMasked = false;
25348 * Ext JS Library 1.1.1
25349 * Copyright(c) 2006-2007, Ext JS, LLC.
25351 * Originally Released Under LGPL - original licence link has changed is not relivant.
25354 * <script type="text/javascript">
25358 * @class Roo.form.Form
25359 * @extends Roo.form.BasicForm
25360 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25361 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25363 * @param {Object} config Configuration options
25365 Roo.form.Form = function(config){
25367 if (config.items) {
25368 xitems = config.items;
25369 delete config.items;
25373 Roo.form.Form.superclass.constructor.call(this, null, config);
25374 this.url = this.url || this.action;
25376 this.root = new Roo.form.Layout(Roo.applyIf({
25380 this.active = this.root;
25382 * Array of all the buttons that have been added to this form via {@link addButton}
25386 this.allItems = [];
25389 * @event clientvalidation
25390 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25391 * @param {Form} this
25392 * @param {Boolean} valid true if the form has passed client-side validation
25394 clientvalidation: true,
25397 * Fires when the form is rendered
25398 * @param {Roo.form.Form} form
25403 if (this.progressUrl) {
25404 // push a hidden field onto the list of fields..
25408 name : 'UPLOAD_IDENTIFIER'
25413 Roo.each(xitems, this.addxtype, this);
25417 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25419 * @cfg {Roo.Button} buttons[] buttons at bottom of form
25423 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25426 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25429 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25431 buttonAlign:'center',
25434 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25439 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25440 * This property cascades to child containers if not set.
25445 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25446 * fires a looping event with that state. This is required to bind buttons to the valid
25447 * state using the config value formBind:true on the button.
25449 monitorValid : false,
25452 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25457 * @cfg {String} progressUrl - Url to return progress data
25460 progressUrl : false,
25462 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25463 * sending a formdata with extra parameters - eg uploaded elements.
25469 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25470 * fields are added and the column is closed. If no fields are passed the column remains open
25471 * until end() is called.
25472 * @param {Object} config The config to pass to the column
25473 * @param {Field} field1 (optional)
25474 * @param {Field} field2 (optional)
25475 * @param {Field} etc (optional)
25476 * @return Column The column container object
25478 column : function(c){
25479 var col = new Roo.form.Column(c);
25481 if(arguments.length > 1){ // duplicate code required because of Opera
25482 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25489 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25490 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25491 * until end() is called.
25492 * @param {Object} config The config to pass to the fieldset
25493 * @param {Field} field1 (optional)
25494 * @param {Field} field2 (optional)
25495 * @param {Field} etc (optional)
25496 * @return FieldSet The fieldset container object
25498 fieldset : function(c){
25499 var fs = new Roo.form.FieldSet(c);
25501 if(arguments.length > 1){ // duplicate code required because of Opera
25502 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25509 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25510 * fields are added and the container is closed. If no fields are passed the container remains open
25511 * until end() is called.
25512 * @param {Object} config The config to pass to the Layout
25513 * @param {Field} field1 (optional)
25514 * @param {Field} field2 (optional)
25515 * @param {Field} etc (optional)
25516 * @return Layout The container object
25518 container : function(c){
25519 var l = new Roo.form.Layout(c);
25521 if(arguments.length > 1){ // duplicate code required because of Opera
25522 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25529 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25530 * @param {Object} container A Roo.form.Layout or subclass of Layout
25531 * @return {Form} this
25533 start : function(c){
25534 // cascade label info
25535 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25536 this.active.stack.push(c);
25537 c.ownerCt = this.active;
25543 * Closes the current open container
25544 * @return {Form} this
25547 if(this.active == this.root){
25550 this.active = this.active.ownerCt;
25555 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25556 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25557 * as the label of the field.
25558 * @param {Field} field1
25559 * @param {Field} field2 (optional)
25560 * @param {Field} etc. (optional)
25561 * @return {Form} this
25564 this.active.stack.push.apply(this.active.stack, arguments);
25565 this.allItems.push.apply(this.allItems,arguments);
25567 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25568 if(a[i].isFormField){
25573 Roo.form.Form.superclass.add.apply(this, r);
25583 * Find any element that has been added to a form, using it's ID or name
25584 * This can include framesets, columns etc. along with regular fields..
25585 * @param {String} id - id or name to find.
25587 * @return {Element} e - or false if nothing found.
25589 findbyId : function(id)
25595 Roo.each(this.allItems, function(f){
25596 if (f.id == id || f.name == id ){
25607 * Render this form into the passed container. This should only be called once!
25608 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25609 * @return {Form} this
25611 render : function(ct)
25617 var o = this.autoCreate || {
25619 method : this.method || 'POST',
25620 id : this.id || Roo.id()
25622 this.initEl(ct.createChild(o));
25624 this.root.render(this.el);
25628 this.items.each(function(f){
25629 f.render('x-form-el-'+f.id);
25632 if(this.buttons.length > 0){
25633 // tables are required to maintain order and for correct IE layout
25634 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25635 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25636 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25638 var tr = tb.getElementsByTagName('tr')[0];
25639 for(var i = 0, len = this.buttons.length; i < len; i++) {
25640 var b = this.buttons[i];
25641 var td = document.createElement('td');
25642 td.className = 'x-form-btn-td';
25643 b.render(tr.appendChild(td));
25646 if(this.monitorValid){ // initialize after render
25647 this.startMonitoring();
25649 this.fireEvent('rendered', this);
25654 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25655 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25656 * object or a valid Roo.DomHelper element config
25657 * @param {Function} handler The function called when the button is clicked
25658 * @param {Object} scope (optional) The scope of the handler function
25659 * @return {Roo.Button}
25661 addButton : function(config, handler, scope){
25665 minWidth: this.minButtonWidth,
25668 if(typeof config == "string"){
25671 Roo.apply(bc, config);
25673 var btn = new Roo.Button(null, bc);
25674 this.buttons.push(btn);
25679 * Adds a series of form elements (using the xtype property as the factory method.
25680 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25681 * @param {Object} config
25684 addxtype : function()
25686 var ar = Array.prototype.slice.call(arguments, 0);
25688 for(var i = 0; i < ar.length; i++) {
25690 continue; // skip -- if this happends something invalid got sent, we
25691 // should ignore it, as basically that interface element will not show up
25692 // and that should be pretty obvious!!
25695 if (Roo.form[ar[i].xtype]) {
25697 var fe = Roo.factory(ar[i], Roo.form);
25703 fe.store.form = this;
25708 this.allItems.push(fe);
25709 if (fe.items && fe.addxtype) {
25710 fe.addxtype.apply(fe, fe.items);
25720 // console.log('adding ' + ar[i].xtype);
25722 if (ar[i].xtype == 'Button') {
25723 //console.log('adding button');
25724 //console.log(ar[i]);
25725 this.addButton(ar[i]);
25726 this.allItems.push(fe);
25730 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25731 alert('end is not supported on xtype any more, use items');
25733 // //console.log('adding end');
25741 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25742 * option "monitorValid"
25744 startMonitoring : function(){
25747 Roo.TaskMgr.start({
25748 run : this.bindHandler,
25749 interval : this.monitorPoll || 200,
25756 * Stops monitoring of the valid state of this form
25758 stopMonitoring : function(){
25759 this.bound = false;
25763 bindHandler : function(){
25765 return false; // stops binding
25768 this.items.each(function(f){
25769 if(!f.isValid(true)){
25774 for(var i = 0, len = this.buttons.length; i < len; i++){
25775 var btn = this.buttons[i];
25776 if(btn.formBind === true && btn.disabled === valid){
25777 btn.setDisabled(!valid);
25780 this.fireEvent('clientvalidation', this, valid);
25794 Roo.Form = Roo.form.Form;
25797 * Ext JS Library 1.1.1
25798 * Copyright(c) 2006-2007, Ext JS, LLC.
25800 * Originally Released Under LGPL - original licence link has changed is not relivant.
25803 * <script type="text/javascript">
25806 // as we use this in bootstrap.
25807 Roo.namespace('Roo.form');
25809 * @class Roo.form.Action
25810 * Internal Class used to handle form actions
25812 * @param {Roo.form.BasicForm} el The form element or its id
25813 * @param {Object} config Configuration options
25818 // define the action interface
25819 Roo.form.Action = function(form, options){
25821 this.options = options || {};
25824 * Client Validation Failed
25827 Roo.form.Action.CLIENT_INVALID = 'client';
25829 * Server Validation Failed
25832 Roo.form.Action.SERVER_INVALID = 'server';
25834 * Connect to Server Failed
25837 Roo.form.Action.CONNECT_FAILURE = 'connect';
25839 * Reading Data from Server Failed
25842 Roo.form.Action.LOAD_FAILURE = 'load';
25844 Roo.form.Action.prototype = {
25846 failureType : undefined,
25847 response : undefined,
25848 result : undefined,
25850 // interface method
25851 run : function(options){
25855 // interface method
25856 success : function(response){
25860 // interface method
25861 handleResponse : function(response){
25865 // default connection failure
25866 failure : function(response){
25868 this.response = response;
25869 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25870 this.form.afterAction(this, false);
25873 processResponse : function(response){
25874 this.response = response;
25875 if(!response.responseText){
25878 this.result = this.handleResponse(response);
25879 return this.result;
25882 // utility functions used internally
25883 getUrl : function(appendParams){
25884 var url = this.options.url || this.form.url || this.form.el.dom.action;
25886 var p = this.getParams();
25888 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25894 getMethod : function(){
25895 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25898 getParams : function(){
25899 var bp = this.form.baseParams;
25900 var p = this.options.params;
25902 if(typeof p == "object"){
25903 p = Roo.urlEncode(Roo.applyIf(p, bp));
25904 }else if(typeof p == 'string' && bp){
25905 p += '&' + Roo.urlEncode(bp);
25908 p = Roo.urlEncode(bp);
25913 createCallback : function(){
25915 success: this.success,
25916 failure: this.failure,
25918 timeout: (this.form.timeout*1000),
25919 upload: this.form.fileUpload ? this.success : undefined
25924 Roo.form.Action.Submit = function(form, options){
25925 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25928 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25931 haveProgress : false,
25932 uploadComplete : false,
25934 // uploadProgress indicator.
25935 uploadProgress : function()
25937 if (!this.form.progressUrl) {
25941 if (!this.haveProgress) {
25942 Roo.MessageBox.progress("Uploading", "Uploading");
25944 if (this.uploadComplete) {
25945 Roo.MessageBox.hide();
25949 this.haveProgress = true;
25951 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25953 var c = new Roo.data.Connection();
25955 url : this.form.progressUrl,
25960 success : function(req){
25961 //console.log(data);
25965 rdata = Roo.decode(req.responseText)
25967 Roo.log("Invalid data from server..");
25971 if (!rdata || !rdata.success) {
25973 Roo.MessageBox.alert(Roo.encode(rdata));
25976 var data = rdata.data;
25978 if (this.uploadComplete) {
25979 Roo.MessageBox.hide();
25984 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25985 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25988 this.uploadProgress.defer(2000,this);
25991 failure: function(data) {
25992 Roo.log('progress url failed ');
26003 // run get Values on the form, so it syncs any secondary forms.
26004 this.form.getValues();
26006 var o = this.options;
26007 var method = this.getMethod();
26008 var isPost = method == 'POST';
26009 if(o.clientValidation === false || this.form.isValid()){
26011 if (this.form.progressUrl) {
26012 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26013 (new Date() * 1) + '' + Math.random());
26018 Roo.Ajax.request(Roo.apply(this.createCallback(), {
26019 form:this.form.el.dom,
26020 url:this.getUrl(!isPost),
26022 params:isPost ? this.getParams() : null,
26023 isUpload: this.form.fileUpload,
26024 formData : this.form.formData
26027 this.uploadProgress();
26029 }else if (o.clientValidation !== false){ // client validation failed
26030 this.failureType = Roo.form.Action.CLIENT_INVALID;
26031 this.form.afterAction(this, false);
26035 success : function(response)
26037 this.uploadComplete= true;
26038 if (this.haveProgress) {
26039 Roo.MessageBox.hide();
26043 var result = this.processResponse(response);
26044 if(result === true || result.success){
26045 this.form.afterAction(this, true);
26049 this.form.markInvalid(result.errors);
26050 this.failureType = Roo.form.Action.SERVER_INVALID;
26052 this.form.afterAction(this, false);
26054 failure : function(response)
26056 this.uploadComplete= true;
26057 if (this.haveProgress) {
26058 Roo.MessageBox.hide();
26061 this.response = response;
26062 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26063 this.form.afterAction(this, false);
26066 handleResponse : function(response){
26067 if(this.form.errorReader){
26068 var rs = this.form.errorReader.read(response);
26071 for(var i = 0, len = rs.records.length; i < len; i++) {
26072 var r = rs.records[i];
26073 errors[i] = r.data;
26076 if(errors.length < 1){
26080 success : rs.success,
26086 ret = Roo.decode(response.responseText);
26090 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26100 Roo.form.Action.Load = function(form, options){
26101 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26102 this.reader = this.form.reader;
26105 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26110 Roo.Ajax.request(Roo.apply(
26111 this.createCallback(), {
26112 method:this.getMethod(),
26113 url:this.getUrl(false),
26114 params:this.getParams()
26118 success : function(response){
26120 var result = this.processResponse(response);
26121 if(result === true || !result.success || !result.data){
26122 this.failureType = Roo.form.Action.LOAD_FAILURE;
26123 this.form.afterAction(this, false);
26126 this.form.clearInvalid();
26127 this.form.setValues(result.data);
26128 this.form.afterAction(this, true);
26131 handleResponse : function(response){
26132 if(this.form.reader){
26133 var rs = this.form.reader.read(response);
26134 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26136 success : rs.success,
26140 return Roo.decode(response.responseText);
26144 Roo.form.Action.ACTION_TYPES = {
26145 'load' : Roo.form.Action.Load,
26146 'submit' : Roo.form.Action.Submit
26149 * Ext JS Library 1.1.1
26150 * Copyright(c) 2006-2007, Ext JS, LLC.
26152 * Originally Released Under LGPL - original licence link has changed is not relivant.
26155 * <script type="text/javascript">
26159 * @class Roo.form.Layout
26160 * @extends Roo.Component
26161 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26162 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26164 * @param {Object} config Configuration options
26166 Roo.form.Layout = function(config){
26168 if (config.items) {
26169 xitems = config.items;
26170 delete config.items;
26172 Roo.form.Layout.superclass.constructor.call(this, config);
26174 Roo.each(xitems, this.addxtype, this);
26178 Roo.extend(Roo.form.Layout, Roo.Component, {
26180 * @cfg {String/Object} autoCreate
26181 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26184 * @cfg {String/Object/Function} style
26185 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26186 * a function which returns such a specification.
26189 * @cfg {String} labelAlign
26190 * Valid values are "left," "top" and "right" (defaults to "left")
26193 * @cfg {Number} labelWidth
26194 * Fixed width in pixels of all field labels (defaults to undefined)
26197 * @cfg {Boolean} clear
26198 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26202 * @cfg {String} labelSeparator
26203 * The separator to use after field labels (defaults to ':')
26205 labelSeparator : ':',
26207 * @cfg {Boolean} hideLabels
26208 * True to suppress the display of field labels in this layout (defaults to false)
26210 hideLabels : false,
26213 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26218 onRender : function(ct, position){
26219 if(this.el){ // from markup
26220 this.el = Roo.get(this.el);
26221 }else { // generate
26222 var cfg = this.getAutoCreate();
26223 this.el = ct.createChild(cfg, position);
26226 this.el.applyStyles(this.style);
26228 if(this.labelAlign){
26229 this.el.addClass('x-form-label-'+this.labelAlign);
26231 if(this.hideLabels){
26232 this.labelStyle = "display:none";
26233 this.elementStyle = "padding-left:0;";
26235 if(typeof this.labelWidth == 'number'){
26236 this.labelStyle = "width:"+this.labelWidth+"px;";
26237 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26239 if(this.labelAlign == 'top'){
26240 this.labelStyle = "width:auto;";
26241 this.elementStyle = "padding-left:0;";
26244 var stack = this.stack;
26245 var slen = stack.length;
26247 if(!this.fieldTpl){
26248 var t = new Roo.Template(
26249 '<div class="x-form-item {5}">',
26250 '<label for="{0}" style="{2}">{1}{4}</label>',
26251 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26253 '</div><div class="x-form-clear-left"></div>'
26255 t.disableFormats = true;
26257 Roo.form.Layout.prototype.fieldTpl = t;
26259 for(var i = 0; i < slen; i++) {
26260 if(stack[i].isFormField){
26261 this.renderField(stack[i]);
26263 this.renderComponent(stack[i]);
26268 this.el.createChild({cls:'x-form-clear'});
26273 renderField : function(f){
26274 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26277 f.labelStyle||this.labelStyle||'', //2
26278 this.elementStyle||'', //3
26279 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26280 f.itemCls||this.itemCls||'' //5
26281 ], true).getPrevSibling());
26285 renderComponent : function(c){
26286 c.render(c.isLayout ? this.el : this.el.createChild());
26289 * Adds a object form elements (using the xtype property as the factory method.)
26290 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
26291 * @param {Object} config
26293 addxtype : function(o)
26295 // create the lement.
26296 o.form = this.form;
26297 var fe = Roo.factory(o, Roo.form);
26298 this.form.allItems.push(fe);
26299 this.stack.push(fe);
26301 if (fe.isFormField) {
26302 this.form.items.add(fe);
26310 * @class Roo.form.Column
26311 * @extends Roo.form.Layout
26312 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26314 * @param {Object} config Configuration options
26316 Roo.form.Column = function(config){
26317 Roo.form.Column.superclass.constructor.call(this, config);
26320 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26322 * @cfg {Number/String} width
26323 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26326 * @cfg {String/Object} autoCreate
26327 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26331 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26334 onRender : function(ct, position){
26335 Roo.form.Column.superclass.onRender.call(this, ct, position);
26337 this.el.setWidth(this.width);
26344 * @class Roo.form.Row
26345 * @extends Roo.form.Layout
26346 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26347 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26349 * @param {Object} config Configuration options
26353 Roo.form.Row = function(config){
26354 Roo.form.Row.superclass.constructor.call(this, config);
26357 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26359 * @cfg {Number/String} width
26360 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26363 * @cfg {Number/String} height
26364 * The fixed height of the column in pixels or CSS value (defaults to "auto")
26366 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26370 onRender : function(ct, position){
26371 //console.log('row render');
26373 var t = new Roo.Template(
26374 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26375 '<label for="{0}" style="{2}">{1}{4}</label>',
26376 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26380 t.disableFormats = true;
26382 Roo.form.Layout.prototype.rowTpl = t;
26384 this.fieldTpl = this.rowTpl;
26386 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26387 var labelWidth = 100;
26389 if ((this.labelAlign != 'top')) {
26390 if (typeof this.labelWidth == 'number') {
26391 labelWidth = this.labelWidth
26393 this.padWidth = 20 + labelWidth;
26397 Roo.form.Column.superclass.onRender.call(this, ct, position);
26399 this.el.setWidth(this.width);
26402 this.el.setHeight(this.height);
26407 renderField : function(f){
26408 f.fieldEl = this.fieldTpl.append(this.el, [
26409 f.id, f.fieldLabel,
26410 f.labelStyle||this.labelStyle||'',
26411 this.elementStyle||'',
26412 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26413 f.itemCls||this.itemCls||'',
26414 f.width ? f.width + this.padWidth : 160 + this.padWidth
26421 * @class Roo.form.FieldSet
26422 * @extends Roo.form.Layout
26423 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26424 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26426 * @param {Object} config Configuration options
26428 Roo.form.FieldSet = function(config){
26429 Roo.form.FieldSet.superclass.constructor.call(this, config);
26432 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26434 * @cfg {String} legend
26435 * The text to display as the legend for the FieldSet (defaults to '')
26438 * @cfg {String/Object} autoCreate
26439 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26443 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26446 onRender : function(ct, position){
26447 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26449 this.setLegend(this.legend);
26454 setLegend : function(text){
26456 this.el.child('legend').update(text);
26461 * Ext JS Library 1.1.1
26462 * Copyright(c) 2006-2007, Ext JS, LLC.
26464 * Originally Released Under LGPL - original licence link has changed is not relivant.
26467 * <script type="text/javascript">
26470 * @class Roo.form.VTypes
26471 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26474 Roo.form.VTypes = function(){
26475 // closure these in so they are only created once.
26476 var alpha = /^[a-zA-Z_]+$/;
26477 var alphanum = /^[a-zA-Z0-9_]+$/;
26478 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26479 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26481 // All these messages and functions are configurable
26484 * The function used to validate email addresses
26485 * @param {String} value The email address
26487 'email' : function(v){
26488 return email.test(v);
26491 * The error text to display when the email validation function returns false
26494 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26496 * The keystroke filter mask to be applied on email input
26499 'emailMask' : /[a-z0-9_\.\-@]/i,
26502 * The function used to validate URLs
26503 * @param {String} value The URL
26505 'url' : function(v){
26506 return url.test(v);
26509 * The error text to display when the url validation function returns false
26512 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26515 * The function used to validate alpha values
26516 * @param {String} value The value
26518 'alpha' : function(v){
26519 return alpha.test(v);
26522 * The error text to display when the alpha validation function returns false
26525 'alphaText' : 'This field should only contain letters and _',
26527 * The keystroke filter mask to be applied on alpha input
26530 'alphaMask' : /[a-z_]/i,
26533 * The function used to validate alphanumeric values
26534 * @param {String} value The value
26536 'alphanum' : function(v){
26537 return alphanum.test(v);
26540 * The error text to display when the alphanumeric validation function returns false
26543 'alphanumText' : 'This field should only contain letters, numbers and _',
26545 * The keystroke filter mask to be applied on alphanumeric input
26548 'alphanumMask' : /[a-z0-9_]/i
26550 }();//<script type="text/javascript">
26553 * @class Roo.form.FCKeditor
26554 * @extends Roo.form.TextArea
26555 * Wrapper around the FCKEditor http://www.fckeditor.net
26557 * Creates a new FCKeditor
26558 * @param {Object} config Configuration options
26560 Roo.form.FCKeditor = function(config){
26561 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26564 * @event editorinit
26565 * Fired when the editor is initialized - you can add extra handlers here..
26566 * @param {FCKeditor} this
26567 * @param {Object} the FCK object.
26574 Roo.form.FCKeditor.editors = { };
26575 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26577 //defaultAutoCreate : {
26578 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26582 * @cfg {Object} fck options - see fck manual for details.
26587 * @cfg {Object} fck toolbar set (Basic or Default)
26589 toolbarSet : 'Basic',
26591 * @cfg {Object} fck BasePath
26593 basePath : '/fckeditor/',
26601 onRender : function(ct, position)
26604 this.defaultAutoCreate = {
26606 style:"width:300px;height:60px;",
26607 autocomplete: "new-password"
26610 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26613 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26614 if(this.preventScrollbars){
26615 this.el.setStyle("overflow", "hidden");
26617 this.el.setHeight(this.growMin);
26620 //console.log('onrender' + this.getId() );
26621 Roo.form.FCKeditor.editors[this.getId()] = this;
26624 this.replaceTextarea() ;
26628 getEditor : function() {
26629 return this.fckEditor;
26632 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26633 * @param {Mixed} value The value to set
26637 setValue : function(value)
26639 //console.log('setValue: ' + value);
26641 if(typeof(value) == 'undefined') { // not sure why this is happending...
26644 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26646 //if(!this.el || !this.getEditor()) {
26647 // this.value = value;
26648 //this.setValue.defer(100,this,[value]);
26652 if(!this.getEditor()) {
26656 this.getEditor().SetData(value);
26663 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26664 * @return {Mixed} value The field value
26666 getValue : function()
26669 if (this.frame && this.frame.dom.style.display == 'none') {
26670 return Roo.form.FCKeditor.superclass.getValue.call(this);
26673 if(!this.el || !this.getEditor()) {
26675 // this.getValue.defer(100,this);
26680 var value=this.getEditor().GetData();
26681 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26682 return Roo.form.FCKeditor.superclass.getValue.call(this);
26688 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26689 * @return {Mixed} value The field value
26691 getRawValue : function()
26693 if (this.frame && this.frame.dom.style.display == 'none') {
26694 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26697 if(!this.el || !this.getEditor()) {
26698 //this.getRawValue.defer(100,this);
26705 var value=this.getEditor().GetData();
26706 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26707 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26711 setSize : function(w,h) {
26715 //if (this.frame && this.frame.dom.style.display == 'none') {
26716 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26719 //if(!this.el || !this.getEditor()) {
26720 // this.setSize.defer(100,this, [w,h]);
26726 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26728 this.frame.dom.setAttribute('width', w);
26729 this.frame.dom.setAttribute('height', h);
26730 this.frame.setSize(w,h);
26734 toggleSourceEdit : function(value) {
26738 this.el.dom.style.display = value ? '' : 'none';
26739 this.frame.dom.style.display = value ? 'none' : '';
26744 focus: function(tag)
26746 if (this.frame.dom.style.display == 'none') {
26747 return Roo.form.FCKeditor.superclass.focus.call(this);
26749 if(!this.el || !this.getEditor()) {
26750 this.focus.defer(100,this, [tag]);
26757 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26758 this.getEditor().Focus();
26760 if (!this.getEditor().Selection.GetSelection()) {
26761 this.focus.defer(100,this, [tag]);
26766 var r = this.getEditor().EditorDocument.createRange();
26767 r.setStart(tgs[0],0);
26768 r.setEnd(tgs[0],0);
26769 this.getEditor().Selection.GetSelection().removeAllRanges();
26770 this.getEditor().Selection.GetSelection().addRange(r);
26771 this.getEditor().Focus();
26778 replaceTextarea : function()
26780 if ( document.getElementById( this.getId() + '___Frame' ) ) {
26783 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26785 // We must check the elements firstly using the Id and then the name.
26786 var oTextarea = document.getElementById( this.getId() );
26788 var colElementsByName = document.getElementsByName( this.getId() ) ;
26790 oTextarea.style.display = 'none' ;
26792 if ( oTextarea.tabIndex ) {
26793 this.TabIndex = oTextarea.tabIndex ;
26796 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26797 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26798 this.frame = Roo.get(this.getId() + '___Frame')
26801 _getConfigHtml : function()
26805 for ( var o in this.fckconfig ) {
26806 sConfig += sConfig.length > 0 ? '&' : '';
26807 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26810 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26814 _getIFrameHtml : function()
26816 var sFile = 'fckeditor.html' ;
26817 /* no idea what this is about..
26820 if ( (/fcksource=true/i).test( window.top.location.search ) )
26821 sFile = 'fckeditor.original.html' ;
26826 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26827 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
26830 var html = '<iframe id="' + this.getId() +
26831 '___Frame" src="' + sLink +
26832 '" width="' + this.width +
26833 '" height="' + this.height + '"' +
26834 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
26835 ' frameborder="0" scrolling="no"></iframe>' ;
26840 _insertHtmlBefore : function( html, element )
26842 if ( element.insertAdjacentHTML ) {
26844 element.insertAdjacentHTML( 'beforeBegin', html ) ;
26846 var oRange = document.createRange() ;
26847 oRange.setStartBefore( element ) ;
26848 var oFragment = oRange.createContextualFragment( html );
26849 element.parentNode.insertBefore( oFragment, element ) ;
26862 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26864 function FCKeditor_OnComplete(editorInstance){
26865 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26866 f.fckEditor = editorInstance;
26867 //console.log("loaded");
26868 f.fireEvent('editorinit', f, editorInstance);
26888 //<script type="text/javascript">
26890 * @class Roo.form.GridField
26891 * @extends Roo.form.Field
26892 * Embed a grid (or editable grid into a form)
26895 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26897 * xgrid.store = Roo.data.Store
26898 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26899 * xgrid.store.reader = Roo.data.JsonReader
26903 * Creates a new GridField
26904 * @param {Object} config Configuration options
26906 Roo.form.GridField = function(config){
26907 Roo.form.GridField.superclass.constructor.call(this, config);
26911 Roo.extend(Roo.form.GridField, Roo.form.Field, {
26913 * @cfg {Number} width - used to restrict width of grid..
26917 * @cfg {Number} height - used to restrict height of grid..
26921 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26927 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26928 * {tag: "input", type: "checkbox", autocomplete: "off"})
26930 // defaultAutoCreate : { tag: 'div' },
26931 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26933 * @cfg {String} addTitle Text to include for adding a title.
26937 onResize : function(){
26938 Roo.form.Field.superclass.onResize.apply(this, arguments);
26941 initEvents : function(){
26942 // Roo.form.Checkbox.superclass.initEvents.call(this);
26943 // has no events...
26948 getResizeEl : function(){
26952 getPositionEl : function(){
26957 onRender : function(ct, position){
26959 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26960 var style = this.style;
26963 Roo.form.GridField.superclass.onRender.call(this, ct, position);
26964 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26965 this.viewEl = this.wrap.createChild({ tag: 'div' });
26967 this.viewEl.applyStyles(style);
26970 this.viewEl.setWidth(this.width);
26973 this.viewEl.setHeight(this.height);
26975 //if(this.inputValue !== undefined){
26976 //this.setValue(this.value);
26979 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26982 this.grid.render();
26983 this.grid.getDataSource().on('remove', this.refreshValue, this);
26984 this.grid.getDataSource().on('update', this.refreshValue, this);
26985 this.grid.on('afteredit', this.refreshValue, this);
26991 * Sets the value of the item.
26992 * @param {String} either an object or a string..
26994 setValue : function(v){
26996 v = v || []; // empty set..
26997 // this does not seem smart - it really only affects memoryproxy grids..
26998 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26999 var ds = this.grid.getDataSource();
27000 // assumes a json reader..
27002 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
27003 ds.loadData( data);
27005 // clear selection so it does not get stale.
27006 if (this.grid.sm) {
27007 this.grid.sm.clearSelections();
27010 Roo.form.GridField.superclass.setValue.call(this, v);
27011 this.refreshValue();
27012 // should load data in the grid really....
27016 refreshValue: function() {
27018 this.grid.getDataSource().each(function(r) {
27021 this.el.dom.value = Roo.encode(val);
27029 * Ext JS Library 1.1.1
27030 * Copyright(c) 2006-2007, Ext JS, LLC.
27032 * Originally Released Under LGPL - original licence link has changed is not relivant.
27035 * <script type="text/javascript">
27038 * @class Roo.form.DisplayField
27039 * @extends Roo.form.Field
27040 * A generic Field to display non-editable data.
27041 * @cfg {Boolean} closable (true|false) default false
27043 * Creates a new Display Field item.
27044 * @param {Object} config Configuration options
27046 Roo.form.DisplayField = function(config){
27047 Roo.form.DisplayField.superclass.constructor.call(this, config);
27052 * Fires after the click the close btn
27053 * @param {Roo.form.DisplayField} this
27059 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
27060 inputType: 'hidden',
27066 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27068 focusClass : undefined,
27070 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27072 fieldClass: 'x-form-field',
27075 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27077 valueRenderer: undefined,
27081 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27082 * {tag: "input", type: "checkbox", autocomplete: "off"})
27085 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27089 onResize : function(){
27090 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27094 initEvents : function(){
27095 // Roo.form.Checkbox.superclass.initEvents.call(this);
27096 // has no events...
27099 this.closeEl.on('click', this.onClose, this);
27105 getResizeEl : function(){
27109 getPositionEl : function(){
27114 onRender : function(ct, position){
27116 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27117 //if(this.inputValue !== undefined){
27118 this.wrap = this.el.wrap();
27120 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27123 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27126 if (this.bodyStyle) {
27127 this.viewEl.applyStyles(this.bodyStyle);
27129 //this.viewEl.setStyle('padding', '2px');
27131 this.setValue(this.value);
27136 initValue : Roo.emptyFn,
27141 onClick : function(){
27146 * Sets the checked state of the checkbox.
27147 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27149 setValue : function(v){
27151 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
27152 // this might be called before we have a dom element..
27153 if (!this.viewEl) {
27156 this.viewEl.dom.innerHTML = html;
27157 Roo.form.DisplayField.superclass.setValue.call(this, v);
27161 onClose : function(e)
27163 e.preventDefault();
27165 this.fireEvent('close', this);
27174 * @class Roo.form.DayPicker
27175 * @extends Roo.form.Field
27176 * A Day picker show [M] [T] [W] ....
27178 * Creates a new Day Picker
27179 * @param {Object} config Configuration options
27181 Roo.form.DayPicker= function(config){
27182 Roo.form.DayPicker.superclass.constructor.call(this, config);
27186 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
27188 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27190 focusClass : undefined,
27192 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27194 fieldClass: "x-form-field",
27197 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27198 * {tag: "input", type: "checkbox", autocomplete: "off"})
27200 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27203 actionMode : 'viewEl',
27207 inputType : 'hidden',
27210 inputElement: false, // real input element?
27211 basedOn: false, // ????
27213 isFormField: true, // not sure where this is needed!!!!
27215 onResize : function(){
27216 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27217 if(!this.boxLabel){
27218 this.el.alignTo(this.wrap, 'c-c');
27222 initEvents : function(){
27223 Roo.form.Checkbox.superclass.initEvents.call(this);
27224 this.el.on("click", this.onClick, this);
27225 this.el.on("change", this.onClick, this);
27229 getResizeEl : function(){
27233 getPositionEl : function(){
27239 onRender : function(ct, position){
27240 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27242 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27244 var r1 = '<table><tr>';
27245 var r2 = '<tr class="x-form-daypick-icons">';
27246 for (var i=0; i < 7; i++) {
27247 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27248 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
27251 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27252 viewEl.select('img').on('click', this.onClick, this);
27253 this.viewEl = viewEl;
27256 // this will not work on Chrome!!!
27257 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
27258 this.el.on('propertychange', this.setFromHidden, this); //ie
27266 initValue : Roo.emptyFn,
27269 * Returns the checked state of the checkbox.
27270 * @return {Boolean} True if checked, else false
27272 getValue : function(){
27273 return this.el.dom.value;
27278 onClick : function(e){
27279 //this.setChecked(!this.checked);
27280 Roo.get(e.target).toggleClass('x-menu-item-checked');
27281 this.refreshValue();
27282 //if(this.el.dom.checked != this.checked){
27283 // this.setValue(this.el.dom.checked);
27288 refreshValue : function()
27291 this.viewEl.select('img',true).each(function(e,i,n) {
27292 val += e.is(".x-menu-item-checked") ? String(n) : '';
27294 this.setValue(val, true);
27298 * Sets the checked state of the checkbox.
27299 * On is always based on a string comparison between inputValue and the param.
27300 * @param {Boolean/String} value - the value to set
27301 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27303 setValue : function(v,suppressEvent){
27304 if (!this.el.dom) {
27307 var old = this.el.dom.value ;
27308 this.el.dom.value = v;
27309 if (suppressEvent) {
27313 // update display..
27314 this.viewEl.select('img',true).each(function(e,i,n) {
27316 var on = e.is(".x-menu-item-checked");
27317 var newv = v.indexOf(String(n)) > -1;
27319 e.toggleClass('x-menu-item-checked');
27325 this.fireEvent('change', this, v, old);
27330 // handle setting of hidden value by some other method!!?!?
27331 setFromHidden: function()
27336 //console.log("SET FROM HIDDEN");
27337 //alert('setFrom hidden');
27338 this.setValue(this.el.dom.value);
27341 onDestroy : function()
27344 Roo.get(this.viewEl).remove();
27347 Roo.form.DayPicker.superclass.onDestroy.call(this);
27351 * RooJS Library 1.1.1
27352 * Copyright(c) 2008-2011 Alan Knowles
27359 * @class Roo.form.ComboCheck
27360 * @extends Roo.form.ComboBox
27361 * A combobox for multiple select items.
27363 * FIXME - could do with a reset button..
27366 * Create a new ComboCheck
27367 * @param {Object} config Configuration options
27369 Roo.form.ComboCheck = function(config){
27370 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27371 // should verify some data...
27373 // hiddenName = required..
27374 // displayField = required
27375 // valudField == required
27376 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27378 Roo.each(req, function(e) {
27379 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27380 throw "Roo.form.ComboCheck : missing value for: " + e;
27387 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27392 selectedClass: 'x-menu-item-checked',
27395 onRender : function(ct, position){
27401 var cls = 'x-combo-list';
27404 this.tpl = new Roo.Template({
27405 html : '<div class="'+cls+'-item x-menu-check-item">' +
27406 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27407 '<span>{' + this.displayField + '}</span>' +
27414 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27415 this.view.singleSelect = false;
27416 this.view.multiSelect = true;
27417 this.view.toggleSelect = true;
27418 this.pageTb.add(new Roo.Toolbar.Fill(), {
27421 handler: function()
27428 onViewOver : function(e, t){
27434 onViewClick : function(doFocus,index){
27438 select: function () {
27439 //Roo.log("SELECT CALLED");
27442 selectByValue : function(xv, scrollIntoView){
27443 var ar = this.getValueArray();
27446 Roo.each(ar, function(v) {
27447 if(v === undefined || v === null){
27450 var r = this.findRecord(this.valueField, v);
27452 sels.push(this.store.indexOf(r))
27456 this.view.select(sels);
27462 onSelect : function(record, index){
27463 // Roo.log("onselect Called");
27464 // this is only called by the clear button now..
27465 this.view.clearSelections();
27466 this.setValue('[]');
27467 if (this.value != this.valueBefore) {
27468 this.fireEvent('change', this, this.value, this.valueBefore);
27469 this.valueBefore = this.value;
27472 getValueArray : function()
27477 //Roo.log(this.value);
27478 if (typeof(this.value) == 'undefined') {
27481 var ar = Roo.decode(this.value);
27482 return ar instanceof Array ? ar : []; //?? valid?
27485 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27490 expand : function ()
27493 Roo.form.ComboCheck.superclass.expand.call(this);
27494 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27495 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27500 collapse : function(){
27501 Roo.form.ComboCheck.superclass.collapse.call(this);
27502 var sl = this.view.getSelectedIndexes();
27503 var st = this.store;
27507 Roo.each(sl, function(i) {
27509 nv.push(r.get(this.valueField));
27511 this.setValue(Roo.encode(nv));
27512 if (this.value != this.valueBefore) {
27514 this.fireEvent('change', this, this.value, this.valueBefore);
27515 this.valueBefore = this.value;
27520 setValue : function(v){
27524 var vals = this.getValueArray();
27526 Roo.each(vals, function(k) {
27527 var r = this.findRecord(this.valueField, k);
27529 tv.push(r.data[this.displayField]);
27530 }else if(this.valueNotFoundText !== undefined){
27531 tv.push( this.valueNotFoundText );
27536 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27537 this.hiddenField.value = v;
27543 * Ext JS Library 1.1.1
27544 * Copyright(c) 2006-2007, Ext JS, LLC.
27546 * Originally Released Under LGPL - original licence link has changed is not relivant.
27549 * <script type="text/javascript">
27553 * @class Roo.form.Signature
27554 * @extends Roo.form.Field
27558 * @param {Object} config Configuration options
27561 Roo.form.Signature = function(config){
27562 Roo.form.Signature.superclass.constructor.call(this, config);
27564 this.addEvents({// not in used??
27567 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27568 * @param {Roo.form.Signature} combo This combo box
27573 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27574 * @param {Roo.form.ComboBox} combo This combo box
27575 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27581 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27583 * @cfg {Object} labels Label to use when rendering a form.
27587 * confirm : "Confirm"
27592 confirm : "Confirm"
27595 * @cfg {Number} width The signature panel width (defaults to 300)
27599 * @cfg {Number} height The signature panel height (defaults to 100)
27603 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27605 allowBlank : false,
27608 // {Object} signPanel The signature SVG panel element (defaults to {})
27610 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27611 isMouseDown : false,
27612 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27613 isConfirmed : false,
27614 // {String} signatureTmp SVG mapping string (defaults to empty string)
27618 defaultAutoCreate : { // modified by initCompnoent..
27624 onRender : function(ct, position){
27626 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27628 this.wrap = this.el.wrap({
27629 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27632 this.createToolbar(this);
27633 this.signPanel = this.wrap.createChild({
27635 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27639 this.svgID = Roo.id();
27640 this.svgEl = this.signPanel.createChild({
27641 xmlns : 'http://www.w3.org/2000/svg',
27643 id : this.svgID + "-svg",
27645 height: this.height,
27646 viewBox: '0 0 '+this.width+' '+this.height,
27650 id: this.svgID + "-svg-r",
27652 height: this.height,
27657 id: this.svgID + "-svg-l",
27659 y1: (this.height*0.8), // start set the line in 80% of height
27660 x2: this.width, // end
27661 y2: (this.height*0.8), // end set the line in 80% of height
27663 'stroke-width': "1",
27664 'stroke-dasharray': "3",
27665 'shape-rendering': "crispEdges",
27666 'pointer-events': "none"
27670 id: this.svgID + "-svg-p",
27672 'stroke-width': "3",
27674 'pointer-events': 'none'
27679 this.svgBox = this.svgEl.dom.getScreenCTM();
27681 createSVG : function(){
27682 var svg = this.signPanel;
27683 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27686 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27687 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27688 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27689 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27690 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27691 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27692 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27695 isTouchEvent : function(e){
27696 return e.type.match(/^touch/);
27698 getCoords : function (e) {
27699 var pt = this.svgEl.dom.createSVGPoint();
27702 if (this.isTouchEvent(e)) {
27703 pt.x = e.targetTouches[0].clientX;
27704 pt.y = e.targetTouches[0].clientY;
27706 var a = this.svgEl.dom.getScreenCTM();
27707 var b = a.inverse();
27708 var mx = pt.matrixTransform(b);
27709 return mx.x + ',' + mx.y;
27711 //mouse event headler
27712 down : function (e) {
27713 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27714 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27716 this.isMouseDown = true;
27718 e.preventDefault();
27720 move : function (e) {
27721 if (this.isMouseDown) {
27722 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27723 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27726 e.preventDefault();
27728 up : function (e) {
27729 this.isMouseDown = false;
27730 var sp = this.signatureTmp.split(' ');
27733 if(!sp[sp.length-2].match(/^L/)){
27737 this.signatureTmp = sp.join(" ");
27740 if(this.getValue() != this.signatureTmp){
27741 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27742 this.isConfirmed = false;
27744 e.preventDefault();
27748 * Protected method that will not generally be called directly. It
27749 * is called when the editor creates its toolbar. Override this method if you need to
27750 * add custom toolbar buttons.
27751 * @param {HtmlEditor} editor
27753 createToolbar : function(editor){
27754 function btn(id, toggle, handler){
27755 var xid = fid + '-'+ id ;
27759 cls : 'x-btn-icon x-edit-'+id,
27760 enableToggle:toggle !== false,
27761 scope: editor, // was editor...
27762 handler:handler||editor.relayBtnCmd,
27763 clickEvent:'mousedown',
27764 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27770 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27774 cls : ' x-signature-btn x-signature-'+id,
27775 scope: editor, // was editor...
27776 handler: this.reset,
27777 clickEvent:'mousedown',
27778 text: this.labels.clear
27785 cls : ' x-signature-btn x-signature-'+id,
27786 scope: editor, // was editor...
27787 handler: this.confirmHandler,
27788 clickEvent:'mousedown',
27789 text: this.labels.confirm
27796 * when user is clicked confirm then show this image.....
27798 * @return {String} Image Data URI
27800 getImageDataURI : function(){
27801 var svg = this.svgEl.dom.parentNode.innerHTML;
27802 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27807 * @return {Boolean} this.isConfirmed
27809 getConfirmed : function(){
27810 return this.isConfirmed;
27814 * @return {Number} this.width
27816 getWidth : function(){
27821 * @return {Number} this.height
27823 getHeight : function(){
27824 return this.height;
27827 getSignature : function(){
27828 return this.signatureTmp;
27831 reset : function(){
27832 this.signatureTmp = '';
27833 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27834 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27835 this.isConfirmed = false;
27836 Roo.form.Signature.superclass.reset.call(this);
27838 setSignature : function(s){
27839 this.signatureTmp = s;
27840 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27841 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27843 this.isConfirmed = false;
27844 Roo.form.Signature.superclass.reset.call(this);
27847 // Roo.log(this.signPanel.dom.contentWindow.up())
27850 setConfirmed : function(){
27854 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27857 confirmHandler : function(){
27858 if(!this.getSignature()){
27862 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27863 this.setValue(this.getSignature());
27864 this.isConfirmed = true;
27866 this.fireEvent('confirm', this);
27869 // Subclasses should provide the validation implementation by overriding this
27870 validateValue : function(value){
27871 if(this.allowBlank){
27875 if(this.isConfirmed){
27882 * Ext JS Library 1.1.1
27883 * Copyright(c) 2006-2007, Ext JS, LLC.
27885 * Originally Released Under LGPL - original licence link has changed is not relivant.
27888 * <script type="text/javascript">
27893 * @class Roo.form.ComboBox
27894 * @extends Roo.form.TriggerField
27895 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27897 * Create a new ComboBox.
27898 * @param {Object} config Configuration options
27900 Roo.form.Select = function(config){
27901 Roo.form.Select.superclass.constructor.call(this, config);
27905 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27907 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27910 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27911 * rendering into an Roo.Editor, defaults to false)
27914 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27915 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27918 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27921 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27922 * the dropdown list (defaults to undefined, with no header element)
27926 * @cfg {String/Roo.Template} tpl The template to use to render the output
27930 defaultAutoCreate : {tag: "select" },
27932 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27934 listWidth: undefined,
27936 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27937 * mode = 'remote' or 'text' if mode = 'local')
27939 displayField: undefined,
27941 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27942 * mode = 'remote' or 'value' if mode = 'local').
27943 * Note: use of a valueField requires the user make a selection
27944 * in order for a value to be mapped.
27946 valueField: undefined,
27950 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27951 * field's data value (defaults to the underlying DOM element's name)
27953 hiddenName: undefined,
27955 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27959 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27961 selectedClass: 'x-combo-selected',
27963 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
27964 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27965 * which displays a downward arrow icon).
27967 triggerClass : 'x-form-arrow-trigger',
27969 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27973 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27974 * anchor positions (defaults to 'tl-bl')
27976 listAlign: 'tl-bl?',
27978 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27982 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
27983 * query specified by the allQuery config option (defaults to 'query')
27985 triggerAction: 'query',
27987 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27988 * (defaults to 4, does not apply if editable = false)
27992 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27993 * delay (typeAheadDelay) if it matches a known value (defaults to false)
27997 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27998 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28002 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28003 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
28007 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
28008 * when editable = true (defaults to false)
28010 selectOnFocus:false,
28012 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28014 queryParam: 'query',
28016 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
28017 * when mode = 'remote' (defaults to 'Loading...')
28019 loadingText: 'Loading...',
28021 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28025 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28029 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28030 * traditional select (defaults to true)
28034 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28038 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28042 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28043 * listWidth has a higher value)
28047 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28048 * allow the user to set arbitrary text into the field (defaults to false)
28050 forceSelection:false,
28052 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28053 * if typeAhead = true (defaults to 250)
28055 typeAheadDelay : 250,
28057 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28058 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28060 valueNotFoundText : undefined,
28063 * @cfg {String} defaultValue The value displayed after loading the store.
28068 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28070 blockFocus : false,
28073 * @cfg {Boolean} disableClear Disable showing of clear button.
28075 disableClear : false,
28077 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
28079 alwaysQuery : false,
28085 // element that contains real text value.. (when hidden is used..)
28088 onRender : function(ct, position){
28089 Roo.form.Field.prototype.onRender.call(this, ct, position);
28092 this.store.on('beforeload', this.onBeforeLoad, this);
28093 this.store.on('load', this.onLoad, this);
28094 this.store.on('loadexception', this.onLoadException, this);
28095 this.store.load({});
28103 initEvents : function(){
28104 //Roo.form.ComboBox.superclass.initEvents.call(this);
28108 onDestroy : function(){
28111 this.store.un('beforeload', this.onBeforeLoad, this);
28112 this.store.un('load', this.onLoad, this);
28113 this.store.un('loadexception', this.onLoadException, this);
28115 //Roo.form.ComboBox.superclass.onDestroy.call(this);
28119 fireKey : function(e){
28120 if(e.isNavKeyPress() && !this.list.isVisible()){
28121 this.fireEvent("specialkey", this, e);
28126 onResize: function(w, h){
28134 * Allow or prevent the user from directly editing the field text. If false is passed,
28135 * the user will only be able to select from the items defined in the dropdown list. This method
28136 * is the runtime equivalent of setting the 'editable' config option at config time.
28137 * @param {Boolean} value True to allow the user to directly edit the field text
28139 setEditable : function(value){
28144 onBeforeLoad : function(){
28146 Roo.log("Select before load");
28149 this.innerList.update(this.loadingText ?
28150 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28151 //this.restrictHeight();
28152 this.selectedIndex = -1;
28156 onLoad : function(){
28159 var dom = this.el.dom;
28160 dom.innerHTML = '';
28161 var od = dom.ownerDocument;
28163 if (this.emptyText) {
28164 var op = od.createElement('option');
28165 op.setAttribute('value', '');
28166 op.innerHTML = String.format('{0}', this.emptyText);
28167 dom.appendChild(op);
28169 if(this.store.getCount() > 0){
28171 var vf = this.valueField;
28172 var df = this.displayField;
28173 this.store.data.each(function(r) {
28174 // which colmsn to use... testing - cdoe / title..
28175 var op = od.createElement('option');
28176 op.setAttribute('value', r.data[vf]);
28177 op.innerHTML = String.format('{0}', r.data[df]);
28178 dom.appendChild(op);
28180 if (typeof(this.defaultValue != 'undefined')) {
28181 this.setValue(this.defaultValue);
28186 //this.onEmptyResults();
28191 onLoadException : function()
28193 dom.innerHTML = '';
28195 Roo.log("Select on load exception");
28199 Roo.log(this.store.reader.jsonData);
28200 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28201 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28207 onTypeAhead : function(){
28212 onSelect : function(record, index){
28213 Roo.log('on select?');
28215 if(this.fireEvent('beforeselect', this, record, index) !== false){
28216 this.setFromData(index > -1 ? record.data : false);
28218 this.fireEvent('select', this, record, index);
28223 * Returns the currently selected field value or empty string if no value is set.
28224 * @return {String} value The selected value
28226 getValue : function(){
28227 var dom = this.el.dom;
28228 this.value = dom.options[dom.selectedIndex].value;
28234 * Clears any text/value currently set in the field
28236 clearValue : function(){
28238 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28243 * Sets the specified value into the field. If the value finds a match, the corresponding record text
28244 * will be displayed in the field. If the value does not match the data value of an existing item,
28245 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28246 * Otherwise the field will be blank (although the value will still be set).
28247 * @param {String} value The value to match
28249 setValue : function(v){
28250 var d = this.el.dom;
28251 for (var i =0; i < d.options.length;i++) {
28252 if (v == d.options[i].value) {
28253 d.selectedIndex = i;
28261 * @property {Object} the last set data for the element
28266 * Sets the value of the field based on a object which is related to the record format for the store.
28267 * @param {Object} value the value to set as. or false on reset?
28269 setFromData : function(o){
28270 Roo.log('setfrom data?');
28276 reset : function(){
28280 findRecord : function(prop, value){
28285 if(this.store.getCount() > 0){
28286 this.store.each(function(r){
28287 if(r.data[prop] == value){
28297 getName: function()
28299 // returns hidden if it's set..
28300 if (!this.rendered) {return ''};
28301 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
28309 onEmptyResults : function(){
28310 Roo.log('empty results');
28315 * Returns true if the dropdown list is expanded, else false.
28317 isExpanded : function(){
28322 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28323 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28324 * @param {String} value The data value of the item to select
28325 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28326 * selected item if it is not currently in view (defaults to true)
28327 * @return {Boolean} True if the value matched an item in the list, else false
28329 selectByValue : function(v, scrollIntoView){
28330 Roo.log('select By Value');
28333 if(v !== undefined && v !== null){
28334 var r = this.findRecord(this.valueField || this.displayField, v);
28336 this.select(this.store.indexOf(r), scrollIntoView);
28344 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28345 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28346 * @param {Number} index The zero-based index of the list item to select
28347 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28348 * selected item if it is not currently in view (defaults to true)
28350 select : function(index, scrollIntoView){
28351 Roo.log('select ');
28354 this.selectedIndex = index;
28355 this.view.select(index);
28356 if(scrollIntoView !== false){
28357 var el = this.view.getNode(index);
28359 this.innerList.scrollChildIntoView(el, false);
28367 validateBlur : function(){
28374 initQuery : function(){
28375 this.doQuery(this.getRawValue());
28379 doForce : function(){
28380 if(this.el.dom.value.length > 0){
28381 this.el.dom.value =
28382 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28388 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28389 * query allowing the query action to be canceled if needed.
28390 * @param {String} query The SQL query to execute
28391 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28392 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28393 * saved in the current store (defaults to false)
28395 doQuery : function(q, forceAll){
28397 Roo.log('doQuery?');
28398 if(q === undefined || q === null){
28403 forceAll: forceAll,
28407 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28411 forceAll = qe.forceAll;
28412 if(forceAll === true || (q.length >= this.minChars)){
28413 if(this.lastQuery != q || this.alwaysQuery){
28414 this.lastQuery = q;
28415 if(this.mode == 'local'){
28416 this.selectedIndex = -1;
28418 this.store.clearFilter();
28420 this.store.filter(this.displayField, q);
28424 this.store.baseParams[this.queryParam] = q;
28426 params: this.getParams(q)
28431 this.selectedIndex = -1;
28438 getParams : function(q){
28440 //p[this.queryParam] = q;
28443 p.limit = this.pageSize;
28449 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28451 collapse : function(){
28456 collapseIf : function(e){
28461 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28463 expand : function(){
28471 * @cfg {Boolean} grow
28475 * @cfg {Number} growMin
28479 * @cfg {Number} growMax
28487 setWidth : function()
28491 getResizeEl : function(){
28494 });//<script type="text/javasscript">
28498 * @class Roo.DDView
28499 * A DnD enabled version of Roo.View.
28500 * @param {Element/String} container The Element in which to create the View.
28501 * @param {String} tpl The template string used to create the markup for each element of the View
28502 * @param {Object} config The configuration properties. These include all the config options of
28503 * {@link Roo.View} plus some specific to this class.<br>
28505 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28506 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28508 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28509 .x-view-drag-insert-above {
28510 border-top:1px dotted #3366cc;
28512 .x-view-drag-insert-below {
28513 border-bottom:1px dotted #3366cc;
28519 Roo.DDView = function(container, tpl, config) {
28520 Roo.DDView.superclass.constructor.apply(this, arguments);
28521 this.getEl().setStyle("outline", "0px none");
28522 this.getEl().unselectable();
28523 if (this.dragGroup) {
28524 this.setDraggable(this.dragGroup.split(","));
28526 if (this.dropGroup) {
28527 this.setDroppable(this.dropGroup.split(","));
28529 if (this.deletable) {
28530 this.setDeletable();
28532 this.isDirtyFlag = false;
28538 Roo.extend(Roo.DDView, Roo.View, {
28539 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28540 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28541 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28542 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28546 reset: Roo.emptyFn,
28548 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28550 validate: function() {
28554 destroy: function() {
28555 this.purgeListeners();
28556 this.getEl.removeAllListeners();
28557 this.getEl().remove();
28558 if (this.dragZone) {
28559 if (this.dragZone.destroy) {
28560 this.dragZone.destroy();
28563 if (this.dropZone) {
28564 if (this.dropZone.destroy) {
28565 this.dropZone.destroy();
28570 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28571 getName: function() {
28575 /** Loads the View from a JSON string representing the Records to put into the Store. */
28576 setValue: function(v) {
28578 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28581 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28582 this.store.proxy = new Roo.data.MemoryProxy(data);
28586 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28587 getValue: function() {
28589 this.store.each(function(rec) {
28590 result += rec.id + ',';
28592 return result.substr(0, result.length - 1) + ')';
28595 getIds: function() {
28596 var i = 0, result = new Array(this.store.getCount());
28597 this.store.each(function(rec) {
28598 result[i++] = rec.id;
28603 isDirty: function() {
28604 return this.isDirtyFlag;
28608 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28609 * whole Element becomes the target, and this causes the drop gesture to append.
28611 getTargetFromEvent : function(e) {
28612 var target = e.getTarget();
28613 while ((target !== null) && (target.parentNode != this.el.dom)) {
28614 target = target.parentNode;
28617 target = this.el.dom.lastChild || this.el.dom;
28623 * Create the drag data which consists of an object which has the property "ddel" as
28624 * the drag proxy element.
28626 getDragData : function(e) {
28627 var target = this.findItemFromChild(e.getTarget());
28629 this.handleSelection(e);
28630 var selNodes = this.getSelectedNodes();
28633 copy: this.copy || (this.allowCopy && e.ctrlKey),
28637 var selectedIndices = this.getSelectedIndexes();
28638 for (var i = 0; i < selectedIndices.length; i++) {
28639 dragData.records.push(this.store.getAt(selectedIndices[i]));
28641 if (selNodes.length == 1) {
28642 dragData.ddel = target.cloneNode(true); // the div element
28644 var div = document.createElement('div'); // create the multi element drag "ghost"
28645 div.className = 'multi-proxy';
28646 for (var i = 0, len = selNodes.length; i < len; i++) {
28647 div.appendChild(selNodes[i].cloneNode(true));
28649 dragData.ddel = div;
28651 //console.log(dragData)
28652 //console.log(dragData.ddel.innerHTML)
28655 //console.log('nodragData')
28659 /** Specify to which ddGroup items in this DDView may be dragged. */
28660 setDraggable: function(ddGroup) {
28661 if (ddGroup instanceof Array) {
28662 Roo.each(ddGroup, this.setDraggable, this);
28665 if (this.dragZone) {
28666 this.dragZone.addToGroup(ddGroup);
28668 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28669 containerScroll: true,
28673 // Draggability implies selection. DragZone's mousedown selects the element.
28674 if (!this.multiSelect) { this.singleSelect = true; }
28676 // Wire the DragZone's handlers up to methods in *this*
28677 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28681 /** Specify from which ddGroup this DDView accepts drops. */
28682 setDroppable: function(ddGroup) {
28683 if (ddGroup instanceof Array) {
28684 Roo.each(ddGroup, this.setDroppable, this);
28687 if (this.dropZone) {
28688 this.dropZone.addToGroup(ddGroup);
28690 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28691 containerScroll: true,
28695 // Wire the DropZone's handlers up to methods in *this*
28696 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28697 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28698 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28699 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28700 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28704 /** Decide whether to drop above or below a View node. */
28705 getDropPoint : function(e, n, dd){
28706 if (n == this.el.dom) { return "above"; }
28707 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28708 var c = t + (b - t) / 2;
28709 var y = Roo.lib.Event.getPageY(e);
28717 onNodeEnter : function(n, dd, e, data){
28721 onNodeOver : function(n, dd, e, data){
28722 var pt = this.getDropPoint(e, n, dd);
28723 // set the insert point style on the target node
28724 var dragElClass = this.dropNotAllowed;
28727 if (pt == "above"){
28728 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28729 targetElClass = "x-view-drag-insert-above";
28731 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28732 targetElClass = "x-view-drag-insert-below";
28734 if (this.lastInsertClass != targetElClass){
28735 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28736 this.lastInsertClass = targetElClass;
28739 return dragElClass;
28742 onNodeOut : function(n, dd, e, data){
28743 this.removeDropIndicators(n);
28746 onNodeDrop : function(n, dd, e, data){
28747 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28750 var pt = this.getDropPoint(e, n, dd);
28751 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28752 if (pt == "below") { insertAt++; }
28753 for (var i = 0; i < data.records.length; i++) {
28754 var r = data.records[i];
28755 var dup = this.store.getById(r.id);
28756 if (dup && (dd != this.dragZone)) {
28757 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28760 this.store.insert(insertAt++, r.copy());
28762 data.source.isDirtyFlag = true;
28764 this.store.insert(insertAt++, r);
28766 this.isDirtyFlag = true;
28769 this.dragZone.cachedTarget = null;
28773 removeDropIndicators : function(n){
28775 Roo.fly(n).removeClass([
28776 "x-view-drag-insert-above",
28777 "x-view-drag-insert-below"]);
28778 this.lastInsertClass = "_noclass";
28783 * Utility method. Add a delete option to the DDView's context menu.
28784 * @param {String} imageUrl The URL of the "delete" icon image.
28786 setDeletable: function(imageUrl) {
28787 if (!this.singleSelect && !this.multiSelect) {
28788 this.singleSelect = true;
28790 var c = this.getContextMenu();
28791 this.contextMenu.on("itemclick", function(item) {
28794 this.remove(this.getSelectedIndexes());
28798 this.contextMenu.add({
28805 /** Return the context menu for this DDView. */
28806 getContextMenu: function() {
28807 if (!this.contextMenu) {
28808 // Create the View's context menu
28809 this.contextMenu = new Roo.menu.Menu({
28810 id: this.id + "-contextmenu"
28812 this.el.on("contextmenu", this.showContextMenu, this);
28814 return this.contextMenu;
28817 disableContextMenu: function() {
28818 if (this.contextMenu) {
28819 this.el.un("contextmenu", this.showContextMenu, this);
28823 showContextMenu: function(e, item) {
28824 item = this.findItemFromChild(e.getTarget());
28827 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28828 this.contextMenu.showAt(e.getXY());
28833 * Remove {@link Roo.data.Record}s at the specified indices.
28834 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28836 remove: function(selectedIndices) {
28837 selectedIndices = [].concat(selectedIndices);
28838 for (var i = 0; i < selectedIndices.length; i++) {
28839 var rec = this.store.getAt(selectedIndices[i]);
28840 this.store.remove(rec);
28845 * Double click fires the event, but also, if this is draggable, and there is only one other
28846 * related DropZone, it transfers the selected node.
28848 onDblClick : function(e){
28849 var item = this.findItemFromChild(e.getTarget());
28851 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28854 if (this.dragGroup) {
28855 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28856 while (targets.indexOf(this.dropZone) > -1) {
28857 targets.remove(this.dropZone);
28859 if (targets.length == 1) {
28860 this.dragZone.cachedTarget = null;
28861 var el = Roo.get(targets[0].getEl());
28862 var box = el.getBox(true);
28863 targets[0].onNodeDrop(el.dom, {
28865 xy: [box.x, box.y + box.height - 1]
28866 }, null, this.getDragData(e));
28872 handleSelection: function(e) {
28873 this.dragZone.cachedTarget = null;
28874 var item = this.findItemFromChild(e.getTarget());
28876 this.clearSelections(true);
28879 if (item && (this.multiSelect || this.singleSelect)){
28880 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28881 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28882 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28883 this.unselect(item);
28885 this.select(item, this.multiSelect && e.ctrlKey);
28886 this.lastSelection = item;
28891 onItemClick : function(item, index, e){
28892 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28898 unselect : function(nodeInfo, suppressEvent){
28899 var node = this.getNode(nodeInfo);
28900 if(node && this.isSelected(node)){
28901 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28902 Roo.fly(node).removeClass(this.selectedClass);
28903 this.selections.remove(node);
28904 if(!suppressEvent){
28905 this.fireEvent("selectionchange", this, this.selections);
28913 * Ext JS Library 1.1.1
28914 * Copyright(c) 2006-2007, Ext JS, LLC.
28916 * Originally Released Under LGPL - original licence link has changed is not relivant.
28919 * <script type="text/javascript">
28923 * @class Roo.LayoutManager
28924 * @extends Roo.util.Observable
28925 * Base class for layout managers.
28927 Roo.LayoutManager = function(container, config){
28928 Roo.LayoutManager.superclass.constructor.call(this);
28929 this.el = Roo.get(container);
28930 // ie scrollbar fix
28931 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28932 document.body.scroll = "no";
28933 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28934 this.el.position('relative');
28936 this.id = this.el.id;
28937 this.el.addClass("x-layout-container");
28938 /** false to disable window resize monitoring @type Boolean */
28939 this.monitorWindowResize = true;
28944 * Fires when a layout is performed.
28945 * @param {Roo.LayoutManager} this
28949 * @event regionresized
28950 * Fires when the user resizes a region.
28951 * @param {Roo.LayoutRegion} region The resized region
28952 * @param {Number} newSize The new size (width for east/west, height for north/south)
28954 "regionresized" : true,
28956 * @event regioncollapsed
28957 * Fires when a region is collapsed.
28958 * @param {Roo.LayoutRegion} region The collapsed region
28960 "regioncollapsed" : true,
28962 * @event regionexpanded
28963 * Fires when a region is expanded.
28964 * @param {Roo.LayoutRegion} region The expanded region
28966 "regionexpanded" : true
28968 this.updating = false;
28969 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28972 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28974 * Returns true if this layout is currently being updated
28975 * @return {Boolean}
28977 isUpdating : function(){
28978 return this.updating;
28982 * Suspend the LayoutManager from doing auto-layouts while
28983 * making multiple add or remove calls
28985 beginUpdate : function(){
28986 this.updating = true;
28990 * Restore auto-layouts and optionally disable the manager from performing a layout
28991 * @param {Boolean} noLayout true to disable a layout update
28993 endUpdate : function(noLayout){
28994 this.updating = false;
29000 layout: function(){
29004 onRegionResized : function(region, newSize){
29005 this.fireEvent("regionresized", region, newSize);
29009 onRegionCollapsed : function(region){
29010 this.fireEvent("regioncollapsed", region);
29013 onRegionExpanded : function(region){
29014 this.fireEvent("regionexpanded", region);
29018 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29019 * performs box-model adjustments.
29020 * @return {Object} The size as an object {width: (the width), height: (the height)}
29022 getViewSize : function(){
29024 if(this.el.dom != document.body){
29025 size = this.el.getSize();
29027 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29029 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29030 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29035 * Returns the Element this layout is bound to.
29036 * @return {Roo.Element}
29038 getEl : function(){
29043 * Returns the specified region.
29044 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29045 * @return {Roo.LayoutRegion}
29047 getRegion : function(target){
29048 return this.regions[target.toLowerCase()];
29051 onWindowResize : function(){
29052 if(this.monitorWindowResize){
29058 * Ext JS Library 1.1.1
29059 * Copyright(c) 2006-2007, Ext JS, LLC.
29061 * Originally Released Under LGPL - original licence link has changed is not relivant.
29064 * <script type="text/javascript">
29067 * @class Roo.BorderLayout
29068 * @extends Roo.LayoutManager
29069 * @children Roo.ContentPanel
29070 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29071 * please see: <br><br>
29072 * <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>
29073 * <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>
29076 var layout = new Roo.BorderLayout(document.body, {
29110 preferredTabWidth: 150
29115 var CP = Roo.ContentPanel;
29117 layout.beginUpdate();
29118 layout.add("north", new CP("north", "North"));
29119 layout.add("south", new CP("south", {title: "South", closable: true}));
29120 layout.add("west", new CP("west", {title: "West"}));
29121 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29122 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29123 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29124 layout.getRegion("center").showPanel("center1");
29125 layout.endUpdate();
29128 <b>The container the layout is rendered into can be either the body element or any other element.
29129 If it is not the body element, the container needs to either be an absolute positioned element,
29130 or you will need to add "position:relative" to the css of the container. You will also need to specify
29131 the container size if it is not the body element.</b>
29134 * Create a new BorderLayout
29135 * @param {String/HTMLElement/Element} container The container this layout is bound to
29136 * @param {Object} config Configuration options
29138 Roo.BorderLayout = function(container, config){
29139 config = config || {};
29140 Roo.BorderLayout.superclass.constructor.call(this, container, config);
29141 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29142 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29143 var target = this.factory.validRegions[i];
29144 if(config[target]){
29145 this.addRegion(target, config[target]);
29150 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29153 * @cfg {Roo.LayoutRegion} east
29156 * @cfg {Roo.LayoutRegion} west
29159 * @cfg {Roo.LayoutRegion} north
29162 * @cfg {Roo.LayoutRegion} south
29165 * @cfg {Roo.LayoutRegion} center
29168 * Creates and adds a new region if it doesn't already exist.
29169 * @param {String} target The target region key (north, south, east, west or center).
29170 * @param {Object} config The regions config object
29171 * @return {BorderLayoutRegion} The new region
29173 addRegion : function(target, config){
29174 if(!this.regions[target]){
29175 var r = this.factory.create(target, this, config);
29176 this.bindRegion(target, r);
29178 return this.regions[target];
29182 bindRegion : function(name, r){
29183 this.regions[name] = r;
29184 r.on("visibilitychange", this.layout, this);
29185 r.on("paneladded", this.layout, this);
29186 r.on("panelremoved", this.layout, this);
29187 r.on("invalidated", this.layout, this);
29188 r.on("resized", this.onRegionResized, this);
29189 r.on("collapsed", this.onRegionCollapsed, this);
29190 r.on("expanded", this.onRegionExpanded, this);
29194 * Performs a layout update.
29196 layout : function(){
29197 if(this.updating) {
29200 var size = this.getViewSize();
29201 var w = size.width;
29202 var h = size.height;
29207 //var x = 0, y = 0;
29209 var rs = this.regions;
29210 var north = rs["north"];
29211 var south = rs["south"];
29212 var west = rs["west"];
29213 var east = rs["east"];
29214 var center = rs["center"];
29215 //if(this.hideOnLayout){ // not supported anymore
29216 //c.el.setStyle("display", "none");
29218 if(north && north.isVisible()){
29219 var b = north.getBox();
29220 var m = north.getMargins();
29221 b.width = w - (m.left+m.right);
29224 centerY = b.height + b.y + m.bottom;
29225 centerH -= centerY;
29226 north.updateBox(this.safeBox(b));
29228 if(south && south.isVisible()){
29229 var b = south.getBox();
29230 var m = south.getMargins();
29231 b.width = w - (m.left+m.right);
29233 var totalHeight = (b.height + m.top + m.bottom);
29234 b.y = h - totalHeight + m.top;
29235 centerH -= totalHeight;
29236 south.updateBox(this.safeBox(b));
29238 if(west && west.isVisible()){
29239 var b = west.getBox();
29240 var m = west.getMargins();
29241 b.height = centerH - (m.top+m.bottom);
29243 b.y = centerY + m.top;
29244 var totalWidth = (b.width + m.left + m.right);
29245 centerX += totalWidth;
29246 centerW -= totalWidth;
29247 west.updateBox(this.safeBox(b));
29249 if(east && east.isVisible()){
29250 var b = east.getBox();
29251 var m = east.getMargins();
29252 b.height = centerH - (m.top+m.bottom);
29253 var totalWidth = (b.width + m.left + m.right);
29254 b.x = w - totalWidth + m.left;
29255 b.y = centerY + m.top;
29256 centerW -= totalWidth;
29257 east.updateBox(this.safeBox(b));
29260 var m = center.getMargins();
29262 x: centerX + m.left,
29263 y: centerY + m.top,
29264 width: centerW - (m.left+m.right),
29265 height: centerH - (m.top+m.bottom)
29267 //if(this.hideOnLayout){
29268 //center.el.setStyle("display", "block");
29270 center.updateBox(this.safeBox(centerBox));
29273 this.fireEvent("layout", this);
29277 safeBox : function(box){
29278 box.width = Math.max(0, box.width);
29279 box.height = Math.max(0, box.height);
29284 * Adds a ContentPanel (or subclass) to this layout.
29285 * @param {String} target The target region key (north, south, east, west or center).
29286 * @param {Roo.ContentPanel} panel The panel to add
29287 * @return {Roo.ContentPanel} The added panel
29289 add : function(target, panel){
29291 target = target.toLowerCase();
29292 return this.regions[target].add(panel);
29296 * Remove a ContentPanel (or subclass) to this layout.
29297 * @param {String} target The target region key (north, south, east, west or center).
29298 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29299 * @return {Roo.ContentPanel} The removed panel
29301 remove : function(target, panel){
29302 target = target.toLowerCase();
29303 return this.regions[target].remove(panel);
29307 * Searches all regions for a panel with the specified id
29308 * @param {String} panelId
29309 * @return {Roo.ContentPanel} The panel or null if it wasn't found
29311 findPanel : function(panelId){
29312 var rs = this.regions;
29313 for(var target in rs){
29314 if(typeof rs[target] != "function"){
29315 var p = rs[target].getPanel(panelId);
29325 * Searches all regions for a panel with the specified id and activates (shows) it.
29326 * @param {String/ContentPanel} panelId The panels id or the panel itself
29327 * @return {Roo.ContentPanel} The shown panel or null
29329 showPanel : function(panelId) {
29330 var rs = this.regions;
29331 for(var target in rs){
29332 var r = rs[target];
29333 if(typeof r != "function"){
29334 if(r.hasPanel(panelId)){
29335 return r.showPanel(panelId);
29343 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29344 * @param {Roo.state.Provider} provider (optional) An alternate state provider
29346 restoreState : function(provider){
29348 provider = Roo.state.Manager;
29350 var sm = new Roo.LayoutStateManager();
29351 sm.init(this, provider);
29355 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
29356 * object should contain properties for each region to add ContentPanels to, and each property's value should be
29357 * a valid ContentPanel config object. Example:
29359 // Create the main layout
29360 var layout = new Roo.BorderLayout('main-ct', {
29371 // Create and add multiple ContentPanels at once via configs
29374 id: 'source-files',
29376 title:'Ext Source Files',
29389 * @param {Object} regions An object containing ContentPanel configs by region name
29391 batchAdd : function(regions){
29392 this.beginUpdate();
29393 for(var rname in regions){
29394 var lr = this.regions[rname];
29396 this.addTypedPanels(lr, regions[rname]);
29403 addTypedPanels : function(lr, ps){
29404 if(typeof ps == 'string'){
29405 lr.add(new Roo.ContentPanel(ps));
29407 else if(ps instanceof Array){
29408 for(var i =0, len = ps.length; i < len; i++){
29409 this.addTypedPanels(lr, ps[i]);
29412 else if(!ps.events){ // raw config?
29414 delete ps.el; // prevent conflict
29415 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29417 else { // panel object assumed!
29422 * Adds a xtype elements to the layout.
29426 xtype : 'ContentPanel',
29433 xtype : 'NestedLayoutPanel',
29439 items : [ ... list of content panels or nested layout panels.. ]
29443 * @param {Object} cfg Xtype definition of item to add.
29445 addxtype : function(cfg)
29447 // basically accepts a pannel...
29448 // can accept a layout region..!?!?
29449 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29451 if (!cfg.xtype.match(/Panel$/)) {
29456 if (typeof(cfg.region) == 'undefined') {
29457 Roo.log("Failed to add Panel, region was not set");
29461 var region = cfg.region;
29467 xitems = cfg.items;
29474 case 'ContentPanel': // ContentPanel (el, cfg)
29475 case 'ScrollPanel': // ContentPanel (el, cfg)
29477 if(cfg.autoCreate) {
29478 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29480 var el = this.el.createChild();
29481 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29484 this.add(region, ret);
29488 case 'TreePanel': // our new panel!
29489 cfg.el = this.el.createChild();
29490 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29491 this.add(region, ret);
29494 case 'NestedLayoutPanel':
29495 // create a new Layout (which is a Border Layout...
29496 var el = this.el.createChild();
29497 var clayout = cfg.layout;
29499 clayout.items = clayout.items || [];
29500 // replace this exitems with the clayout ones..
29501 xitems = clayout.items;
29504 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29505 cfg.background = false;
29507 var layout = new Roo.BorderLayout(el, clayout);
29509 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29510 //console.log('adding nested layout panel ' + cfg.toSource());
29511 this.add(region, ret);
29512 nb = {}; /// find first...
29517 // needs grid and region
29519 //var el = this.getRegion(region).el.createChild();
29520 var el = this.el.createChild();
29521 // create the grid first...
29523 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29525 if (region == 'center' && this.active ) {
29526 cfg.background = false;
29528 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29530 this.add(region, ret);
29531 if (cfg.background) {
29532 ret.on('activate', function(gp) {
29533 if (!gp.grid.rendered) {
29548 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29550 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29551 this.add(region, ret);
29554 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29558 // GridPanel (grid, cfg)
29561 this.beginUpdate();
29565 Roo.each(xitems, function(i) {
29566 region = nb && i.region ? i.region : false;
29568 var add = ret.addxtype(i);
29571 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29572 if (!i.background) {
29573 abn[region] = nb[region] ;
29580 // make the last non-background panel active..
29581 //if (nb) { Roo.log(abn); }
29584 for(var r in abn) {
29585 region = this.getRegion(r);
29587 // tried using nb[r], but it does not work..
29589 region.showPanel(abn[r]);
29600 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29601 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29602 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29603 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29606 var CP = Roo.ContentPanel;
29608 var layout = Roo.BorderLayout.create({
29612 panels: [new CP("north", "North")]
29621 panels: [new CP("west", {title: "West"})]
29630 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29639 panels: [new CP("south", {title: "South", closable: true})]
29646 preferredTabWidth: 150,
29648 new CP("center1", {title: "Close Me", closable: true}),
29649 new CP("center2", {title: "Center Panel", closable: false})
29654 layout.getRegion("center").showPanel("center1");
29659 Roo.BorderLayout.create = function(config, targetEl){
29660 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29661 layout.beginUpdate();
29662 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29663 for(var j = 0, jlen = regions.length; j < jlen; j++){
29664 var lr = regions[j];
29665 if(layout.regions[lr] && config[lr].panels){
29666 var r = layout.regions[lr];
29667 var ps = config[lr].panels;
29668 layout.addTypedPanels(r, ps);
29671 layout.endUpdate();
29676 Roo.BorderLayout.RegionFactory = {
29678 validRegions : ["north","south","east","west","center"],
29681 create : function(target, mgr, config){
29682 target = target.toLowerCase();
29683 if(config.lightweight || config.basic){
29684 return new Roo.BasicLayoutRegion(mgr, config, target);
29688 return new Roo.NorthLayoutRegion(mgr, config);
29690 return new Roo.SouthLayoutRegion(mgr, config);
29692 return new Roo.EastLayoutRegion(mgr, config);
29694 return new Roo.WestLayoutRegion(mgr, config);
29696 return new Roo.CenterLayoutRegion(mgr, config);
29698 throw 'Layout region "'+target+'" not supported.';
29702 * Ext JS Library 1.1.1
29703 * Copyright(c) 2006-2007, Ext JS, LLC.
29705 * Originally Released Under LGPL - original licence link has changed is not relivant.
29708 * <script type="text/javascript">
29712 * @class Roo.BasicLayoutRegion
29713 * @extends Roo.util.Observable
29714 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29715 * and does not have a titlebar, tabs or any other features. All it does is size and position
29716 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29718 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29720 this.position = pos;
29723 * @scope Roo.BasicLayoutRegion
29727 * @event beforeremove
29728 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29729 * @param {Roo.LayoutRegion} this
29730 * @param {Roo.ContentPanel} panel The panel
29731 * @param {Object} e The cancel event object
29733 "beforeremove" : true,
29735 * @event invalidated
29736 * Fires when the layout for this region is changed.
29737 * @param {Roo.LayoutRegion} this
29739 "invalidated" : true,
29741 * @event visibilitychange
29742 * Fires when this region is shown or hidden
29743 * @param {Roo.LayoutRegion} this
29744 * @param {Boolean} visibility true or false
29746 "visibilitychange" : true,
29748 * @event paneladded
29749 * Fires when a panel is added.
29750 * @param {Roo.LayoutRegion} this
29751 * @param {Roo.ContentPanel} panel The panel
29753 "paneladded" : true,
29755 * @event panelremoved
29756 * Fires when a panel is removed.
29757 * @param {Roo.LayoutRegion} this
29758 * @param {Roo.ContentPanel} panel The panel
29760 "panelremoved" : true,
29762 * @event beforecollapse
29763 * Fires when this region before collapse.
29764 * @param {Roo.LayoutRegion} this
29766 "beforecollapse" : true,
29769 * Fires when this region is collapsed.
29770 * @param {Roo.LayoutRegion} this
29772 "collapsed" : true,
29775 * Fires when this region is expanded.
29776 * @param {Roo.LayoutRegion} this
29781 * Fires when this region is slid into view.
29782 * @param {Roo.LayoutRegion} this
29784 "slideshow" : true,
29787 * Fires when this region slides out of view.
29788 * @param {Roo.LayoutRegion} this
29790 "slidehide" : true,
29792 * @event panelactivated
29793 * Fires when a panel is activated.
29794 * @param {Roo.LayoutRegion} this
29795 * @param {Roo.ContentPanel} panel The activated panel
29797 "panelactivated" : true,
29800 * Fires when the user resizes this region.
29801 * @param {Roo.LayoutRegion} this
29802 * @param {Number} newSize The new size (width for east/west, height for north/south)
29806 /** A collection of panels in this region. @type Roo.util.MixedCollection */
29807 this.panels = new Roo.util.MixedCollection();
29808 this.panels.getKey = this.getPanelId.createDelegate(this);
29810 this.activePanel = null;
29811 // ensure listeners are added...
29813 if (config.listeners || config.events) {
29814 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29815 listeners : config.listeners || {},
29816 events : config.events || {}
29820 if(skipConfig !== true){
29821 this.applyConfig(config);
29825 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29826 getPanelId : function(p){
29830 applyConfig : function(config){
29831 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29832 this.config = config;
29837 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
29838 * the width, for horizontal (north, south) the height.
29839 * @param {Number} newSize The new width or height
29841 resizeTo : function(newSize){
29842 var el = this.el ? this.el :
29843 (this.activePanel ? this.activePanel.getEl() : null);
29845 switch(this.position){
29848 el.setWidth(newSize);
29849 this.fireEvent("resized", this, newSize);
29853 el.setHeight(newSize);
29854 this.fireEvent("resized", this, newSize);
29860 getBox : function(){
29861 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29864 getMargins : function(){
29865 return this.margins;
29868 updateBox : function(box){
29870 var el = this.activePanel.getEl();
29871 el.dom.style.left = box.x + "px";
29872 el.dom.style.top = box.y + "px";
29873 this.activePanel.setSize(box.width, box.height);
29877 * Returns the container element for this region.
29878 * @return {Roo.Element}
29880 getEl : function(){
29881 return this.activePanel;
29885 * Returns true if this region is currently visible.
29886 * @return {Boolean}
29888 isVisible : function(){
29889 return this.activePanel ? true : false;
29892 setActivePanel : function(panel){
29893 panel = this.getPanel(panel);
29894 if(this.activePanel && this.activePanel != panel){
29895 this.activePanel.setActiveState(false);
29896 this.activePanel.getEl().setLeftTop(-10000,-10000);
29898 this.activePanel = panel;
29899 panel.setActiveState(true);
29901 panel.setSize(this.box.width, this.box.height);
29903 this.fireEvent("panelactivated", this, panel);
29904 this.fireEvent("invalidated");
29908 * Show the specified panel.
29909 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29910 * @return {Roo.ContentPanel} The shown panel or null
29912 showPanel : function(panel){
29913 if(panel = this.getPanel(panel)){
29914 this.setActivePanel(panel);
29920 * Get the active panel for this region.
29921 * @return {Roo.ContentPanel} The active panel or null
29923 getActivePanel : function(){
29924 return this.activePanel;
29928 * Add the passed ContentPanel(s)
29929 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29930 * @return {Roo.ContentPanel} The panel added (if only one was added)
29932 add : function(panel){
29933 if(arguments.length > 1){
29934 for(var i = 0, len = arguments.length; i < len; i++) {
29935 this.add(arguments[i]);
29939 if(this.hasPanel(panel)){
29940 this.showPanel(panel);
29943 var el = panel.getEl();
29944 if(el.dom.parentNode != this.mgr.el.dom){
29945 this.mgr.el.dom.appendChild(el.dom);
29947 if(panel.setRegion){
29948 panel.setRegion(this);
29950 this.panels.add(panel);
29951 el.setStyle("position", "absolute");
29952 if(!panel.background){
29953 this.setActivePanel(panel);
29954 if(this.config.initialSize && this.panels.getCount()==1){
29955 this.resizeTo(this.config.initialSize);
29958 this.fireEvent("paneladded", this, panel);
29963 * Returns true if the panel is in this region.
29964 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29965 * @return {Boolean}
29967 hasPanel : function(panel){
29968 if(typeof panel == "object"){ // must be panel obj
29969 panel = panel.getId();
29971 return this.getPanel(panel) ? true : false;
29975 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29976 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29977 * @param {Boolean} preservePanel Overrides the config preservePanel option
29978 * @return {Roo.ContentPanel} The panel that was removed
29980 remove : function(panel, preservePanel){
29981 panel = this.getPanel(panel);
29986 this.fireEvent("beforeremove", this, panel, e);
29987 if(e.cancel === true){
29990 var panelId = panel.getId();
29991 this.panels.removeKey(panelId);
29996 * Returns the panel specified or null if it's not in this region.
29997 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29998 * @return {Roo.ContentPanel}
30000 getPanel : function(id){
30001 if(typeof id == "object"){ // must be panel obj
30004 return this.panels.get(id);
30008 * Returns this regions position (north/south/east/west/center).
30011 getPosition: function(){
30012 return this.position;
30016 * Ext JS Library 1.1.1
30017 * Copyright(c) 2006-2007, Ext JS, LLC.
30019 * Originally Released Under LGPL - original licence link has changed is not relivant.
30022 * <script type="text/javascript">
30026 * @class Roo.LayoutRegion
30027 * @extends Roo.BasicLayoutRegion
30028 * This class represents a region in a layout manager.
30029 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
30030 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
30031 * @cfg {Boolean} floatable False to disable floating (defaults to true)
30032 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30033 * @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})
30034 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
30035 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
30036 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
30037 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
30038 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
30039 * @cfg {String} title The title for the region (overrides panel titles)
30040 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
30041 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30042 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
30043 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30044 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
30045 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30046 * the space available, similar to FireFox 1.5 tabs (defaults to false)
30047 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
30048 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
30049 * @cfg {Boolean} showPin True to show a pin button
30050 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
30051 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
30052 * @cfg {Boolean} disableTabTips True to disable tab tooltips
30053 * @cfg {Number} width For East/West panels
30054 * @cfg {Number} height For North/South panels
30055 * @cfg {Boolean} split To show the splitter
30056 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
30058 Roo.LayoutRegion = function(mgr, config, pos){
30059 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30060 var dh = Roo.DomHelper;
30061 /** This region's container element
30062 * @type Roo.Element */
30063 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30064 /** This region's title element
30065 * @type Roo.Element */
30067 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30068 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
30069 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30071 this.titleEl.enableDisplayMode();
30072 /** This region's title text element
30073 * @type HTMLElement */
30074 this.titleTextEl = this.titleEl.dom.firstChild;
30075 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30076 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30077 this.closeBtn.enableDisplayMode();
30078 this.closeBtn.on("click", this.closeClicked, this);
30079 this.closeBtn.hide();
30081 this.createBody(config);
30082 this.visible = true;
30083 this.collapsed = false;
30085 if(config.hideWhenEmpty){
30087 this.on("paneladded", this.validateVisibility, this);
30088 this.on("panelremoved", this.validateVisibility, this);
30090 this.applyConfig(config);
30093 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30095 createBody : function(){
30096 /** This region's body element
30097 * @type Roo.Element */
30098 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30101 applyConfig : function(c){
30102 if(c.collapsible && this.position != "center" && !this.collapsedEl){
30103 var dh = Roo.DomHelper;
30104 if(c.titlebar !== false){
30105 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30106 this.collapseBtn.on("click", this.collapse, this);
30107 this.collapseBtn.enableDisplayMode();
30109 if(c.showPin === true || this.showPin){
30110 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30111 this.stickBtn.enableDisplayMode();
30112 this.stickBtn.on("click", this.expand, this);
30113 this.stickBtn.hide();
30116 /** This region's collapsed element
30117 * @type Roo.Element */
30118 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30119 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30121 if(c.floatable !== false){
30122 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30123 this.collapsedEl.on("click", this.collapseClick, this);
30126 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30127 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30128 id: "message", unselectable: "on", style:{"float":"left"}});
30129 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30131 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30132 this.expandBtn.on("click", this.expand, this);
30134 if(this.collapseBtn){
30135 this.collapseBtn.setVisible(c.collapsible == true);
30137 this.cmargins = c.cmargins || this.cmargins ||
30138 (this.position == "west" || this.position == "east" ?
30139 {top: 0, left: 2, right:2, bottom: 0} :
30140 {top: 2, left: 0, right:0, bottom: 2});
30141 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30142 this.bottomTabs = c.tabPosition != "top";
30143 this.autoScroll = c.autoScroll || false;
30144 if(this.autoScroll){
30145 this.bodyEl.setStyle("overflow", "auto");
30147 this.bodyEl.setStyle("overflow", "hidden");
30149 //if(c.titlebar !== false){
30150 if((!c.titlebar && !c.title) || c.titlebar === false){
30151 this.titleEl.hide();
30153 this.titleEl.show();
30155 this.titleTextEl.innerHTML = c.title;
30159 this.duration = c.duration || .30;
30160 this.slideDuration = c.slideDuration || .45;
30163 this.collapse(true);
30170 * Returns true if this region is currently visible.
30171 * @return {Boolean}
30173 isVisible : function(){
30174 return this.visible;
30178 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30179 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
30181 setCollapsedTitle : function(title){
30182 title = title || " ";
30183 if(this.collapsedTitleTextEl){
30184 this.collapsedTitleTextEl.innerHTML = title;
30188 getBox : function(){
30190 if(!this.collapsed){
30191 b = this.el.getBox(false, true);
30193 b = this.collapsedEl.getBox(false, true);
30198 getMargins : function(){
30199 return this.collapsed ? this.cmargins : this.margins;
30202 highlight : function(){
30203 this.el.addClass("x-layout-panel-dragover");
30206 unhighlight : function(){
30207 this.el.removeClass("x-layout-panel-dragover");
30210 updateBox : function(box){
30212 if(!this.collapsed){
30213 this.el.dom.style.left = box.x + "px";
30214 this.el.dom.style.top = box.y + "px";
30215 this.updateBody(box.width, box.height);
30217 this.collapsedEl.dom.style.left = box.x + "px";
30218 this.collapsedEl.dom.style.top = box.y + "px";
30219 this.collapsedEl.setSize(box.width, box.height);
30222 this.tabs.autoSizeTabs();
30226 updateBody : function(w, h){
30228 this.el.setWidth(w);
30229 w -= this.el.getBorderWidth("rl");
30230 if(this.config.adjustments){
30231 w += this.config.adjustments[0];
30235 this.el.setHeight(h);
30236 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30237 h -= this.el.getBorderWidth("tb");
30238 if(this.config.adjustments){
30239 h += this.config.adjustments[1];
30241 this.bodyEl.setHeight(h);
30243 h = this.tabs.syncHeight(h);
30246 if(this.panelSize){
30247 w = w !== null ? w : this.panelSize.width;
30248 h = h !== null ? h : this.panelSize.height;
30250 if(this.activePanel){
30251 var el = this.activePanel.getEl();
30252 w = w !== null ? w : el.getWidth();
30253 h = h !== null ? h : el.getHeight();
30254 this.panelSize = {width: w, height: h};
30255 this.activePanel.setSize(w, h);
30257 if(Roo.isIE && this.tabs){
30258 this.tabs.el.repaint();
30263 * Returns the container element for this region.
30264 * @return {Roo.Element}
30266 getEl : function(){
30271 * Hides this region.
30274 if(!this.collapsed){
30275 this.el.dom.style.left = "-2000px";
30278 this.collapsedEl.dom.style.left = "-2000px";
30279 this.collapsedEl.hide();
30281 this.visible = false;
30282 this.fireEvent("visibilitychange", this, false);
30286 * Shows this region if it was previously hidden.
30289 if(!this.collapsed){
30292 this.collapsedEl.show();
30294 this.visible = true;
30295 this.fireEvent("visibilitychange", this, true);
30298 closeClicked : function(){
30299 if(this.activePanel){
30300 this.remove(this.activePanel);
30304 collapseClick : function(e){
30306 e.stopPropagation();
30309 e.stopPropagation();
30315 * Collapses this region.
30316 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30318 collapse : function(skipAnim, skipCheck){
30319 if(this.collapsed) {
30323 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30325 this.collapsed = true;
30327 this.split.el.hide();
30329 if(this.config.animate && skipAnim !== true){
30330 this.fireEvent("invalidated", this);
30331 this.animateCollapse();
30333 this.el.setLocation(-20000,-20000);
30335 this.collapsedEl.show();
30336 this.fireEvent("collapsed", this);
30337 this.fireEvent("invalidated", this);
30343 animateCollapse : function(){
30348 * Expands this region if it was previously collapsed.
30349 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30350 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30352 expand : function(e, skipAnim){
30354 e.stopPropagation();
30356 if(!this.collapsed || this.el.hasActiveFx()) {
30360 this.afterSlideIn();
30363 this.collapsed = false;
30364 if(this.config.animate && skipAnim !== true){
30365 this.animateExpand();
30369 this.split.el.show();
30371 this.collapsedEl.setLocation(-2000,-2000);
30372 this.collapsedEl.hide();
30373 this.fireEvent("invalidated", this);
30374 this.fireEvent("expanded", this);
30378 animateExpand : function(){
30382 initTabs : function()
30384 this.bodyEl.setStyle("overflow", "hidden");
30385 var ts = new Roo.TabPanel(
30388 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30389 disableTooltips: this.config.disableTabTips,
30390 toolbar : this.config.toolbar
30393 if(this.config.hideTabs){
30394 ts.stripWrap.setDisplayed(false);
30397 ts.resizeTabs = this.config.resizeTabs === true;
30398 ts.minTabWidth = this.config.minTabWidth || 40;
30399 ts.maxTabWidth = this.config.maxTabWidth || 250;
30400 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30401 ts.monitorResize = false;
30402 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30403 ts.bodyEl.addClass('x-layout-tabs-body');
30404 this.panels.each(this.initPanelAsTab, this);
30407 initPanelAsTab : function(panel){
30408 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30409 this.config.closeOnTab && panel.isClosable());
30410 if(panel.tabTip !== undefined){
30411 ti.setTooltip(panel.tabTip);
30413 ti.on("activate", function(){
30414 this.setActivePanel(panel);
30416 if(this.config.closeOnTab){
30417 ti.on("beforeclose", function(t, e){
30419 this.remove(panel);
30425 updatePanelTitle : function(panel, title){
30426 if(this.activePanel == panel){
30427 this.updateTitle(title);
30430 var ti = this.tabs.getTab(panel.getEl().id);
30432 if(panel.tabTip !== undefined){
30433 ti.setTooltip(panel.tabTip);
30438 updateTitle : function(title){
30439 if(this.titleTextEl && !this.config.title){
30440 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30444 setActivePanel : function(panel){
30445 panel = this.getPanel(panel);
30446 if(this.activePanel && this.activePanel != panel){
30447 this.activePanel.setActiveState(false);
30449 this.activePanel = panel;
30450 panel.setActiveState(true);
30451 if(this.panelSize){
30452 panel.setSize(this.panelSize.width, this.panelSize.height);
30455 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30457 this.updateTitle(panel.getTitle());
30459 this.fireEvent("invalidated", this);
30461 this.fireEvent("panelactivated", this, panel);
30465 * Shows the specified panel.
30466 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30467 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30469 showPanel : function(panel)
30471 panel = this.getPanel(panel);
30474 var tab = this.tabs.getTab(panel.getEl().id);
30475 if(tab.isHidden()){
30476 this.tabs.unhideTab(tab.id);
30480 this.setActivePanel(panel);
30487 * Get the active panel for this region.
30488 * @return {Roo.ContentPanel} The active panel or null
30490 getActivePanel : function(){
30491 return this.activePanel;
30494 validateVisibility : function(){
30495 if(this.panels.getCount() < 1){
30496 this.updateTitle(" ");
30497 this.closeBtn.hide();
30500 if(!this.isVisible()){
30507 * Adds the passed ContentPanel(s) to this region.
30508 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30509 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30511 add : function(panel){
30512 if(arguments.length > 1){
30513 for(var i = 0, len = arguments.length; i < len; i++) {
30514 this.add(arguments[i]);
30518 if(this.hasPanel(panel)){
30519 this.showPanel(panel);
30522 panel.setRegion(this);
30523 this.panels.add(panel);
30524 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30525 this.bodyEl.dom.appendChild(panel.getEl().dom);
30526 if(panel.background !== true){
30527 this.setActivePanel(panel);
30529 this.fireEvent("paneladded", this, panel);
30535 this.initPanelAsTab(panel);
30537 if(panel.background !== true){
30538 this.tabs.activate(panel.getEl().id);
30540 this.fireEvent("paneladded", this, panel);
30545 * Hides the tab for the specified panel.
30546 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30548 hidePanel : function(panel){
30549 if(this.tabs && (panel = this.getPanel(panel))){
30550 this.tabs.hideTab(panel.getEl().id);
30555 * Unhides the tab for a previously hidden panel.
30556 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30558 unhidePanel : function(panel){
30559 if(this.tabs && (panel = this.getPanel(panel))){
30560 this.tabs.unhideTab(panel.getEl().id);
30564 clearPanels : function(){
30565 while(this.panels.getCount() > 0){
30566 this.remove(this.panels.first());
30571 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30572 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30573 * @param {Boolean} preservePanel Overrides the config preservePanel option
30574 * @return {Roo.ContentPanel} The panel that was removed
30576 remove : function(panel, preservePanel){
30577 panel = this.getPanel(panel);
30582 this.fireEvent("beforeremove", this, panel, e);
30583 if(e.cancel === true){
30586 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30587 var panelId = panel.getId();
30588 this.panels.removeKey(panelId);
30590 document.body.appendChild(panel.getEl().dom);
30593 this.tabs.removeTab(panel.getEl().id);
30594 }else if (!preservePanel){
30595 this.bodyEl.dom.removeChild(panel.getEl().dom);
30597 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30598 var p = this.panels.first();
30599 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30600 tempEl.appendChild(p.getEl().dom);
30601 this.bodyEl.update("");
30602 this.bodyEl.dom.appendChild(p.getEl().dom);
30604 this.updateTitle(p.getTitle());
30606 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30607 this.setActivePanel(p);
30609 panel.setRegion(null);
30610 if(this.activePanel == panel){
30611 this.activePanel = null;
30613 if(this.config.autoDestroy !== false && preservePanel !== true){
30614 try{panel.destroy();}catch(e){}
30616 this.fireEvent("panelremoved", this, panel);
30621 * Returns the TabPanel component used by this region
30622 * @return {Roo.TabPanel}
30624 getTabs : function(){
30628 createTool : function(parentEl, className){
30629 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30630 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30631 btn.addClassOnOver("x-layout-tools-button-over");
30636 * Ext JS Library 1.1.1
30637 * Copyright(c) 2006-2007, Ext JS, LLC.
30639 * Originally Released Under LGPL - original licence link has changed is not relivant.
30642 * <script type="text/javascript">
30648 * @class Roo.SplitLayoutRegion
30649 * @extends Roo.LayoutRegion
30650 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30652 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30653 this.cursor = cursor;
30654 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30657 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30658 splitTip : "Drag to resize.",
30659 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30660 useSplitTips : false,
30662 applyConfig : function(config){
30663 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30666 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30667 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30668 /** The SplitBar for this region
30669 * @type Roo.SplitBar */
30670 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30671 this.split.on("moved", this.onSplitMove, this);
30672 this.split.useShim = config.useShim === true;
30673 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30674 if(this.useSplitTips){
30675 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30677 if(config.collapsible){
30678 this.split.el.on("dblclick", this.collapse, this);
30681 if(typeof config.minSize != "undefined"){
30682 this.split.minSize = config.minSize;
30684 if(typeof config.maxSize != "undefined"){
30685 this.split.maxSize = config.maxSize;
30687 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30688 this.hideSplitter();
30693 getHMaxSize : function(){
30694 var cmax = this.config.maxSize || 10000;
30695 var center = this.mgr.getRegion("center");
30696 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30699 getVMaxSize : function(){
30700 var cmax = this.config.maxSize || 10000;
30701 var center = this.mgr.getRegion("center");
30702 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30705 onSplitMove : function(split, newSize){
30706 this.fireEvent("resized", this, newSize);
30710 * Returns the {@link Roo.SplitBar} for this region.
30711 * @return {Roo.SplitBar}
30713 getSplitBar : function(){
30718 this.hideSplitter();
30719 Roo.SplitLayoutRegion.superclass.hide.call(this);
30722 hideSplitter : function(){
30724 this.split.el.setLocation(-2000,-2000);
30725 this.split.el.hide();
30731 this.split.el.show();
30733 Roo.SplitLayoutRegion.superclass.show.call(this);
30736 beforeSlide: function(){
30737 if(Roo.isGecko){// firefox overflow auto bug workaround
30738 this.bodyEl.clip();
30740 this.tabs.bodyEl.clip();
30742 if(this.activePanel){
30743 this.activePanel.getEl().clip();
30745 if(this.activePanel.beforeSlide){
30746 this.activePanel.beforeSlide();
30752 afterSlide : function(){
30753 if(Roo.isGecko){// firefox overflow auto bug workaround
30754 this.bodyEl.unclip();
30756 this.tabs.bodyEl.unclip();
30758 if(this.activePanel){
30759 this.activePanel.getEl().unclip();
30760 if(this.activePanel.afterSlide){
30761 this.activePanel.afterSlide();
30767 initAutoHide : function(){
30768 if(this.autoHide !== false){
30769 if(!this.autoHideHd){
30770 var st = new Roo.util.DelayedTask(this.slideIn, this);
30771 this.autoHideHd = {
30772 "mouseout": function(e){
30773 if(!e.within(this.el, true)){
30777 "mouseover" : function(e){
30783 this.el.on(this.autoHideHd);
30787 clearAutoHide : function(){
30788 if(this.autoHide !== false){
30789 this.el.un("mouseout", this.autoHideHd.mouseout);
30790 this.el.un("mouseover", this.autoHideHd.mouseover);
30794 clearMonitor : function(){
30795 Roo.get(document).un("click", this.slideInIf, this);
30798 // these names are backwards but not changed for compat
30799 slideOut : function(){
30800 if(this.isSlid || this.el.hasActiveFx()){
30803 this.isSlid = true;
30804 if(this.collapseBtn){
30805 this.collapseBtn.hide();
30807 this.closeBtnState = this.closeBtn.getStyle('display');
30808 this.closeBtn.hide();
30810 this.stickBtn.show();
30813 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30814 this.beforeSlide();
30815 this.el.setStyle("z-index", 10001);
30816 this.el.slideIn(this.getSlideAnchor(), {
30817 callback: function(){
30819 this.initAutoHide();
30820 Roo.get(document).on("click", this.slideInIf, this);
30821 this.fireEvent("slideshow", this);
30828 afterSlideIn : function(){
30829 this.clearAutoHide();
30830 this.isSlid = false;
30831 this.clearMonitor();
30832 this.el.setStyle("z-index", "");
30833 if(this.collapseBtn){
30834 this.collapseBtn.show();
30836 this.closeBtn.setStyle('display', this.closeBtnState);
30838 this.stickBtn.hide();
30840 this.fireEvent("slidehide", this);
30843 slideIn : function(cb){
30844 if(!this.isSlid || this.el.hasActiveFx()){
30848 this.isSlid = false;
30849 this.beforeSlide();
30850 this.el.slideOut(this.getSlideAnchor(), {
30851 callback: function(){
30852 this.el.setLeftTop(-10000, -10000);
30854 this.afterSlideIn();
30862 slideInIf : function(e){
30863 if(!e.within(this.el)){
30868 animateCollapse : function(){
30869 this.beforeSlide();
30870 this.el.setStyle("z-index", 20000);
30871 var anchor = this.getSlideAnchor();
30872 this.el.slideOut(anchor, {
30873 callback : function(){
30874 this.el.setStyle("z-index", "");
30875 this.collapsedEl.slideIn(anchor, {duration:.3});
30877 this.el.setLocation(-10000,-10000);
30879 this.fireEvent("collapsed", this);
30886 animateExpand : function(){
30887 this.beforeSlide();
30888 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30889 this.el.setStyle("z-index", 20000);
30890 this.collapsedEl.hide({
30893 this.el.slideIn(this.getSlideAnchor(), {
30894 callback : function(){
30895 this.el.setStyle("z-index", "");
30898 this.split.el.show();
30900 this.fireEvent("invalidated", this);
30901 this.fireEvent("expanded", this);
30929 getAnchor : function(){
30930 return this.anchors[this.position];
30933 getCollapseAnchor : function(){
30934 return this.canchors[this.position];
30937 getSlideAnchor : function(){
30938 return this.sanchors[this.position];
30941 getAlignAdj : function(){
30942 var cm = this.cmargins;
30943 switch(this.position){
30959 getExpandAdj : function(){
30960 var c = this.collapsedEl, cm = this.cmargins;
30961 switch(this.position){
30963 return [-(cm.right+c.getWidth()+cm.left), 0];
30966 return [cm.right+c.getWidth()+cm.left, 0];
30969 return [0, -(cm.top+cm.bottom+c.getHeight())];
30972 return [0, cm.top+cm.bottom+c.getHeight()];
30978 * Ext JS Library 1.1.1
30979 * Copyright(c) 2006-2007, Ext JS, LLC.
30981 * Originally Released Under LGPL - original licence link has changed is not relivant.
30984 * <script type="text/javascript">
30987 * These classes are private internal classes
30989 Roo.CenterLayoutRegion = function(mgr, config){
30990 Roo.LayoutRegion.call(this, mgr, config, "center");
30991 this.visible = true;
30992 this.minWidth = config.minWidth || 20;
30993 this.minHeight = config.minHeight || 20;
30996 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30998 // center panel can't be hidden
31002 // center panel can't be hidden
31005 getMinWidth: function(){
31006 return this.minWidth;
31009 getMinHeight: function(){
31010 return this.minHeight;
31015 Roo.NorthLayoutRegion = function(mgr, config){
31016 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31018 this.split.placement = Roo.SplitBar.TOP;
31019 this.split.orientation = Roo.SplitBar.VERTICAL;
31020 this.split.el.addClass("x-layout-split-v");
31022 var size = config.initialSize || config.height;
31023 if(typeof size != "undefined"){
31024 this.el.setHeight(size);
31027 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31028 orientation: Roo.SplitBar.VERTICAL,
31029 getBox : function(){
31030 if(this.collapsed){
31031 return this.collapsedEl.getBox();
31033 var box = this.el.getBox();
31035 box.height += this.split.el.getHeight();
31040 updateBox : function(box){
31041 if(this.split && !this.collapsed){
31042 box.height -= this.split.el.getHeight();
31043 this.split.el.setLeft(box.x);
31044 this.split.el.setTop(box.y+box.height);
31045 this.split.el.setWidth(box.width);
31047 if(this.collapsed){
31048 this.updateBody(box.width, null);
31050 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31054 Roo.SouthLayoutRegion = function(mgr, config){
31055 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31057 this.split.placement = Roo.SplitBar.BOTTOM;
31058 this.split.orientation = Roo.SplitBar.VERTICAL;
31059 this.split.el.addClass("x-layout-split-v");
31061 var size = config.initialSize || config.height;
31062 if(typeof size != "undefined"){
31063 this.el.setHeight(size);
31066 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31067 orientation: Roo.SplitBar.VERTICAL,
31068 getBox : function(){
31069 if(this.collapsed){
31070 return this.collapsedEl.getBox();
31072 var box = this.el.getBox();
31074 var sh = this.split.el.getHeight();
31081 updateBox : function(box){
31082 if(this.split && !this.collapsed){
31083 var sh = this.split.el.getHeight();
31086 this.split.el.setLeft(box.x);
31087 this.split.el.setTop(box.y-sh);
31088 this.split.el.setWidth(box.width);
31090 if(this.collapsed){
31091 this.updateBody(box.width, null);
31093 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31097 Roo.EastLayoutRegion = function(mgr, config){
31098 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31100 this.split.placement = Roo.SplitBar.RIGHT;
31101 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31102 this.split.el.addClass("x-layout-split-h");
31104 var size = config.initialSize || config.width;
31105 if(typeof size != "undefined"){
31106 this.el.setWidth(size);
31109 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31110 orientation: Roo.SplitBar.HORIZONTAL,
31111 getBox : function(){
31112 if(this.collapsed){
31113 return this.collapsedEl.getBox();
31115 var box = this.el.getBox();
31117 var sw = this.split.el.getWidth();
31124 updateBox : function(box){
31125 if(this.split && !this.collapsed){
31126 var sw = this.split.el.getWidth();
31128 this.split.el.setLeft(box.x);
31129 this.split.el.setTop(box.y);
31130 this.split.el.setHeight(box.height);
31133 if(this.collapsed){
31134 this.updateBody(null, box.height);
31136 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31140 Roo.WestLayoutRegion = function(mgr, config){
31141 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31143 this.split.placement = Roo.SplitBar.LEFT;
31144 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31145 this.split.el.addClass("x-layout-split-h");
31147 var size = config.initialSize || config.width;
31148 if(typeof size != "undefined"){
31149 this.el.setWidth(size);
31152 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31153 orientation: Roo.SplitBar.HORIZONTAL,
31154 getBox : function(){
31155 if(this.collapsed){
31156 return this.collapsedEl.getBox();
31158 var box = this.el.getBox();
31160 box.width += this.split.el.getWidth();
31165 updateBox : function(box){
31166 if(this.split && !this.collapsed){
31167 var sw = this.split.el.getWidth();
31169 this.split.el.setLeft(box.x+box.width);
31170 this.split.el.setTop(box.y);
31171 this.split.el.setHeight(box.height);
31173 if(this.collapsed){
31174 this.updateBody(null, box.height);
31176 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31181 * Ext JS Library 1.1.1
31182 * Copyright(c) 2006-2007, Ext JS, LLC.
31184 * Originally Released Under LGPL - original licence link has changed is not relivant.
31187 * <script type="text/javascript">
31192 * Private internal class for reading and applying state
31194 Roo.LayoutStateManager = function(layout){
31195 // default empty state
31204 Roo.LayoutStateManager.prototype = {
31205 init : function(layout, provider){
31206 this.provider = provider;
31207 var state = provider.get(layout.id+"-layout-state");
31209 var wasUpdating = layout.isUpdating();
31211 layout.beginUpdate();
31213 for(var key in state){
31214 if(typeof state[key] != "function"){
31215 var rstate = state[key];
31216 var r = layout.getRegion(key);
31219 r.resizeTo(rstate.size);
31221 if(rstate.collapsed == true){
31224 r.expand(null, true);
31230 layout.endUpdate();
31232 this.state = state;
31234 this.layout = layout;
31235 layout.on("regionresized", this.onRegionResized, this);
31236 layout.on("regioncollapsed", this.onRegionCollapsed, this);
31237 layout.on("regionexpanded", this.onRegionExpanded, this);
31240 storeState : function(){
31241 this.provider.set(this.layout.id+"-layout-state", this.state);
31244 onRegionResized : function(region, newSize){
31245 this.state[region.getPosition()].size = newSize;
31249 onRegionCollapsed : function(region){
31250 this.state[region.getPosition()].collapsed = true;
31254 onRegionExpanded : function(region){
31255 this.state[region.getPosition()].collapsed = false;
31260 * Ext JS Library 1.1.1
31261 * Copyright(c) 2006-2007, Ext JS, LLC.
31263 * Originally Released Under LGPL - original licence link has changed is not relivant.
31266 * <script type="text/javascript">
31269 * @class Roo.ContentPanel
31270 * @extends Roo.util.Observable
31271 * @children Roo.form.Form Roo.JsonView Roo.View
31272 * A basic ContentPanel element.
31273 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
31274 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
31275 * @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
31276 * @cfg {Boolean} closable True if the panel can be closed/removed
31277 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31278 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31279 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
31280 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31281 * @cfg {String} title The title for this panel
31282 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31283 * @cfg {String} url Calls {@link #setUrl} with this value
31284 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31285 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31286 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31287 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
31288 * @cfg {String} style Extra style to add to the content panel
31289 * @cfg {Roo.menu.Menu} menu popup menu
31292 * Create a new ContentPanel.
31293 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31294 * @param {String/Object} config A string to set only the title or a config object
31295 * @param {String} content (optional) Set the HTML content for this panel
31296 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31298 Roo.ContentPanel = function(el, config, content){
31302 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31306 if (config && config.parentLayout) {
31307 el = config.parentLayout.el.createChild();
31310 if(el.autoCreate){ // xtype is available if this is called from factory
31314 this.el = Roo.get(el);
31315 if(!this.el && config && config.autoCreate){
31316 if(typeof config.autoCreate == "object"){
31317 if(!config.autoCreate.id){
31318 config.autoCreate.id = config.id||el;
31320 this.el = Roo.DomHelper.append(document.body,
31321 config.autoCreate, true);
31323 this.el = Roo.DomHelper.append(document.body,
31324 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31329 this.closable = false;
31330 this.loaded = false;
31331 this.active = false;
31332 if(typeof config == "string"){
31333 this.title = config;
31335 Roo.apply(this, config);
31338 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31339 this.wrapEl = this.el.wrap();
31340 this.toolbar.container = this.el.insertSibling(false, 'before');
31341 this.toolbar = new Roo.Toolbar(this.toolbar);
31344 // xtype created footer. - not sure if will work as we normally have to render first..
31345 if (this.footer && !this.footer.el && this.footer.xtype) {
31346 if (!this.wrapEl) {
31347 this.wrapEl = this.el.wrap();
31350 this.footer.container = this.wrapEl.createChild();
31352 this.footer = Roo.factory(this.footer, Roo);
31357 this.resizeEl = Roo.get(this.resizeEl, true);
31359 this.resizeEl = this.el;
31361 // handle view.xtype
31369 * Fires when this panel is activated.
31370 * @param {Roo.ContentPanel} this
31374 * @event deactivate
31375 * Fires when this panel is activated.
31376 * @param {Roo.ContentPanel} this
31378 "deactivate" : true,
31382 * Fires when this panel is resized if fitToFrame is true.
31383 * @param {Roo.ContentPanel} this
31384 * @param {Number} width The width after any component adjustments
31385 * @param {Number} height The height after any component adjustments
31391 * Fires when this tab is created
31392 * @param {Roo.ContentPanel} this
31402 if(this.autoScroll){
31403 this.resizeEl.setStyle("overflow", "auto");
31405 // fix randome scrolling
31406 this.el.on('scroll', function() {
31407 Roo.log('fix random scolling');
31408 this.scrollTo('top',0);
31411 content = content || this.content;
31413 this.setContent(content);
31415 if(config && config.url){
31416 this.setUrl(this.url, this.params, this.loadOnce);
31421 Roo.ContentPanel.superclass.constructor.call(this);
31423 if (this.view && typeof(this.view.xtype) != 'undefined') {
31424 this.view.el = this.el.appendChild(document.createElement("div"));
31425 this.view = Roo.factory(this.view);
31426 this.view.render && this.view.render(false, '');
31430 this.fireEvent('render', this);
31433 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31435 setRegion : function(region){
31436 this.region = region;
31438 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31440 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31445 * Returns the toolbar for this Panel if one was configured.
31446 * @return {Roo.Toolbar}
31448 getToolbar : function(){
31449 return this.toolbar;
31452 setActiveState : function(active){
31453 this.active = active;
31455 this.fireEvent("deactivate", this);
31457 this.fireEvent("activate", this);
31461 * Updates this panel's element
31462 * @param {String} content The new content
31463 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31465 setContent : function(content, loadScripts){
31466 this.el.update(content, loadScripts);
31469 ignoreResize : function(w, h){
31470 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31473 this.lastSize = {width: w, height: h};
31478 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31479 * @return {Roo.UpdateManager} The UpdateManager
31481 getUpdateManager : function(){
31482 return this.el.getUpdateManager();
31485 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31486 * @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:
31489 url: "your-url.php",
31490 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31491 callback: yourFunction,
31492 scope: yourObject, //(optional scope)
31495 text: "Loading...",
31500 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31501 * 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.
31502 * @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}
31503 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31504 * @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.
31505 * @return {Roo.ContentPanel} this
31508 var um = this.el.getUpdateManager();
31509 um.update.apply(um, arguments);
31515 * 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.
31516 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31517 * @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)
31518 * @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)
31519 * @return {Roo.UpdateManager} The UpdateManager
31521 setUrl : function(url, params, loadOnce){
31522 if(this.refreshDelegate){
31523 this.removeListener("activate", this.refreshDelegate);
31525 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31526 this.on("activate", this.refreshDelegate);
31527 return this.el.getUpdateManager();
31530 _handleRefresh : function(url, params, loadOnce){
31531 if(!loadOnce || !this.loaded){
31532 var updater = this.el.getUpdateManager();
31533 updater.update(url, params, this._setLoaded.createDelegate(this));
31537 _setLoaded : function(){
31538 this.loaded = true;
31542 * Returns this panel's id
31545 getId : function(){
31550 * Returns this panel's element - used by regiosn to add.
31551 * @return {Roo.Element}
31553 getEl : function(){
31554 return this.wrapEl || this.el;
31557 adjustForComponents : function(width, height)
31559 //Roo.log('adjustForComponents ');
31560 if(this.resizeEl != this.el){
31561 width -= this.el.getFrameWidth('lr');
31562 height -= this.el.getFrameWidth('tb');
31565 var te = this.toolbar.getEl();
31566 height -= te.getHeight();
31567 te.setWidth(width);
31570 var te = this.footer.getEl();
31571 //Roo.log("footer:" + te.getHeight());
31573 height -= te.getHeight();
31574 te.setWidth(width);
31578 if(this.adjustments){
31579 width += this.adjustments[0];
31580 height += this.adjustments[1];
31582 return {"width": width, "height": height};
31585 setSize : function(width, height){
31586 if(this.fitToFrame && !this.ignoreResize(width, height)){
31587 if(this.fitContainer && this.resizeEl != this.el){
31588 this.el.setSize(width, height);
31590 var size = this.adjustForComponents(width, height);
31591 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31592 this.fireEvent('resize', this, size.width, size.height);
31597 * Returns this panel's title
31600 getTitle : function(){
31605 * Set this panel's title
31606 * @param {String} title
31608 setTitle : function(title){
31609 this.title = title;
31611 this.region.updatePanelTitle(this, title);
31616 * Returns true is this panel was configured to be closable
31617 * @return {Boolean}
31619 isClosable : function(){
31620 return this.closable;
31623 beforeSlide : function(){
31625 this.resizeEl.clip();
31628 afterSlide : function(){
31630 this.resizeEl.unclip();
31634 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31635 * Will fail silently if the {@link #setUrl} method has not been called.
31636 * This does not activate the panel, just updates its content.
31638 refresh : function(){
31639 if(this.refreshDelegate){
31640 this.loaded = false;
31641 this.refreshDelegate();
31646 * Destroys this panel
31648 destroy : function(){
31649 this.el.removeAllListeners();
31650 var tempEl = document.createElement("span");
31651 tempEl.appendChild(this.el.dom);
31652 tempEl.innerHTML = "";
31658 * form - if the content panel contains a form - this is a reference to it.
31659 * @type {Roo.form.Form}
31663 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31664 * This contains a reference to it.
31670 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31680 * @param {Object} cfg Xtype definition of item to add.
31683 addxtype : function(cfg) {
31685 if (cfg.xtype.match(/^Form$/)) {
31688 //if (this.footer) {
31689 // el = this.footer.container.insertSibling(false, 'before');
31691 el = this.el.createChild();
31694 this.form = new Roo.form.Form(cfg);
31697 if ( this.form.allItems.length) {
31698 this.form.render(el.dom);
31702 // should only have one of theses..
31703 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31704 // views.. should not be just added - used named prop 'view''
31706 cfg.el = this.el.appendChild(document.createElement("div"));
31709 var ret = new Roo.factory(cfg);
31711 ret.render && ret.render(false, ''); // render blank..
31720 * @class Roo.GridPanel
31721 * @extends Roo.ContentPanel
31723 * Create a new GridPanel.
31724 * @param {Roo.grid.Grid} grid The grid for this panel
31725 * @param {String/Object} config A string to set only the panel's title, or a config object
31727 Roo.GridPanel = function(grid, config){
31730 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31731 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31733 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31735 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31738 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31740 // xtype created footer. - not sure if will work as we normally have to render first..
31741 if (this.footer && !this.footer.el && this.footer.xtype) {
31743 this.footer.container = this.grid.getView().getFooterPanel(true);
31744 this.footer.dataSource = this.grid.dataSource;
31745 this.footer = Roo.factory(this.footer, Roo);
31749 grid.monitorWindowResize = false; // turn off autosizing
31750 grid.autoHeight = false;
31751 grid.autoWidth = false;
31753 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31756 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31757 getId : function(){
31758 return this.grid.id;
31762 * Returns the grid for this panel
31763 * @return {Roo.grid.Grid}
31765 getGrid : function(){
31769 setSize : function(width, height){
31770 if(!this.ignoreResize(width, height)){
31771 var grid = this.grid;
31772 var size = this.adjustForComponents(width, height);
31773 grid.getGridEl().setSize(size.width, size.height);
31778 beforeSlide : function(){
31779 this.grid.getView().scroller.clip();
31782 afterSlide : function(){
31783 this.grid.getView().scroller.unclip();
31786 destroy : function(){
31787 this.grid.destroy();
31789 Roo.GridPanel.superclass.destroy.call(this);
31795 * @class Roo.NestedLayoutPanel
31796 * @extends Roo.ContentPanel
31798 * Create a new NestedLayoutPanel.
31801 * @param {Roo.BorderLayout} layout [required] The layout for this panel
31802 * @param {String/Object} config A string to set only the title or a config object
31804 Roo.NestedLayoutPanel = function(layout, config)
31806 // construct with only one argument..
31807 /* FIXME - implement nicer consturctors
31808 if (layout.layout) {
31810 layout = config.layout;
31811 delete config.layout;
31813 if (layout.xtype && !layout.getEl) {
31814 // then layout needs constructing..
31815 layout = Roo.factory(layout, Roo);
31820 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31822 layout.monitorWindowResize = false; // turn off autosizing
31823 this.layout = layout;
31824 this.layout.getEl().addClass("x-layout-nested-layout");
31831 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31833 setSize : function(width, height){
31834 if(!this.ignoreResize(width, height)){
31835 var size = this.adjustForComponents(width, height);
31836 var el = this.layout.getEl();
31837 el.setSize(size.width, size.height);
31838 var touch = el.dom.offsetWidth;
31839 this.layout.layout();
31840 // ie requires a double layout on the first pass
31841 if(Roo.isIE && !this.initialized){
31842 this.initialized = true;
31843 this.layout.layout();
31848 // activate all subpanels if not currently active..
31850 setActiveState : function(active){
31851 this.active = active;
31853 this.fireEvent("deactivate", this);
31857 this.fireEvent("activate", this);
31858 // not sure if this should happen before or after..
31859 if (!this.layout) {
31860 return; // should not happen..
31863 for (var r in this.layout.regions) {
31864 reg = this.layout.getRegion(r);
31865 if (reg.getActivePanel()) {
31866 //reg.showPanel(reg.getActivePanel()); // force it to activate..
31867 reg.setActivePanel(reg.getActivePanel());
31870 if (!reg.panels.length) {
31873 reg.showPanel(reg.getPanel(0));
31882 * Returns the nested BorderLayout for this panel
31883 * @return {Roo.BorderLayout}
31885 getLayout : function(){
31886 return this.layout;
31890 * Adds a xtype elements to the layout of the nested panel
31894 xtype : 'ContentPanel',
31901 xtype : 'NestedLayoutPanel',
31907 items : [ ... list of content panels or nested layout panels.. ]
31911 * @param {Object} cfg Xtype definition of item to add.
31913 addxtype : function(cfg) {
31914 return this.layout.addxtype(cfg);
31919 Roo.ScrollPanel = function(el, config, content){
31920 config = config || {};
31921 config.fitToFrame = true;
31922 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31924 this.el.dom.style.overflow = "hidden";
31925 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31926 this.el.removeClass("x-layout-inactive-content");
31927 this.el.on("mousewheel", this.onWheel, this);
31929 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
31930 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
31931 up.unselectable(); down.unselectable();
31932 up.on("click", this.scrollUp, this);
31933 down.on("click", this.scrollDown, this);
31934 up.addClassOnOver("x-scroller-btn-over");
31935 down.addClassOnOver("x-scroller-btn-over");
31936 up.addClassOnClick("x-scroller-btn-click");
31937 down.addClassOnClick("x-scroller-btn-click");
31938 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31940 this.resizeEl = this.el;
31941 this.el = wrap; this.up = up; this.down = down;
31944 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31946 wheelIncrement : 5,
31947 scrollUp : function(){
31948 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31951 scrollDown : function(){
31952 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31955 afterScroll : function(){
31956 var el = this.resizeEl;
31957 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31958 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31959 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31962 setSize : function(){
31963 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31964 this.afterScroll();
31967 onWheel : function(e){
31968 var d = e.getWheelDelta();
31969 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31970 this.afterScroll();
31974 setContent : function(content, loadScripts){
31975 this.resizeEl.update(content, loadScripts);
31983 * @class Roo.TreePanel
31984 * @extends Roo.ContentPanel
31985 * Treepanel component
31988 * Create a new TreePanel. - defaults to fit/scoll contents.
31989 * @param {String/Object} config A string to set only the panel's title, or a config object
31991 Roo.TreePanel = function(config){
31992 var el = config.el;
31993 var tree = config.tree;
31994 delete config.tree;
31995 delete config.el; // hopefull!
31997 // wrapper for IE7 strict & safari scroll issue
31999 var treeEl = el.createChild();
32000 config.resizeEl = treeEl;
32004 Roo.TreePanel.superclass.constructor.call(this, el, config);
32007 this.tree = new Roo.tree.TreePanel(treeEl , tree);
32008 //console.log(tree);
32009 this.on('activate', function()
32011 if (this.tree.rendered) {
32014 //console.log('render tree');
32015 this.tree.render();
32017 // this should not be needed.. - it's actually the 'el' that resizes?
32018 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32020 //this.on('resize', function (cp, w, h) {
32021 // this.tree.innerCt.setWidth(w);
32022 // this.tree.innerCt.setHeight(h);
32023 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
32030 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
32034 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32052 * Ext JS Library 1.1.1
32053 * Copyright(c) 2006-2007, Ext JS, LLC.
32055 * Originally Released Under LGPL - original licence link has changed is not relivant.
32058 * <script type="text/javascript">
32063 * @class Roo.ReaderLayout
32064 * @extends Roo.BorderLayout
32065 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
32066 * center region containing two nested regions (a top one for a list view and one for item preview below),
32067 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32068 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32069 * expedites the setup of the overall layout and regions for this common application style.
32072 var reader = new Roo.ReaderLayout();
32073 var CP = Roo.ContentPanel; // shortcut for adding
32075 reader.beginUpdate();
32076 reader.add("north", new CP("north", "North"));
32077 reader.add("west", new CP("west", {title: "West"}));
32078 reader.add("east", new CP("east", {title: "East"}));
32080 reader.regions.listView.add(new CP("listView", "List"));
32081 reader.regions.preview.add(new CP("preview", "Preview"));
32082 reader.endUpdate();
32085 * Create a new ReaderLayout
32086 * @param {Object} config Configuration options
32087 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32088 * document.body if omitted)
32090 Roo.ReaderLayout = function(config, renderTo){
32091 var c = config || {size:{}};
32092 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32093 north: c.north !== false ? Roo.apply({
32097 }, c.north) : false,
32098 west: c.west !== false ? Roo.apply({
32106 margins:{left:5,right:0,bottom:5,top:5},
32107 cmargins:{left:5,right:5,bottom:5,top:5}
32108 }, c.west) : false,
32109 east: c.east !== false ? Roo.apply({
32117 margins:{left:0,right:5,bottom:5,top:5},
32118 cmargins:{left:5,right:5,bottom:5,top:5}
32119 }, c.east) : false,
32120 center: Roo.apply({
32121 tabPosition: 'top',
32125 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32129 this.el.addClass('x-reader');
32131 this.beginUpdate();
32133 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32134 south: c.preview !== false ? Roo.apply({
32141 cmargins:{top:5,left:0, right:0, bottom:0}
32142 }, c.preview) : false,
32143 center: Roo.apply({
32149 this.add('center', new Roo.NestedLayoutPanel(inner,
32150 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32154 this.regions.preview = inner.getRegion('south');
32155 this.regions.listView = inner.getRegion('center');
32158 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32160 * Ext JS Library 1.1.1
32161 * Copyright(c) 2006-2007, Ext JS, LLC.
32163 * Originally Released Under LGPL - original licence link has changed is not relivant.
32166 * <script type="text/javascript">
32170 * @class Roo.grid.Grid
32171 * @extends Roo.util.Observable
32172 * This class represents the primary interface of a component based grid control.
32173 * <br><br>Usage:<pre><code>
32174 var grid = new Roo.grid.Grid("my-container-id", {
32177 selModel: mySelectionModel,
32178 autoSizeColumns: true,
32179 monitorWindowResize: false,
32180 trackMouseOver: true
32185 * <b>Common Problems:</b><br/>
32186 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32187 * element will correct this<br/>
32188 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32189 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32190 * are unpredictable.<br/>
32191 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32192 * grid to calculate dimensions/offsets.<br/>
32194 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32195 * The container MUST have some type of size defined for the grid to fill. The container will be
32196 * automatically set to position relative if it isn't already.
32197 * @param {Object} config A config object that sets properties on this grid.
32199 Roo.grid.Grid = function(container, config){
32200 // initialize the container
32201 this.container = Roo.get(container);
32202 this.container.update("");
32203 this.container.setStyle("overflow", "hidden");
32204 this.container.addClass('x-grid-container');
32206 this.id = this.container.id;
32208 Roo.apply(this, config);
32209 // check and correct shorthanded configs
32211 this.dataSource = this.ds;
32215 this.colModel = this.cm;
32219 this.selModel = this.sm;
32223 if (this.selModel) {
32224 this.selModel = Roo.factory(this.selModel, Roo.grid);
32225 this.sm = this.selModel;
32226 this.sm.xmodule = this.xmodule || false;
32228 if (typeof(this.colModel.config) == 'undefined') {
32229 this.colModel = new Roo.grid.ColumnModel(this.colModel);
32230 this.cm = this.colModel;
32231 this.cm.xmodule = this.xmodule || false;
32233 if (this.dataSource) {
32234 this.dataSource= Roo.factory(this.dataSource, Roo.data);
32235 this.ds = this.dataSource;
32236 this.ds.xmodule = this.xmodule || false;
32243 this.container.setWidth(this.width);
32247 this.container.setHeight(this.height);
32254 * The raw click event for the entire grid.
32255 * @param {Roo.EventObject} e
32260 * The raw dblclick event for the entire grid.
32261 * @param {Roo.EventObject} e
32265 * @event contextmenu
32266 * The raw contextmenu event for the entire grid.
32267 * @param {Roo.EventObject} e
32269 "contextmenu" : true,
32272 * The raw mousedown event for the entire grid.
32273 * @param {Roo.EventObject} e
32275 "mousedown" : true,
32278 * The raw mouseup event for the entire grid.
32279 * @param {Roo.EventObject} e
32284 * The raw mouseover event for the entire grid.
32285 * @param {Roo.EventObject} e
32287 "mouseover" : true,
32290 * The raw mouseout event for the entire grid.
32291 * @param {Roo.EventObject} e
32296 * The raw keypress event for the entire grid.
32297 * @param {Roo.EventObject} e
32302 * The raw keydown event for the entire grid.
32303 * @param {Roo.EventObject} e
32311 * Fires when a cell is clicked
32312 * @param {Grid} this
32313 * @param {Number} rowIndex
32314 * @param {Number} columnIndex
32315 * @param {Roo.EventObject} e
32317 "cellclick" : true,
32319 * @event celldblclick
32320 * Fires when a cell is double clicked
32321 * @param {Grid} this
32322 * @param {Number} rowIndex
32323 * @param {Number} columnIndex
32324 * @param {Roo.EventObject} e
32326 "celldblclick" : true,
32329 * Fires when a row is clicked
32330 * @param {Grid} this
32331 * @param {Number} rowIndex
32332 * @param {Roo.EventObject} e
32336 * @event rowdblclick
32337 * Fires when a row is double clicked
32338 * @param {Grid} this
32339 * @param {Number} rowIndex
32340 * @param {Roo.EventObject} e
32342 "rowdblclick" : true,
32344 * @event headerclick
32345 * Fires when a header is clicked
32346 * @param {Grid} this
32347 * @param {Number} columnIndex
32348 * @param {Roo.EventObject} e
32350 "headerclick" : true,
32352 * @event headerdblclick
32353 * Fires when a header cell is double clicked
32354 * @param {Grid} this
32355 * @param {Number} columnIndex
32356 * @param {Roo.EventObject} e
32358 "headerdblclick" : true,
32360 * @event rowcontextmenu
32361 * Fires when a row is right clicked
32362 * @param {Grid} this
32363 * @param {Number} rowIndex
32364 * @param {Roo.EventObject} e
32366 "rowcontextmenu" : true,
32368 * @event cellcontextmenu
32369 * Fires when a cell is right clicked
32370 * @param {Grid} this
32371 * @param {Number} rowIndex
32372 * @param {Number} cellIndex
32373 * @param {Roo.EventObject} e
32375 "cellcontextmenu" : true,
32377 * @event headercontextmenu
32378 * Fires when a header is right clicked
32379 * @param {Grid} this
32380 * @param {Number} columnIndex
32381 * @param {Roo.EventObject} e
32383 "headercontextmenu" : true,
32385 * @event bodyscroll
32386 * Fires when the body element is scrolled
32387 * @param {Number} scrollLeft
32388 * @param {Number} scrollTop
32390 "bodyscroll" : true,
32392 * @event columnresize
32393 * Fires when the user resizes a column
32394 * @param {Number} columnIndex
32395 * @param {Number} newSize
32397 "columnresize" : true,
32399 * @event columnmove
32400 * Fires when the user moves a column
32401 * @param {Number} oldIndex
32402 * @param {Number} newIndex
32404 "columnmove" : true,
32407 * Fires when row(s) start being dragged
32408 * @param {Grid} this
32409 * @param {Roo.GridDD} dd The drag drop object
32410 * @param {event} e The raw browser event
32412 "startdrag" : true,
32415 * Fires when a drag operation is complete
32416 * @param {Grid} this
32417 * @param {Roo.GridDD} dd The drag drop object
32418 * @param {event} e The raw browser event
32423 * Fires when dragged row(s) are dropped on a valid DD target
32424 * @param {Grid} this
32425 * @param {Roo.GridDD} dd The drag drop object
32426 * @param {String} targetId The target drag drop object
32427 * @param {event} e The raw browser event
32432 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32433 * @param {Grid} this
32434 * @param {Roo.GridDD} dd The drag drop object
32435 * @param {String} targetId The target drag drop object
32436 * @param {event} e The raw browser event
32441 * Fires when the dragged row(s) first cross another DD target while being dragged
32442 * @param {Grid} this
32443 * @param {Roo.GridDD} dd The drag drop object
32444 * @param {String} targetId The target drag drop object
32445 * @param {event} e The raw browser event
32447 "dragenter" : true,
32450 * Fires when the dragged row(s) leave another DD target while being dragged
32451 * @param {Grid} this
32452 * @param {Roo.GridDD} dd The drag drop object
32453 * @param {String} targetId The target drag drop object
32454 * @param {event} e The raw browser event
32459 * Fires when a row is rendered, so you can change add a style to it.
32460 * @param {GridView} gridview The grid view
32461 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32467 * Fires when the grid is rendered
32468 * @param {Grid} grid
32473 Roo.grid.Grid.superclass.constructor.call(this);
32475 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32478 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32481 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
32484 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32487 * @cfg {Roo.grid.Store} ds The data store for the grid
32490 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32493 * @cfg {String} ddGroup - drag drop group.
32496 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32500 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32502 minColumnWidth : 25,
32505 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32506 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32507 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32509 autoSizeColumns : false,
32512 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32514 autoSizeHeaders : true,
32517 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32519 monitorWindowResize : true,
32522 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32523 * rows measured to get a columns size. Default is 0 (all rows).
32525 maxRowsToMeasure : 0,
32528 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32530 trackMouseOver : true,
32533 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32536 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
32540 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32542 enableDragDrop : false,
32545 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32547 enableColumnMove : true,
32550 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32552 enableColumnHide : true,
32555 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32557 enableRowHeightSync : false,
32560 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32565 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32567 autoHeight : false,
32570 * @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.
32572 autoExpandColumn : false,
32575 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32578 autoExpandMin : 50,
32581 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32583 autoExpandMax : 1000,
32586 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32591 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32595 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32605 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32606 * of a fixed width. Default is false.
32609 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32614 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32615 * %0 is replaced with the number of selected rows.
32617 ddText : "{0} selected row{1}",
32621 * Called once after all setup has been completed and the grid is ready to be rendered.
32622 * @return {Roo.grid.Grid} this
32624 render : function()
32626 var c = this.container;
32627 // try to detect autoHeight/width mode
32628 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32629 this.autoHeight = true;
32631 var view = this.getView();
32634 c.on("click", this.onClick, this);
32635 c.on("dblclick", this.onDblClick, this);
32636 c.on("contextmenu", this.onContextMenu, this);
32637 c.on("keydown", this.onKeyDown, this);
32639 c.on("touchstart", this.onTouchStart, this);
32642 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32644 this.getSelectionModel().init(this);
32649 this.loadMask = new Roo.LoadMask(this.container,
32650 Roo.apply({store:this.dataSource}, this.loadMask));
32654 if (this.toolbar && this.toolbar.xtype) {
32655 this.toolbar.container = this.getView().getHeaderPanel(true);
32656 this.toolbar = new Roo.Toolbar(this.toolbar);
32658 if (this.footer && this.footer.xtype) {
32659 this.footer.dataSource = this.getDataSource();
32660 this.footer.container = this.getView().getFooterPanel(true);
32661 this.footer = Roo.factory(this.footer, Roo);
32663 if (this.dropTarget && this.dropTarget.xtype) {
32664 delete this.dropTarget.xtype;
32665 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32669 this.rendered = true;
32670 this.fireEvent('render', this);
32675 * Reconfigures the grid to use a different Store and Column Model.
32676 * The View will be bound to the new objects and refreshed.
32677 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32678 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32680 reconfigure : function(dataSource, colModel){
32682 this.loadMask.destroy();
32683 this.loadMask = new Roo.LoadMask(this.container,
32684 Roo.apply({store:dataSource}, this.loadMask));
32686 this.view.bind(dataSource, colModel);
32687 this.dataSource = dataSource;
32688 this.colModel = colModel;
32689 this.view.refresh(true);
32693 * Add's a column, default at the end..
32695 * @param {int} position to add (default end)
32696 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
32698 addColumns : function(pos, ar)
32701 for (var i =0;i< ar.length;i++) {
32703 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32704 this.cm.lookup[cfg.id] = cfg;
32708 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32709 pos = this.cm.config.length; //this.cm.config.push(cfg);
32711 pos = Math.max(0,pos);
32714 this.cm.config.splice.apply(this.cm.config, ar);
32718 this.view.generateRules(this.cm);
32719 this.view.refresh(true);
32727 onKeyDown : function(e){
32728 this.fireEvent("keydown", e);
32732 * Destroy this grid.
32733 * @param {Boolean} removeEl True to remove the element
32735 destroy : function(removeEl, keepListeners){
32737 this.loadMask.destroy();
32739 var c = this.container;
32740 c.removeAllListeners();
32741 this.view.destroy();
32742 this.colModel.purgeListeners();
32743 if(!keepListeners){
32744 this.purgeListeners();
32747 if(removeEl === true){
32753 processEvent : function(name, e){
32754 // does this fire select???
32755 //Roo.log('grid:processEvent ' + name);
32757 if (name != 'touchstart' ) {
32758 this.fireEvent(name, e);
32761 var t = e.getTarget();
32763 var header = v.findHeaderIndex(t);
32764 if(header !== false){
32765 var ename = name == 'touchstart' ? 'click' : name;
32767 this.fireEvent("header" + ename, this, header, e);
32769 var row = v.findRowIndex(t);
32770 var cell = v.findCellIndex(t);
32771 if (name == 'touchstart') {
32772 // first touch is always a click.
32773 // hopefull this happens after selection is updated.?
32776 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32777 var cs = this.selModel.getSelectedCell();
32778 if (row == cs[0] && cell == cs[1]){
32782 if (typeof(this.selModel.getSelections) != 'undefined') {
32783 var cs = this.selModel.getSelections();
32784 var ds = this.dataSource;
32785 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32796 this.fireEvent("row" + name, this, row, e);
32797 if(cell !== false){
32798 this.fireEvent("cell" + name, this, row, cell, e);
32805 onClick : function(e){
32806 this.processEvent("click", e);
32809 onTouchStart : function(e){
32810 this.processEvent("touchstart", e);
32814 onContextMenu : function(e, t){
32815 this.processEvent("contextmenu", e);
32819 onDblClick : function(e){
32820 this.processEvent("dblclick", e);
32824 walkCells : function(row, col, step, fn, scope){
32825 var cm = this.colModel, clen = cm.getColumnCount();
32826 var ds = this.dataSource, rlen = ds.getCount(), first = true;
32838 if(fn.call(scope || this, row, col, cm) === true){
32856 if(fn.call(scope || this, row, col, cm) === true){
32868 getSelections : function(){
32869 return this.selModel.getSelections();
32873 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32874 * but if manual update is required this method will initiate it.
32876 autoSize : function(){
32878 this.view.layout();
32879 if(this.view.adjustForScroll){
32880 this.view.adjustForScroll();
32886 * Returns the grid's underlying element.
32887 * @return {Element} The element
32889 getGridEl : function(){
32890 return this.container;
32893 // private for compatibility, overridden by editor grid
32894 stopEditing : function(){},
32897 * Returns the grid's SelectionModel.
32898 * @return {SelectionModel}
32900 getSelectionModel : function(){
32901 if(!this.selModel){
32902 this.selModel = new Roo.grid.RowSelectionModel();
32904 return this.selModel;
32908 * Returns the grid's DataSource.
32909 * @return {DataSource}
32911 getDataSource : function(){
32912 return this.dataSource;
32916 * Returns the grid's ColumnModel.
32917 * @return {ColumnModel}
32919 getColumnModel : function(){
32920 return this.colModel;
32924 * Returns the grid's GridView object.
32925 * @return {GridView}
32927 getView : function(){
32929 this.view = new Roo.grid.GridView(this.viewConfig);
32930 this.relayEvents(this.view, [
32931 "beforerowremoved", "beforerowsinserted",
32932 "beforerefresh", "rowremoved",
32933 "rowsinserted", "rowupdated" ,"refresh"
32939 * Called to get grid's drag proxy text, by default returns this.ddText.
32940 * Override this to put something different in the dragged text.
32943 getDragDropText : function(){
32944 var count = this.selModel.getCount();
32945 return String.format(this.ddText, count, count == 1 ? '' : 's');
32950 * Ext JS Library 1.1.1
32951 * Copyright(c) 2006-2007, Ext JS, LLC.
32953 * Originally Released Under LGPL - original licence link has changed is not relivant.
32956 * <script type="text/javascript">
32959 * @class Roo.grid.AbstractGridView
32960 * @extends Roo.util.Observable
32962 * Abstract base class for grid Views
32965 Roo.grid.AbstractGridView = function(){
32969 "beforerowremoved" : true,
32970 "beforerowsinserted" : true,
32971 "beforerefresh" : true,
32972 "rowremoved" : true,
32973 "rowsinserted" : true,
32974 "rowupdated" : true,
32977 Roo.grid.AbstractGridView.superclass.constructor.call(this);
32980 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32981 rowClass : "x-grid-row",
32982 cellClass : "x-grid-cell",
32983 tdClass : "x-grid-td",
32984 hdClass : "x-grid-hd",
32985 splitClass : "x-grid-hd-split",
32987 init: function(grid){
32989 var cid = this.grid.getGridEl().id;
32990 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32991 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32992 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32993 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32996 getColumnRenderers : function(){
32997 var renderers = [];
32998 var cm = this.grid.colModel;
32999 var colCount = cm.getColumnCount();
33000 for(var i = 0; i < colCount; i++){
33001 renderers[i] = cm.getRenderer(i);
33006 getColumnIds : function(){
33008 var cm = this.grid.colModel;
33009 var colCount = cm.getColumnCount();
33010 for(var i = 0; i < colCount; i++){
33011 ids[i] = cm.getColumnId(i);
33016 getDataIndexes : function(){
33017 if(!this.indexMap){
33018 this.indexMap = this.buildIndexMap();
33020 return this.indexMap.colToData;
33023 getColumnIndexByDataIndex : function(dataIndex){
33024 if(!this.indexMap){
33025 this.indexMap = this.buildIndexMap();
33027 return this.indexMap.dataToCol[dataIndex];
33031 * Set a css style for a column dynamically.
33032 * @param {Number} colIndex The index of the column
33033 * @param {String} name The css property name
33034 * @param {String} value The css value
33036 setCSSStyle : function(colIndex, name, value){
33037 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33038 Roo.util.CSS.updateRule(selector, name, value);
33041 generateRules : function(cm){
33042 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33043 Roo.util.CSS.removeStyleSheet(rulesId);
33044 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33045 var cid = cm.getColumnId(i);
33046 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33047 this.tdSelector, cid, " {\n}\n",
33048 this.hdSelector, cid, " {\n}\n",
33049 this.splitSelector, cid, " {\n}\n");
33051 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33055 * Ext JS Library 1.1.1
33056 * Copyright(c) 2006-2007, Ext JS, LLC.
33058 * Originally Released Under LGPL - original licence link has changed is not relivant.
33061 * <script type="text/javascript">
33065 // This is a support class used internally by the Grid components
33066 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33068 this.view = grid.getView();
33069 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33070 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33072 this.setHandleElId(Roo.id(hd));
33073 this.setOuterHandleElId(Roo.id(hd2));
33075 this.scroll = false;
33077 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33079 getDragData : function(e){
33080 var t = Roo.lib.Event.getTarget(e);
33081 var h = this.view.findHeaderCell(t);
33083 return {ddel: h.firstChild, header:h};
33088 onInitDrag : function(e){
33089 this.view.headersDisabled = true;
33090 var clone = this.dragData.ddel.cloneNode(true);
33091 clone.id = Roo.id();
33092 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33093 this.proxy.update(clone);
33097 afterValidDrop : function(){
33099 setTimeout(function(){
33100 v.headersDisabled = false;
33104 afterInvalidDrop : function(){
33106 setTimeout(function(){
33107 v.headersDisabled = false;
33113 * Ext JS Library 1.1.1
33114 * Copyright(c) 2006-2007, Ext JS, LLC.
33116 * Originally Released Under LGPL - original licence link has changed is not relivant.
33119 * <script type="text/javascript">
33122 // This is a support class used internally by the Grid components
33123 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33125 this.view = grid.getView();
33126 // split the proxies so they don't interfere with mouse events
33127 this.proxyTop = Roo.DomHelper.append(document.body, {
33128 cls:"col-move-top", html:" "
33130 this.proxyBottom = Roo.DomHelper.append(document.body, {
33131 cls:"col-move-bottom", html:" "
33133 this.proxyTop.hide = this.proxyBottom.hide = function(){
33134 this.setLeftTop(-100,-100);
33135 this.setStyle("visibility", "hidden");
33137 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33138 // temporarily disabled
33139 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33140 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33142 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33143 proxyOffsets : [-4, -9],
33144 fly: Roo.Element.fly,
33146 getTargetFromEvent : function(e){
33147 var t = Roo.lib.Event.getTarget(e);
33148 var cindex = this.view.findCellIndex(t);
33149 if(cindex !== false){
33150 return this.view.getHeaderCell(cindex);
33155 nextVisible : function(h){
33156 var v = this.view, cm = this.grid.colModel;
33159 if(!cm.isHidden(v.getCellIndex(h))){
33167 prevVisible : function(h){
33168 var v = this.view, cm = this.grid.colModel;
33171 if(!cm.isHidden(v.getCellIndex(h))){
33179 positionIndicator : function(h, n, e){
33180 var x = Roo.lib.Event.getPageX(e);
33181 var r = Roo.lib.Dom.getRegion(n.firstChild);
33182 var px, pt, py = r.top + this.proxyOffsets[1];
33183 if((r.right - x) <= (r.right-r.left)/2){
33184 px = r.right+this.view.borderWidth;
33190 var oldIndex = this.view.getCellIndex(h);
33191 var newIndex = this.view.getCellIndex(n);
33193 if(this.grid.colModel.isFixed(newIndex)){
33197 var locked = this.grid.colModel.isLocked(newIndex);
33202 if(oldIndex < newIndex){
33205 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33208 px += this.proxyOffsets[0];
33209 this.proxyTop.setLeftTop(px, py);
33210 this.proxyTop.show();
33211 if(!this.bottomOffset){
33212 this.bottomOffset = this.view.mainHd.getHeight();
33214 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33215 this.proxyBottom.show();
33219 onNodeEnter : function(n, dd, e, data){
33220 if(data.header != n){
33221 this.positionIndicator(data.header, n, e);
33225 onNodeOver : function(n, dd, e, data){
33226 var result = false;
33227 if(data.header != n){
33228 result = this.positionIndicator(data.header, n, e);
33231 this.proxyTop.hide();
33232 this.proxyBottom.hide();
33234 return result ? this.dropAllowed : this.dropNotAllowed;
33237 onNodeOut : function(n, dd, e, data){
33238 this.proxyTop.hide();
33239 this.proxyBottom.hide();
33242 onNodeDrop : function(n, dd, e, data){
33243 var h = data.header;
33245 var cm = this.grid.colModel;
33246 var x = Roo.lib.Event.getPageX(e);
33247 var r = Roo.lib.Dom.getRegion(n.firstChild);
33248 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33249 var oldIndex = this.view.getCellIndex(h);
33250 var newIndex = this.view.getCellIndex(n);
33251 var locked = cm.isLocked(newIndex);
33255 if(oldIndex < newIndex){
33258 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33261 cm.setLocked(oldIndex, locked, true);
33262 cm.moveColumn(oldIndex, newIndex);
33263 this.grid.fireEvent("columnmove", oldIndex, newIndex);
33271 * Ext JS Library 1.1.1
33272 * Copyright(c) 2006-2007, Ext JS, LLC.
33274 * Originally Released Under LGPL - original licence link has changed is not relivant.
33277 * <script type="text/javascript">
33281 * @class Roo.grid.GridView
33282 * @extends Roo.util.Observable
33285 * @param {Object} config
33287 Roo.grid.GridView = function(config){
33288 Roo.grid.GridView.superclass.constructor.call(this);
33291 Roo.apply(this, config);
33294 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33296 unselectable : 'unselectable="on"',
33297 unselectableCls : 'x-unselectable',
33300 rowClass : "x-grid-row",
33302 cellClass : "x-grid-col",
33304 tdClass : "x-grid-td",
33306 hdClass : "x-grid-hd",
33308 splitClass : "x-grid-split",
33310 sortClasses : ["sort-asc", "sort-desc"],
33312 enableMoveAnim : false,
33316 dh : Roo.DomHelper,
33318 fly : Roo.Element.fly,
33320 css : Roo.util.CSS,
33326 scrollIncrement : 22,
33328 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33330 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33332 bind : function(ds, cm){
33334 this.ds.un("load", this.onLoad, this);
33335 this.ds.un("datachanged", this.onDataChange, this);
33336 this.ds.un("add", this.onAdd, this);
33337 this.ds.un("remove", this.onRemove, this);
33338 this.ds.un("update", this.onUpdate, this);
33339 this.ds.un("clear", this.onClear, this);
33342 ds.on("load", this.onLoad, this);
33343 ds.on("datachanged", this.onDataChange, this);
33344 ds.on("add", this.onAdd, this);
33345 ds.on("remove", this.onRemove, this);
33346 ds.on("update", this.onUpdate, this);
33347 ds.on("clear", this.onClear, this);
33352 this.cm.un("widthchange", this.onColWidthChange, this);
33353 this.cm.un("headerchange", this.onHeaderChange, this);
33354 this.cm.un("hiddenchange", this.onHiddenChange, this);
33355 this.cm.un("columnmoved", this.onColumnMove, this);
33356 this.cm.un("columnlockchange", this.onColumnLock, this);
33359 this.generateRules(cm);
33360 cm.on("widthchange", this.onColWidthChange, this);
33361 cm.on("headerchange", this.onHeaderChange, this);
33362 cm.on("hiddenchange", this.onHiddenChange, this);
33363 cm.on("columnmoved", this.onColumnMove, this);
33364 cm.on("columnlockchange", this.onColumnLock, this);
33369 init: function(grid){
33370 Roo.grid.GridView.superclass.init.call(this, grid);
33372 this.bind(grid.dataSource, grid.colModel);
33374 grid.on("headerclick", this.handleHeaderClick, this);
33376 if(grid.trackMouseOver){
33377 grid.on("mouseover", this.onRowOver, this);
33378 grid.on("mouseout", this.onRowOut, this);
33380 grid.cancelTextSelection = function(){};
33381 this.gridId = grid.id;
33383 var tpls = this.templates || {};
33386 tpls.master = new Roo.Template(
33387 '<div class="x-grid" hidefocus="true">',
33388 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33389 '<div class="x-grid-topbar"></div>',
33390 '<div class="x-grid-scroller"><div></div></div>',
33391 '<div class="x-grid-locked">',
33392 '<div class="x-grid-header">{lockedHeader}</div>',
33393 '<div class="x-grid-body">{lockedBody}</div>',
33395 '<div class="x-grid-viewport">',
33396 '<div class="x-grid-header">{header}</div>',
33397 '<div class="x-grid-body">{body}</div>',
33399 '<div class="x-grid-bottombar"></div>',
33401 '<div class="x-grid-resize-proxy"> </div>',
33404 tpls.master.disableformats = true;
33408 tpls.header = new Roo.Template(
33409 '<table border="0" cellspacing="0" cellpadding="0">',
33410 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33413 tpls.header.disableformats = true;
33415 tpls.header.compile();
33418 tpls.hcell = new Roo.Template(
33419 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33420 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33423 tpls.hcell.disableFormats = true;
33425 tpls.hcell.compile();
33428 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33429 this.unselectableCls + '" ' + this.unselectable +'> </div>');
33430 tpls.hsplit.disableFormats = true;
33432 tpls.hsplit.compile();
33435 tpls.body = new Roo.Template(
33436 '<table border="0" cellspacing="0" cellpadding="0">',
33437 "<tbody>{rows}</tbody>",
33440 tpls.body.disableFormats = true;
33442 tpls.body.compile();
33445 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33446 tpls.row.disableFormats = true;
33448 tpls.row.compile();
33451 tpls.cell = new Roo.Template(
33452 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33453 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33454 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33457 tpls.cell.disableFormats = true;
33459 tpls.cell.compile();
33461 this.templates = tpls;
33464 // remap these for backwards compat
33465 onColWidthChange : function(){
33466 this.updateColumns.apply(this, arguments);
33468 onHeaderChange : function(){
33469 this.updateHeaders.apply(this, arguments);
33471 onHiddenChange : function(){
33472 this.handleHiddenChange.apply(this, arguments);
33474 onColumnMove : function(){
33475 this.handleColumnMove.apply(this, arguments);
33477 onColumnLock : function(){
33478 this.handleLockChange.apply(this, arguments);
33481 onDataChange : function(){
33483 this.updateHeaderSortState();
33486 onClear : function(){
33490 onUpdate : function(ds, record){
33491 this.refreshRow(record);
33494 refreshRow : function(record){
33495 var ds = this.ds, index;
33496 if(typeof record == 'number'){
33498 record = ds.getAt(index);
33500 index = ds.indexOf(record);
33502 this.insertRows(ds, index, index, true);
33503 this.onRemove(ds, record, index+1, true);
33504 this.syncRowHeights(index, index);
33506 this.fireEvent("rowupdated", this, index, record);
33509 onAdd : function(ds, records, index){
33510 this.insertRows(ds, index, index + (records.length-1));
33513 onRemove : function(ds, record, index, isUpdate){
33514 if(isUpdate !== true){
33515 this.fireEvent("beforerowremoved", this, index, record);
33517 var bt = this.getBodyTable(), lt = this.getLockedTable();
33518 if(bt.rows[index]){
33519 bt.firstChild.removeChild(bt.rows[index]);
33521 if(lt.rows[index]){
33522 lt.firstChild.removeChild(lt.rows[index]);
33524 if(isUpdate !== true){
33525 this.stripeRows(index);
33526 this.syncRowHeights(index, index);
33528 this.fireEvent("rowremoved", this, index, record);
33532 onLoad : function(){
33533 this.scrollToTop();
33537 * Scrolls the grid to the top
33539 scrollToTop : function(){
33541 this.scroller.dom.scrollTop = 0;
33547 * Gets a panel in the header of the grid that can be used for toolbars etc.
33548 * After modifying the contents of this panel a call to grid.autoSize() may be
33549 * required to register any changes in size.
33550 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33551 * @return Roo.Element
33553 getHeaderPanel : function(doShow){
33555 this.headerPanel.show();
33557 return this.headerPanel;
33561 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33562 * After modifying the contents of this panel a call to grid.autoSize() may be
33563 * required to register any changes in size.
33564 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33565 * @return Roo.Element
33567 getFooterPanel : function(doShow){
33569 this.footerPanel.show();
33571 return this.footerPanel;
33574 initElements : function(){
33575 var E = Roo.Element;
33576 var el = this.grid.getGridEl().dom.firstChild;
33577 var cs = el.childNodes;
33579 this.el = new E(el);
33581 this.focusEl = new E(el.firstChild);
33582 this.focusEl.swallowEvent("click", true);
33584 this.headerPanel = new E(cs[1]);
33585 this.headerPanel.enableDisplayMode("block");
33587 this.scroller = new E(cs[2]);
33588 this.scrollSizer = new E(this.scroller.dom.firstChild);
33590 this.lockedWrap = new E(cs[3]);
33591 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33592 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33594 this.mainWrap = new E(cs[4]);
33595 this.mainHd = new E(this.mainWrap.dom.firstChild);
33596 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33598 this.footerPanel = new E(cs[5]);
33599 this.footerPanel.enableDisplayMode("block");
33601 this.resizeProxy = new E(cs[6]);
33603 this.headerSelector = String.format(
33604 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33605 this.lockedHd.id, this.mainHd.id
33608 this.splitterSelector = String.format(
33609 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33610 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33613 idToCssName : function(s)
33615 return s.replace(/[^a-z0-9]+/ig, '-');
33618 getHeaderCell : function(index){
33619 return Roo.DomQuery.select(this.headerSelector)[index];
33622 getHeaderCellMeasure : function(index){
33623 return this.getHeaderCell(index).firstChild;
33626 getHeaderCellText : function(index){
33627 return this.getHeaderCell(index).firstChild.firstChild;
33630 getLockedTable : function(){
33631 return this.lockedBody.dom.firstChild;
33634 getBodyTable : function(){
33635 return this.mainBody.dom.firstChild;
33638 getLockedRow : function(index){
33639 return this.getLockedTable().rows[index];
33642 getRow : function(index){
33643 return this.getBodyTable().rows[index];
33646 getRowComposite : function(index){
33648 this.rowEl = new Roo.CompositeElementLite();
33650 var els = [], lrow, mrow;
33651 if(lrow = this.getLockedRow(index)){
33654 if(mrow = this.getRow(index)){
33657 this.rowEl.elements = els;
33661 * Gets the 'td' of the cell
33663 * @param {Integer} rowIndex row to select
33664 * @param {Integer} colIndex column to select
33668 getCell : function(rowIndex, colIndex){
33669 var locked = this.cm.getLockedCount();
33671 if(colIndex < locked){
33672 source = this.lockedBody.dom.firstChild;
33674 source = this.mainBody.dom.firstChild;
33675 colIndex -= locked;
33677 return source.rows[rowIndex].childNodes[colIndex];
33680 getCellText : function(rowIndex, colIndex){
33681 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33684 getCellBox : function(cell){
33685 var b = this.fly(cell).getBox();
33686 if(Roo.isOpera){ // opera fails to report the Y
33687 b.y = cell.offsetTop + this.mainBody.getY();
33692 getCellIndex : function(cell){
33693 var id = String(cell.className).match(this.cellRE);
33695 return parseInt(id[1], 10);
33700 findHeaderIndex : function(n){
33701 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33702 return r ? this.getCellIndex(r) : false;
33705 findHeaderCell : function(n){
33706 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33707 return r ? r : false;
33710 findRowIndex : function(n){
33714 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33715 return r ? r.rowIndex : false;
33718 findCellIndex : function(node){
33719 var stop = this.el.dom;
33720 while(node && node != stop){
33721 if(this.findRE.test(node.className)){
33722 return this.getCellIndex(node);
33724 node = node.parentNode;
33729 getColumnId : function(index){
33730 return this.cm.getColumnId(index);
33733 getSplitters : function()
33735 if(this.splitterSelector){
33736 return Roo.DomQuery.select(this.splitterSelector);
33742 getSplitter : function(index){
33743 return this.getSplitters()[index];
33746 onRowOver : function(e, t){
33748 if((row = this.findRowIndex(t)) !== false){
33749 this.getRowComposite(row).addClass("x-grid-row-over");
33753 onRowOut : function(e, t){
33755 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33756 this.getRowComposite(row).removeClass("x-grid-row-over");
33760 renderHeaders : function(){
33762 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33763 var cb = [], lb = [], sb = [], lsb = [], p = {};
33764 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33765 p.cellId = "x-grid-hd-0-" + i;
33766 p.splitId = "x-grid-csplit-0-" + i;
33767 p.id = cm.getColumnId(i);
33768 p.value = cm.getColumnHeader(i) || "";
33769 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33770 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33771 if(!cm.isLocked(i)){
33772 cb[cb.length] = ct.apply(p);
33773 sb[sb.length] = st.apply(p);
33775 lb[lb.length] = ct.apply(p);
33776 lsb[lsb.length] = st.apply(p);
33779 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33780 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33783 updateHeaders : function(){
33784 var html = this.renderHeaders();
33785 this.lockedHd.update(html[0]);
33786 this.mainHd.update(html[1]);
33790 * Focuses the specified row.
33791 * @param {Number} row The row index
33793 focusRow : function(row)
33795 //Roo.log('GridView.focusRow');
33796 var x = this.scroller.dom.scrollLeft;
33797 this.focusCell(row, 0, false);
33798 this.scroller.dom.scrollLeft = x;
33802 * Focuses the specified cell.
33803 * @param {Number} row The row index
33804 * @param {Number} col The column index
33805 * @param {Boolean} hscroll false to disable horizontal scrolling
33807 focusCell : function(row, col, hscroll)
33809 //Roo.log('GridView.focusCell');
33810 var el = this.ensureVisible(row, col, hscroll);
33811 this.focusEl.alignTo(el, "tl-tl");
33813 this.focusEl.focus();
33815 this.focusEl.focus.defer(1, this.focusEl);
33820 * Scrolls the specified cell into view
33821 * @param {Number} row The row index
33822 * @param {Number} col The column index
33823 * @param {Boolean} hscroll false to disable horizontal scrolling
33825 ensureVisible : function(row, col, hscroll)
33827 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33828 //return null; //disable for testing.
33829 if(typeof row != "number"){
33830 row = row.rowIndex;
33832 if(row < 0 && row >= this.ds.getCount()){
33835 col = (col !== undefined ? col : 0);
33836 var cm = this.grid.colModel;
33837 while(cm.isHidden(col)){
33841 var el = this.getCell(row, col);
33845 var c = this.scroller.dom;
33847 var ctop = parseInt(el.offsetTop, 10);
33848 var cleft = parseInt(el.offsetLeft, 10);
33849 var cbot = ctop + el.offsetHeight;
33850 var cright = cleft + el.offsetWidth;
33852 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33853 var stop = parseInt(c.scrollTop, 10);
33854 var sleft = parseInt(c.scrollLeft, 10);
33855 var sbot = stop + ch;
33856 var sright = sleft + c.clientWidth;
33858 Roo.log('GridView.ensureVisible:' +
33860 ' c.clientHeight:' + c.clientHeight +
33861 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33869 c.scrollTop = ctop;
33870 //Roo.log("set scrolltop to ctop DISABLE?");
33871 }else if(cbot > sbot){
33872 //Roo.log("set scrolltop to cbot-ch");
33873 c.scrollTop = cbot-ch;
33876 if(hscroll !== false){
33878 c.scrollLeft = cleft;
33879 }else if(cright > sright){
33880 c.scrollLeft = cright-c.clientWidth;
33887 updateColumns : function(){
33888 this.grid.stopEditing();
33889 var cm = this.grid.colModel, colIds = this.getColumnIds();
33890 //var totalWidth = cm.getTotalWidth();
33892 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33893 //if(cm.isHidden(i)) continue;
33894 var w = cm.getColumnWidth(i);
33895 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33896 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33898 this.updateSplitters();
33901 generateRules : function(cm){
33902 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33903 Roo.util.CSS.removeStyleSheet(rulesId);
33904 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33905 var cid = cm.getColumnId(i);
33907 if(cm.config[i].align){
33908 align = 'text-align:'+cm.config[i].align+';';
33911 if(cm.isHidden(i)){
33912 hidden = 'display:none;';
33914 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33916 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33917 this.hdSelector, cid, " {\n", align, width, "}\n",
33918 this.tdSelector, cid, " {\n",hidden,"\n}\n",
33919 this.splitSelector, cid, " {\n", hidden , "\n}\n");
33921 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33924 updateSplitters : function(){
33925 var cm = this.cm, s = this.getSplitters();
33926 if(s){ // splitters not created yet
33927 var pos = 0, locked = true;
33928 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33929 if(cm.isHidden(i)) {
33932 var w = cm.getColumnWidth(i); // make sure it's a number
33933 if(!cm.isLocked(i) && locked){
33938 s[i].style.left = (pos-this.splitOffset) + "px";
33943 handleHiddenChange : function(colModel, colIndex, hidden){
33945 this.hideColumn(colIndex);
33947 this.unhideColumn(colIndex);
33951 hideColumn : function(colIndex){
33952 var cid = this.getColumnId(colIndex);
33953 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33954 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33956 this.updateHeaders();
33958 this.updateSplitters();
33962 unhideColumn : function(colIndex){
33963 var cid = this.getColumnId(colIndex);
33964 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33965 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33968 this.updateHeaders();
33970 this.updateSplitters();
33974 insertRows : function(dm, firstRow, lastRow, isUpdate){
33975 if(firstRow == 0 && lastRow == dm.getCount()-1){
33979 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33981 var s = this.getScrollState();
33982 var markup = this.renderRows(firstRow, lastRow);
33983 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33984 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33985 this.restoreScroll(s);
33987 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33988 this.syncRowHeights(firstRow, lastRow);
33989 this.stripeRows(firstRow);
33995 bufferRows : function(markup, target, index){
33996 var before = null, trows = target.rows, tbody = target.tBodies[0];
33997 if(index < trows.length){
33998 before = trows[index];
34000 var b = document.createElement("div");
34001 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34002 var rows = b.firstChild.rows;
34003 for(var i = 0, len = rows.length; i < len; i++){
34005 tbody.insertBefore(rows[0], before);
34007 tbody.appendChild(rows[0]);
34014 deleteRows : function(dm, firstRow, lastRow){
34015 if(dm.getRowCount()<1){
34016 this.fireEvent("beforerefresh", this);
34017 this.mainBody.update("");
34018 this.lockedBody.update("");
34019 this.fireEvent("refresh", this);
34021 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34022 var bt = this.getBodyTable();
34023 var tbody = bt.firstChild;
34024 var rows = bt.rows;
34025 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34026 tbody.removeChild(rows[firstRow]);
34028 this.stripeRows(firstRow);
34029 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34033 updateRows : function(dataSource, firstRow, lastRow){
34034 var s = this.getScrollState();
34036 this.restoreScroll(s);
34039 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34043 this.updateHeaderSortState();
34046 getScrollState : function(){
34048 var sb = this.scroller.dom;
34049 return {left: sb.scrollLeft, top: sb.scrollTop};
34052 stripeRows : function(startRow){
34053 if(!this.grid.stripeRows || this.ds.getCount() < 1){
34056 startRow = startRow || 0;
34057 var rows = this.getBodyTable().rows;
34058 var lrows = this.getLockedTable().rows;
34059 var cls = ' x-grid-row-alt ';
34060 for(var i = startRow, len = rows.length; i < len; i++){
34061 var row = rows[i], lrow = lrows[i];
34062 var isAlt = ((i+1) % 2 == 0);
34063 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34064 if(isAlt == hasAlt){
34068 row.className += " x-grid-row-alt";
34070 row.className = row.className.replace("x-grid-row-alt", "");
34073 lrow.className = row.className;
34078 restoreScroll : function(state){
34079 //Roo.log('GridView.restoreScroll');
34080 var sb = this.scroller.dom;
34081 sb.scrollLeft = state.left;
34082 sb.scrollTop = state.top;
34086 syncScroll : function(){
34087 //Roo.log('GridView.syncScroll');
34088 var sb = this.scroller.dom;
34089 var sh = this.mainHd.dom;
34090 var bs = this.mainBody.dom;
34091 var lv = this.lockedBody.dom;
34092 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34093 lv.scrollTop = bs.scrollTop = sb.scrollTop;
34096 handleScroll : function(e){
34098 var sb = this.scroller.dom;
34099 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34103 handleWheel : function(e){
34104 var d = e.getWheelDelta();
34105 this.scroller.dom.scrollTop -= d*22;
34106 // set this here to prevent jumpy scrolling on large tables
34107 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34111 renderRows : function(startRow, endRow){
34112 // pull in all the crap needed to render rows
34113 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34114 var colCount = cm.getColumnCount();
34116 if(ds.getCount() < 1){
34120 // build a map for all the columns
34122 for(var i = 0; i < colCount; i++){
34123 var name = cm.getDataIndex(i);
34125 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34126 renderer : cm.getRenderer(i),
34127 id : cm.getColumnId(i),
34128 locked : cm.isLocked(i),
34129 has_editor : cm.isCellEditable(i)
34133 startRow = startRow || 0;
34134 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34136 // records to render
34137 var rs = ds.getRange(startRow, endRow);
34139 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34142 // As much as I hate to duplicate code, this was branched because FireFox really hates
34143 // [].join("") on strings. The performance difference was substantial enough to
34144 // branch this function
34145 doRender : Roo.isGecko ?
34146 function(cs, rs, ds, startRow, colCount, stripe){
34147 var ts = this.templates, ct = ts.cell, rt = ts.row;
34149 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34151 var hasListener = this.grid.hasListener('rowclass');
34153 for(var j = 0, len = rs.length; j < len; j++){
34154 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34155 for(var i = 0; i < colCount; i++){
34157 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34159 p.css = p.attr = "";
34160 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34161 if(p.value == undefined || p.value === "") {
34162 p.value = " ";
34165 p.css += ' x-grid-editable-cell';
34167 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34168 p.css += ' x-grid-dirty-cell';
34170 var markup = ct.apply(p);
34178 if(stripe && ((rowIndex+1) % 2 == 0)){
34179 alt.push("x-grid-row-alt")
34182 alt.push( " x-grid-dirty-row");
34185 if(this.getRowClass){
34186 alt.push(this.getRowClass(r, rowIndex));
34192 rowIndex : rowIndex,
34195 this.grid.fireEvent('rowclass', this, rowcfg);
34196 alt.push(rowcfg.rowClass);
34198 rp.alt = alt.join(" ");
34199 lbuf+= rt.apply(rp);
34201 buf+= rt.apply(rp);
34203 return [lbuf, buf];
34205 function(cs, rs, ds, startRow, colCount, stripe){
34206 var ts = this.templates, ct = ts.cell, rt = ts.row;
34208 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34209 var hasListener = this.grid.hasListener('rowclass');
34212 for(var j = 0, len = rs.length; j < len; j++){
34213 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34214 for(var i = 0; i < colCount; i++){
34216 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34218 p.css = p.attr = "";
34219 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34220 if(p.value == undefined || p.value === "") {
34221 p.value = " ";
34225 p.css += ' x-grid-editable-cell';
34227 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34228 p.css += ' x-grid-dirty-cell'
34231 var markup = ct.apply(p);
34233 cb[cb.length] = markup;
34235 lcb[lcb.length] = markup;
34239 if(stripe && ((rowIndex+1) % 2 == 0)){
34240 alt.push( "x-grid-row-alt");
34243 alt.push(" x-grid-dirty-row");
34246 if(this.getRowClass){
34247 alt.push( this.getRowClass(r, rowIndex));
34253 rowIndex : rowIndex,
34256 this.grid.fireEvent('rowclass', this, rowcfg);
34257 alt.push(rowcfg.rowClass);
34260 rp.alt = alt.join(" ");
34261 rp.cells = lcb.join("");
34262 lbuf[lbuf.length] = rt.apply(rp);
34263 rp.cells = cb.join("");
34264 buf[buf.length] = rt.apply(rp);
34266 return [lbuf.join(""), buf.join("")];
34269 renderBody : function(){
34270 var markup = this.renderRows();
34271 var bt = this.templates.body;
34272 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34276 * Refreshes the grid
34277 * @param {Boolean} headersToo
34279 refresh : function(headersToo){
34280 this.fireEvent("beforerefresh", this);
34281 this.grid.stopEditing();
34282 var result = this.renderBody();
34283 this.lockedBody.update(result[0]);
34284 this.mainBody.update(result[1]);
34285 if(headersToo === true){
34286 this.updateHeaders();
34287 this.updateColumns();
34288 this.updateSplitters();
34289 this.updateHeaderSortState();
34291 this.syncRowHeights();
34293 this.fireEvent("refresh", this);
34296 handleColumnMove : function(cm, oldIndex, newIndex){
34297 this.indexMap = null;
34298 var s = this.getScrollState();
34299 this.refresh(true);
34300 this.restoreScroll(s);
34301 this.afterMove(newIndex);
34304 afterMove : function(colIndex){
34305 if(this.enableMoveAnim && Roo.enableFx){
34306 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34308 // if multisort - fix sortOrder, and reload..
34309 if (this.grid.dataSource.multiSort) {
34310 // the we can call sort again..
34311 var dm = this.grid.dataSource;
34312 var cm = this.grid.colModel;
34314 for(var i = 0; i < cm.config.length; i++ ) {
34316 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34317 continue; // dont' bother, it's not in sort list or being set.
34320 so.push(cm.config[i].dataIndex);
34323 dm.load(dm.lastOptions);
34330 updateCell : function(dm, rowIndex, dataIndex){
34331 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34332 if(typeof colIndex == "undefined"){ // not present in grid
34335 var cm = this.grid.colModel;
34336 var cell = this.getCell(rowIndex, colIndex);
34337 var cellText = this.getCellText(rowIndex, colIndex);
34340 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34341 id : cm.getColumnId(colIndex),
34342 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34344 var renderer = cm.getRenderer(colIndex);
34345 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34346 if(typeof val == "undefined" || val === "") {
34349 cellText.innerHTML = val;
34350 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34351 this.syncRowHeights(rowIndex, rowIndex);
34354 calcColumnWidth : function(colIndex, maxRowsToMeasure){
34356 if(this.grid.autoSizeHeaders){
34357 var h = this.getHeaderCellMeasure(colIndex);
34358 maxWidth = Math.max(maxWidth, h.scrollWidth);
34361 if(this.cm.isLocked(colIndex)){
34362 tb = this.getLockedTable();
34365 tb = this.getBodyTable();
34366 index = colIndex - this.cm.getLockedCount();
34369 var rows = tb.rows;
34370 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34371 for(var i = 0; i < stopIndex; i++){
34372 var cell = rows[i].childNodes[index].firstChild;
34373 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34376 return maxWidth + /*margin for error in IE*/ 5;
34379 * Autofit a column to its content.
34380 * @param {Number} colIndex
34381 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34383 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34384 if(this.cm.isHidden(colIndex)){
34385 return; // can't calc a hidden column
34388 var cid = this.cm.getColumnId(colIndex);
34389 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34390 if(this.grid.autoSizeHeaders){
34391 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34394 var newWidth = this.calcColumnWidth(colIndex);
34395 this.cm.setColumnWidth(colIndex,
34396 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34397 if(!suppressEvent){
34398 this.grid.fireEvent("columnresize", colIndex, newWidth);
34403 * Autofits all columns to their content and then expands to fit any extra space in the grid
34405 autoSizeColumns : function(){
34406 var cm = this.grid.colModel;
34407 var colCount = cm.getColumnCount();
34408 for(var i = 0; i < colCount; i++){
34409 this.autoSizeColumn(i, true, true);
34411 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34414 this.updateColumns();
34420 * Autofits all columns to the grid's width proportionate with their current size
34421 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34423 fitColumns : function(reserveScrollSpace){
34424 var cm = this.grid.colModel;
34425 var colCount = cm.getColumnCount();
34429 for (i = 0; i < colCount; i++){
34430 if(!cm.isHidden(i) && !cm.isFixed(i)){
34431 w = cm.getColumnWidth(i);
34437 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34438 if(reserveScrollSpace){
34441 var frac = (avail - cm.getTotalWidth())/width;
34442 while (cols.length){
34445 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34447 this.updateColumns();
34451 onRowSelect : function(rowIndex){
34452 var row = this.getRowComposite(rowIndex);
34453 row.addClass("x-grid-row-selected");
34456 onRowDeselect : function(rowIndex){
34457 var row = this.getRowComposite(rowIndex);
34458 row.removeClass("x-grid-row-selected");
34461 onCellSelect : function(row, col){
34462 var cell = this.getCell(row, col);
34464 Roo.fly(cell).addClass("x-grid-cell-selected");
34468 onCellDeselect : function(row, col){
34469 var cell = this.getCell(row, col);
34471 Roo.fly(cell).removeClass("x-grid-cell-selected");
34475 updateHeaderSortState : function(){
34477 // sort state can be single { field: xxx, direction : yyy}
34478 // or { xxx=>ASC , yyy : DESC ..... }
34481 if (!this.ds.multiSort) {
34482 var state = this.ds.getSortState();
34486 mstate[state.field] = state.direction;
34487 // FIXME... - this is not used here.. but might be elsewhere..
34488 this.sortState = state;
34491 mstate = this.ds.sortToggle;
34493 //remove existing sort classes..
34495 var sc = this.sortClasses;
34496 var hds = this.el.select(this.headerSelector).removeClass(sc);
34498 for(var f in mstate) {
34500 var sortColumn = this.cm.findColumnIndex(f);
34502 if(sortColumn != -1){
34503 var sortDir = mstate[f];
34504 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34513 handleHeaderClick : function(g, index,e){
34515 Roo.log("header click");
34518 // touch events on header are handled by context
34519 this.handleHdCtx(g,index,e);
34524 if(this.headersDisabled){
34527 var dm = g.dataSource, cm = g.colModel;
34528 if(!cm.isSortable(index)){
34533 if (dm.multiSort) {
34534 // update the sortOrder
34536 for(var i = 0; i < cm.config.length; i++ ) {
34538 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34539 continue; // dont' bother, it's not in sort list or being set.
34542 so.push(cm.config[i].dataIndex);
34548 dm.sort(cm.getDataIndex(index));
34552 destroy : function(){
34554 this.colMenu.removeAll();
34555 Roo.menu.MenuMgr.unregister(this.colMenu);
34556 this.colMenu.getEl().remove();
34557 delete this.colMenu;
34560 this.hmenu.removeAll();
34561 Roo.menu.MenuMgr.unregister(this.hmenu);
34562 this.hmenu.getEl().remove();
34565 if(this.grid.enableColumnMove){
34566 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34568 for(var dd in dds){
34569 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34570 var elid = dds[dd].dragElId;
34572 Roo.get(elid).remove();
34573 } else if(dds[dd].config.isTarget){
34574 dds[dd].proxyTop.remove();
34575 dds[dd].proxyBottom.remove();
34578 if(Roo.dd.DDM.locationCache[dd]){
34579 delete Roo.dd.DDM.locationCache[dd];
34582 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34585 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34586 this.bind(null, null);
34587 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34590 handleLockChange : function(){
34591 this.refresh(true);
34594 onDenyColumnLock : function(){
34598 onDenyColumnHide : function(){
34602 handleHdMenuClick : function(item){
34603 var index = this.hdCtxIndex;
34604 var cm = this.cm, ds = this.ds;
34607 ds.sort(cm.getDataIndex(index), "ASC");
34610 ds.sort(cm.getDataIndex(index), "DESC");
34613 var lc = cm.getLockedCount();
34614 if(cm.getColumnCount(true) <= lc+1){
34615 this.onDenyColumnLock();
34619 cm.setLocked(index, true, true);
34620 cm.moveColumn(index, lc);
34621 this.grid.fireEvent("columnmove", index, lc);
34623 cm.setLocked(index, true);
34627 var lc = cm.getLockedCount();
34628 if((lc-1) != index){
34629 cm.setLocked(index, false, true);
34630 cm.moveColumn(index, lc-1);
34631 this.grid.fireEvent("columnmove", index, lc-1);
34633 cm.setLocked(index, false);
34636 case 'wider': // used to expand cols on touch..
34638 var cw = cm.getColumnWidth(index);
34639 cw += (item.id == 'wider' ? 1 : -1) * 50;
34640 cw = Math.max(0, cw);
34641 cw = Math.min(cw,4000);
34642 cm.setColumnWidth(index, cw);
34646 index = cm.getIndexById(item.id.substr(4));
34648 if(item.checked && cm.getColumnCount(true) <= 1){
34649 this.onDenyColumnHide();
34652 cm.setHidden(index, item.checked);
34658 beforeColMenuShow : function(){
34659 var cm = this.cm, colCount = cm.getColumnCount();
34660 this.colMenu.removeAll();
34661 for(var i = 0; i < colCount; i++){
34662 this.colMenu.add(new Roo.menu.CheckItem({
34663 id: "col-"+cm.getColumnId(i),
34664 text: cm.getColumnHeader(i),
34665 checked: !cm.isHidden(i),
34671 handleHdCtx : function(g, index, e){
34673 var hd = this.getHeaderCell(index);
34674 this.hdCtxIndex = index;
34675 var ms = this.hmenu.items, cm = this.cm;
34676 ms.get("asc").setDisabled(!cm.isSortable(index));
34677 ms.get("desc").setDisabled(!cm.isSortable(index));
34678 if(this.grid.enableColLock !== false){
34679 ms.get("lock").setDisabled(cm.isLocked(index));
34680 ms.get("unlock").setDisabled(!cm.isLocked(index));
34682 this.hmenu.show(hd, "tl-bl");
34685 handleHdOver : function(e){
34686 var hd = this.findHeaderCell(e.getTarget());
34687 if(hd && !this.headersDisabled){
34688 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34689 this.fly(hd).addClass("x-grid-hd-over");
34694 handleHdOut : function(e){
34695 var hd = this.findHeaderCell(e.getTarget());
34697 this.fly(hd).removeClass("x-grid-hd-over");
34701 handleSplitDblClick : function(e, t){
34702 var i = this.getCellIndex(t);
34703 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34704 this.autoSizeColumn(i, true);
34709 render : function(){
34712 var colCount = cm.getColumnCount();
34714 if(this.grid.monitorWindowResize === true){
34715 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34717 var header = this.renderHeaders();
34718 var body = this.templates.body.apply({rows:""});
34719 var html = this.templates.master.apply({
34722 lockedHeader: header[0],
34726 //this.updateColumns();
34728 this.grid.getGridEl().dom.innerHTML = html;
34730 this.initElements();
34732 // a kludge to fix the random scolling effect in webkit
34733 this.el.on("scroll", function() {
34734 this.el.dom.scrollTop=0; // hopefully not recursive..
34737 this.scroller.on("scroll", this.handleScroll, this);
34738 this.lockedBody.on("mousewheel", this.handleWheel, this);
34739 this.mainBody.on("mousewheel", this.handleWheel, this);
34741 this.mainHd.on("mouseover", this.handleHdOver, this);
34742 this.mainHd.on("mouseout", this.handleHdOut, this);
34743 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34744 {delegate: "."+this.splitClass});
34746 this.lockedHd.on("mouseover", this.handleHdOver, this);
34747 this.lockedHd.on("mouseout", this.handleHdOut, this);
34748 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34749 {delegate: "."+this.splitClass});
34751 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34752 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34755 this.updateSplitters();
34757 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34758 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34759 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34762 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34763 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34765 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34766 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34768 if(this.grid.enableColLock !== false){
34769 this.hmenu.add('-',
34770 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34771 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34775 this.hmenu.add('-',
34776 {id:"wider", text: this.columnsWiderText},
34777 {id:"narrow", text: this.columnsNarrowText }
34783 if(this.grid.enableColumnHide !== false){
34785 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34786 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34787 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34789 this.hmenu.add('-',
34790 {id:"columns", text: this.columnsText, menu: this.colMenu}
34793 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34795 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34798 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34799 this.dd = new Roo.grid.GridDragZone(this.grid, {
34800 ddGroup : this.grid.ddGroup || 'GridDD'
34806 for(var i = 0; i < colCount; i++){
34807 if(cm.isHidden(i)){
34808 this.hideColumn(i);
34810 if(cm.config[i].align){
34811 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34812 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34816 this.updateHeaderSortState();
34818 this.beforeInitialResize();
34821 // two part rendering gives faster view to the user
34822 this.renderPhase2.defer(1, this);
34825 renderPhase2 : function(){
34826 // render the rows now
34828 if(this.grid.autoSizeColumns){
34829 this.autoSizeColumns();
34833 beforeInitialResize : function(){
34837 onColumnSplitterMoved : function(i, w){
34838 this.userResized = true;
34839 var cm = this.grid.colModel;
34840 cm.setColumnWidth(i, w, true);
34841 var cid = cm.getColumnId(i);
34842 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34843 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34844 this.updateSplitters();
34846 this.grid.fireEvent("columnresize", i, w);
34849 syncRowHeights : function(startIndex, endIndex){
34850 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34851 startIndex = startIndex || 0;
34852 var mrows = this.getBodyTable().rows;
34853 var lrows = this.getLockedTable().rows;
34854 var len = mrows.length-1;
34855 endIndex = Math.min(endIndex || len, len);
34856 for(var i = startIndex; i <= endIndex; i++){
34857 var m = mrows[i], l = lrows[i];
34858 var h = Math.max(m.offsetHeight, l.offsetHeight);
34859 m.style.height = l.style.height = h + "px";
34864 layout : function(initialRender, is2ndPass)
34867 var auto = g.autoHeight;
34868 var scrollOffset = 16;
34869 var c = g.getGridEl(), cm = this.cm,
34870 expandCol = g.autoExpandColumn,
34872 //c.beginMeasure();
34874 if(!c.dom.offsetWidth){ // display:none?
34876 this.lockedWrap.show();
34877 this.mainWrap.show();
34882 var hasLock = this.cm.isLocked(0);
34884 var tbh = this.headerPanel.getHeight();
34885 var bbh = this.footerPanel.getHeight();
34888 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34889 var newHeight = ch + c.getBorderWidth("tb");
34891 newHeight = Math.min(g.maxHeight, newHeight);
34893 c.setHeight(newHeight);
34897 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34900 var s = this.scroller;
34902 var csize = c.getSize(true);
34904 this.el.setSize(csize.width, csize.height);
34906 this.headerPanel.setWidth(csize.width);
34907 this.footerPanel.setWidth(csize.width);
34909 var hdHeight = this.mainHd.getHeight();
34910 var vw = csize.width;
34911 var vh = csize.height - (tbh + bbh);
34915 var bt = this.getBodyTable();
34917 if(cm.getLockedCount() == cm.config.length){
34918 bt = this.getLockedTable();
34921 var ltWidth = hasLock ?
34922 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34924 var scrollHeight = bt.offsetHeight;
34925 var scrollWidth = ltWidth + bt.offsetWidth;
34926 var vscroll = false, hscroll = false;
34928 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34930 var lw = this.lockedWrap, mw = this.mainWrap;
34931 var lb = this.lockedBody, mb = this.mainBody;
34933 setTimeout(function(){
34934 var t = s.dom.offsetTop;
34935 var w = s.dom.clientWidth,
34936 h = s.dom.clientHeight;
34939 lw.setSize(ltWidth, h);
34941 mw.setLeftTop(ltWidth, t);
34942 mw.setSize(w-ltWidth, h);
34944 lb.setHeight(h-hdHeight);
34945 mb.setHeight(h-hdHeight);
34947 if(is2ndPass !== true && !gv.userResized && expandCol){
34948 // high speed resize without full column calculation
34950 var ci = cm.getIndexById(expandCol);
34952 ci = cm.findColumnIndex(expandCol);
34954 ci = Math.max(0, ci); // make sure it's got at least the first col.
34955 var expandId = cm.getColumnId(ci);
34956 var tw = cm.getTotalWidth(false);
34957 var currentWidth = cm.getColumnWidth(ci);
34958 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34959 if(currentWidth != cw){
34960 cm.setColumnWidth(ci, cw, true);
34961 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34962 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34963 gv.updateSplitters();
34964 gv.layout(false, true);
34976 onWindowResize : function(){
34977 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34983 appendFooter : function(parentEl){
34987 sortAscText : "Sort Ascending",
34988 sortDescText : "Sort Descending",
34989 lockText : "Lock Column",
34990 unlockText : "Unlock Column",
34991 columnsText : "Columns",
34993 columnsWiderText : "Wider",
34994 columnsNarrowText : "Thinner"
34998 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34999 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35000 this.proxy.el.addClass('x-grid3-col-dd');
35003 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35004 handleMouseDown : function(e){
35008 callHandleMouseDown : function(e){
35009 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35014 * Ext JS Library 1.1.1
35015 * Copyright(c) 2006-2007, Ext JS, LLC.
35017 * Originally Released Under LGPL - original licence link has changed is not relivant.
35020 * <script type="text/javascript">
35023 * @extends Roo.dd.DDProxy
35024 * @class Roo.grid.SplitDragZone
35025 * Support for Column Header resizing
35027 * @param {Object} config
35030 // This is a support class used internally by the Grid components
35031 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35033 this.view = grid.getView();
35034 this.proxy = this.view.resizeProxy;
35035 Roo.grid.SplitDragZone.superclass.constructor.call(
35038 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35040 dragElId : Roo.id(this.proxy.dom),
35045 this.setHandleElId(Roo.id(hd));
35046 if (hd2 !== false) {
35047 this.setOuterHandleElId(Roo.id(hd2));
35050 this.scroll = false;
35052 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35053 fly: Roo.Element.fly,
35055 b4StartDrag : function(x, y){
35056 this.view.headersDisabled = true;
35057 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35058 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35060 this.proxy.setHeight(h);
35062 // for old system colWidth really stored the actual width?
35063 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35064 // which in reality did not work.. - it worked only for fixed sizes
35065 // for resizable we need to use actual sizes.
35066 var w = this.cm.getColumnWidth(this.cellIndex);
35067 if (!this.view.mainWrap) {
35069 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35074 // this was w-this.grid.minColumnWidth;
35075 // doesnt really make sense? - w = thie curren width or the rendered one?
35076 var minw = Math.max(w-this.grid.minColumnWidth, 0);
35077 this.resetConstraints();
35078 this.setXConstraint(minw, 1000);
35079 this.setYConstraint(0, 0);
35080 this.minX = x - minw;
35081 this.maxX = x + 1000;
35083 if (!this.view.mainWrap) { // this is Bootstrap code..
35084 this.getDragEl().style.display='block';
35087 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35091 handleMouseDown : function(e){
35092 ev = Roo.EventObject.setEvent(e);
35093 var t = this.fly(ev.getTarget());
35094 if(t.hasClass("x-grid-split")){
35095 this.cellIndex = this.view.getCellIndex(t.dom);
35096 this.split = t.dom;
35097 this.cm = this.grid.colModel;
35098 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35099 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35104 endDrag : function(e){
35105 this.view.headersDisabled = false;
35106 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35107 var diff = endX - this.startPos;
35109 var w = this.cm.getColumnWidth(this.cellIndex);
35110 if (!this.view.mainWrap) {
35113 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35116 autoOffset : function(){
35117 this.setDelta(0,0);
35121 * Ext JS Library 1.1.1
35122 * Copyright(c) 2006-2007, Ext JS, LLC.
35124 * Originally Released Under LGPL - original licence link has changed is not relivant.
35127 * <script type="text/javascript">
35131 // This is a support class used internally by the Grid components
35132 Roo.grid.GridDragZone = function(grid, config){
35133 this.view = grid.getView();
35134 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35135 if(this.view.lockedBody){
35136 this.setHandleElId(Roo.id(this.view.mainBody.dom));
35137 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35139 this.scroll = false;
35141 this.ddel = document.createElement('div');
35142 this.ddel.className = 'x-grid-dd-wrap';
35145 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35146 ddGroup : "GridDD",
35148 getDragData : function(e){
35149 var t = Roo.lib.Event.getTarget(e);
35150 var rowIndex = this.view.findRowIndex(t);
35151 var sm = this.grid.selModel;
35153 //Roo.log(rowIndex);
35155 if (sm.getSelectedCell) {
35156 // cell selection..
35157 if (!sm.getSelectedCell()) {
35160 if (rowIndex != sm.getSelectedCell()[0]) {
35165 if (sm.getSelections && sm.getSelections().length < 1) {
35170 // before it used to all dragging of unseleted... - now we dont do that.
35171 if(rowIndex !== false){
35176 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35178 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35181 if (e.hasModifier()){
35182 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35185 Roo.log("getDragData");
35190 rowIndex: rowIndex,
35191 selections: sm.getSelections ? sm.getSelections() : (
35192 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35199 onInitDrag : function(e){
35200 var data = this.dragData;
35201 this.ddel.innerHTML = this.grid.getDragDropText();
35202 this.proxy.update(this.ddel);
35203 // fire start drag?
35206 afterRepair : function(){
35207 this.dragging = false;
35210 getRepairXY : function(e, data){
35214 onEndDrag : function(data, e){
35218 onValidDrop : function(dd, e, id){
35223 beforeInvalidDrop : function(e, id){
35228 * Ext JS Library 1.1.1
35229 * Copyright(c) 2006-2007, Ext JS, LLC.
35231 * Originally Released Under LGPL - original licence link has changed is not relivant.
35234 * <script type="text/javascript">
35239 * @class Roo.grid.ColumnModel
35240 * @extends Roo.util.Observable
35241 * This is the default implementation of a ColumnModel used by the Grid. It defines
35242 * the columns in the grid.
35245 var colModel = new Roo.grid.ColumnModel([
35246 {header: "Ticker", width: 60, sortable: true, locked: true},
35247 {header: "Company Name", width: 150, sortable: true},
35248 {header: "Market Cap.", width: 100, sortable: true},
35249 {header: "$ Sales", width: 100, sortable: true, renderer: money},
35250 {header: "Employees", width: 100, sortable: true, resizable: false}
35255 * The config options listed for this class are options which may appear in each
35256 * individual column definition.
35257 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35259 * @param {Object} config An Array of column config objects. See this class's
35260 * config objects for details.
35262 Roo.grid.ColumnModel = function(config){
35264 * The config passed into the constructor
35266 this.config = []; //config;
35269 // if no id, create one
35270 // if the column does not have a dataIndex mapping,
35271 // map it to the order it is in the config
35272 for(var i = 0, len = config.length; i < len; i++){
35273 this.addColumn(config[i]);
35278 * The width of columns which have no width specified (defaults to 100)
35281 this.defaultWidth = 100;
35284 * Default sortable of columns which have no sortable specified (defaults to false)
35287 this.defaultSortable = false;
35291 * @event widthchange
35292 * Fires when the width of a column changes.
35293 * @param {ColumnModel} this
35294 * @param {Number} columnIndex The column index
35295 * @param {Number} newWidth The new width
35297 "widthchange": true,
35299 * @event headerchange
35300 * Fires when the text of a header changes.
35301 * @param {ColumnModel} this
35302 * @param {Number} columnIndex The column index
35303 * @param {Number} newText The new header text
35305 "headerchange": true,
35307 * @event hiddenchange
35308 * Fires when a column is hidden or "unhidden".
35309 * @param {ColumnModel} this
35310 * @param {Number} columnIndex The column index
35311 * @param {Boolean} hidden true if hidden, false otherwise
35313 "hiddenchange": true,
35315 * @event columnmoved
35316 * Fires when a column is moved.
35317 * @param {ColumnModel} this
35318 * @param {Number} oldIndex
35319 * @param {Number} newIndex
35321 "columnmoved" : true,
35323 * @event columlockchange
35324 * Fires when a column's locked state is changed
35325 * @param {ColumnModel} this
35326 * @param {Number} colIndex
35327 * @param {Boolean} locked true if locked
35329 "columnlockchange" : true
35331 Roo.grid.ColumnModel.superclass.constructor.call(this);
35333 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35335 * @cfg {String} header The header text to display in the Grid view.
35338 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35341 * @cfg {String} smHeader Header at Bootsrap Small width
35344 * @cfg {String} mdHeader Header at Bootsrap Medium width
35347 * @cfg {String} lgHeader Header at Bootsrap Large width
35350 * @cfg {String} xlHeader Header at Bootsrap extra Large width
35353 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35354 * {@link Roo.data.Record} definition from which to draw the column's value. If not
35355 * specified, the column's index is used as an index into the Record's data Array.
35358 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35359 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35362 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35363 * Defaults to the value of the {@link #defaultSortable} property.
35364 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35367 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
35370 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
35373 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35376 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35379 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35380 * given the cell's data value. See {@link #setRenderer}. If not specified, the
35381 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35382 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35385 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
35388 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
35391 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
35394 * @cfg {String} cursor (Optional)
35397 * @cfg {String} tooltip (Optional)
35400 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35403 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35406 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35409 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35412 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35415 * Returns the id of the column at the specified index.
35416 * @param {Number} index The column index
35417 * @return {String} the id
35419 getColumnId : function(index){
35420 return this.config[index].id;
35424 * Returns the column for a specified id.
35425 * @param {String} id The column id
35426 * @return {Object} the column
35428 getColumnById : function(id){
35429 return this.lookup[id];
35434 * Returns the column Object for a specified dataIndex.
35435 * @param {String} dataIndex The column dataIndex
35436 * @return {Object|Boolean} the column or false if not found
35438 getColumnByDataIndex: function(dataIndex){
35439 var index = this.findColumnIndex(dataIndex);
35440 return index > -1 ? this.config[index] : false;
35444 * Returns the index for a specified column id.
35445 * @param {String} id The column id
35446 * @return {Number} the index, or -1 if not found
35448 getIndexById : function(id){
35449 for(var i = 0, len = this.config.length; i < len; i++){
35450 if(this.config[i].id == id){
35458 * Returns the index for a specified column dataIndex.
35459 * @param {String} dataIndex The column dataIndex
35460 * @return {Number} the index, or -1 if not found
35463 findColumnIndex : function(dataIndex){
35464 for(var i = 0, len = this.config.length; i < len; i++){
35465 if(this.config[i].dataIndex == dataIndex){
35473 moveColumn : function(oldIndex, newIndex){
35474 var c = this.config[oldIndex];
35475 this.config.splice(oldIndex, 1);
35476 this.config.splice(newIndex, 0, c);
35477 this.dataMap = null;
35478 this.fireEvent("columnmoved", this, oldIndex, newIndex);
35481 isLocked : function(colIndex){
35482 return this.config[colIndex].locked === true;
35485 setLocked : function(colIndex, value, suppressEvent){
35486 if(this.isLocked(colIndex) == value){
35489 this.config[colIndex].locked = value;
35490 if(!suppressEvent){
35491 this.fireEvent("columnlockchange", this, colIndex, value);
35495 getTotalLockedWidth : function(){
35496 var totalWidth = 0;
35497 for(var i = 0; i < this.config.length; i++){
35498 if(this.isLocked(i) && !this.isHidden(i)){
35499 this.totalWidth += this.getColumnWidth(i);
35505 getLockedCount : function(){
35506 for(var i = 0, len = this.config.length; i < len; i++){
35507 if(!this.isLocked(i)){
35512 return this.config.length;
35516 * Returns the number of columns.
35519 getColumnCount : function(visibleOnly){
35520 if(visibleOnly === true){
35522 for(var i = 0, len = this.config.length; i < len; i++){
35523 if(!this.isHidden(i)){
35529 return this.config.length;
35533 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35534 * @param {Function} fn
35535 * @param {Object} scope (optional)
35536 * @return {Array} result
35538 getColumnsBy : function(fn, scope){
35540 for(var i = 0, len = this.config.length; i < len; i++){
35541 var c = this.config[i];
35542 if(fn.call(scope||this, c, i) === true){
35550 * Returns true if the specified column is sortable.
35551 * @param {Number} col The column index
35552 * @return {Boolean}
35554 isSortable : function(col){
35555 if(typeof this.config[col].sortable == "undefined"){
35556 return this.defaultSortable;
35558 return this.config[col].sortable;
35562 * Returns the rendering (formatting) function defined for the column.
35563 * @param {Number} col The column index.
35564 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35566 getRenderer : function(col){
35567 if(!this.config[col].renderer){
35568 return Roo.grid.ColumnModel.defaultRenderer;
35570 return this.config[col].renderer;
35574 * Sets the rendering (formatting) function for a column.
35575 * @param {Number} col The column index
35576 * @param {Function} fn The function to use to process the cell's raw data
35577 * to return HTML markup for the grid view. The render function is called with
35578 * the following parameters:<ul>
35579 * <li>Data value.</li>
35580 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35581 * <li>css A CSS style string to apply to the table cell.</li>
35582 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35583 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35584 * <li>Row index</li>
35585 * <li>Column index</li>
35586 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35588 setRenderer : function(col, fn){
35589 this.config[col].renderer = fn;
35593 * Returns the width for the specified column.
35594 * @param {Number} col The column index
35595 * @param (optional) {String} gridSize bootstrap width size.
35598 getColumnWidth : function(col, gridSize)
35600 var cfg = this.config[col];
35602 if (typeof(gridSize) == 'undefined') {
35603 return cfg.width * 1 || this.defaultWidth;
35605 if (gridSize === false) { // if we set it..
35606 return cfg.width || false;
35608 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35610 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35611 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35614 return cfg[ sizes[i] ];
35621 * Sets the width for a column.
35622 * @param {Number} col The column index
35623 * @param {Number} width The new width
35625 setColumnWidth : function(col, width, suppressEvent){
35626 this.config[col].width = width;
35627 this.totalWidth = null;
35628 if(!suppressEvent){
35629 this.fireEvent("widthchange", this, col, width);
35634 * Returns the total width of all columns.
35635 * @param {Boolean} includeHidden True to include hidden column widths
35638 getTotalWidth : function(includeHidden){
35639 if(!this.totalWidth){
35640 this.totalWidth = 0;
35641 for(var i = 0, len = this.config.length; i < len; i++){
35642 if(includeHidden || !this.isHidden(i)){
35643 this.totalWidth += this.getColumnWidth(i);
35647 return this.totalWidth;
35651 * Returns the header for the specified column.
35652 * @param {Number} col The column index
35655 getColumnHeader : function(col){
35656 return this.config[col].header;
35660 * Sets the header for a column.
35661 * @param {Number} col The column index
35662 * @param {String} header The new header
35664 setColumnHeader : function(col, header){
35665 this.config[col].header = header;
35666 this.fireEvent("headerchange", this, col, header);
35670 * Returns the tooltip for the specified column.
35671 * @param {Number} col The column index
35674 getColumnTooltip : function(col){
35675 return this.config[col].tooltip;
35678 * Sets the tooltip for a column.
35679 * @param {Number} col The column index
35680 * @param {String} tooltip The new tooltip
35682 setColumnTooltip : function(col, tooltip){
35683 this.config[col].tooltip = tooltip;
35687 * Returns the dataIndex for the specified column.
35688 * @param {Number} col The column index
35691 getDataIndex : function(col){
35692 return this.config[col].dataIndex;
35696 * Sets the dataIndex for a column.
35697 * @param {Number} col The column index
35698 * @param {Number} dataIndex The new dataIndex
35700 setDataIndex : function(col, dataIndex){
35701 this.config[col].dataIndex = dataIndex;
35707 * Returns true if the cell is editable.
35708 * @param {Number} colIndex The column index
35709 * @param {Number} rowIndex The row index - this is nto actually used..?
35710 * @return {Boolean}
35712 isCellEditable : function(colIndex, rowIndex){
35713 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35717 * Returns the editor defined for the cell/column.
35718 * return false or null to disable editing.
35719 * @param {Number} colIndex The column index
35720 * @param {Number} rowIndex The row index
35723 getCellEditor : function(colIndex, rowIndex){
35724 return this.config[colIndex].editor;
35728 * Sets if a column is editable.
35729 * @param {Number} col The column index
35730 * @param {Boolean} editable True if the column is editable
35732 setEditable : function(col, editable){
35733 this.config[col].editable = editable;
35738 * Returns true if the column is hidden.
35739 * @param {Number} colIndex The column index
35740 * @return {Boolean}
35742 isHidden : function(colIndex){
35743 return this.config[colIndex].hidden;
35748 * Returns true if the column width cannot be changed
35750 isFixed : function(colIndex){
35751 return this.config[colIndex].fixed;
35755 * Returns true if the column can be resized
35756 * @return {Boolean}
35758 isResizable : function(colIndex){
35759 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35762 * Sets if a column is hidden.
35763 * @param {Number} colIndex The column index
35764 * @param {Boolean} hidden True if the column is hidden
35766 setHidden : function(colIndex, hidden){
35767 this.config[colIndex].hidden = hidden;
35768 this.totalWidth = null;
35769 this.fireEvent("hiddenchange", this, colIndex, hidden);
35773 * Sets the editor for a column.
35774 * @param {Number} col The column index
35775 * @param {Object} editor The editor object
35777 setEditor : function(col, editor){
35778 this.config[col].editor = editor;
35781 * Add a column (experimental...) - defaults to adding to the end..
35782 * @param {Object} config
35784 addColumn : function(c)
35787 var i = this.config.length;
35788 this.config[i] = c;
35790 if(typeof c.dataIndex == "undefined"){
35793 if(typeof c.renderer == "string"){
35794 c.renderer = Roo.util.Format[c.renderer];
35796 if(typeof c.id == "undefined"){
35799 if(c.editor && c.editor.xtype){
35800 c.editor = Roo.factory(c.editor, Roo.grid);
35802 if(c.editor && c.editor.isFormField){
35803 c.editor = new Roo.grid.GridEditor(c.editor);
35805 this.lookup[c.id] = c;
35810 Roo.grid.ColumnModel.defaultRenderer = function(value)
35812 if(typeof value == "object") {
35815 if(typeof value == "string" && value.length < 1){
35819 return String.format("{0}", value);
35822 // Alias for backwards compatibility
35823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35826 * Ext JS Library 1.1.1
35827 * Copyright(c) 2006-2007, Ext JS, LLC.
35829 * Originally Released Under LGPL - original licence link has changed is not relivant.
35832 * <script type="text/javascript">
35836 * @class Roo.grid.AbstractSelectionModel
35837 * @extends Roo.util.Observable
35839 * Abstract base class for grid SelectionModels. It provides the interface that should be
35840 * implemented by descendant classes. This class should not be directly instantiated.
35843 Roo.grid.AbstractSelectionModel = function(){
35844 this.locked = false;
35845 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35848 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35849 /** @ignore Called by the grid automatically. Do not call directly. */
35850 init : function(grid){
35856 * Locks the selections.
35859 this.locked = true;
35863 * Unlocks the selections.
35865 unlock : function(){
35866 this.locked = false;
35870 * Returns true if the selections are locked.
35871 * @return {Boolean}
35873 isLocked : function(){
35874 return this.locked;
35878 * Ext JS Library 1.1.1
35879 * Copyright(c) 2006-2007, Ext JS, LLC.
35881 * Originally Released Under LGPL - original licence link has changed is not relivant.
35884 * <script type="text/javascript">
35887 * @extends Roo.grid.AbstractSelectionModel
35888 * @class Roo.grid.RowSelectionModel
35889 * The default SelectionModel used by {@link Roo.grid.Grid}.
35890 * It supports multiple selections and keyboard selection/navigation.
35892 * @param {Object} config
35894 Roo.grid.RowSelectionModel = function(config){
35895 Roo.apply(this, config);
35896 this.selections = new Roo.util.MixedCollection(false, function(o){
35901 this.lastActive = false;
35905 * @event selectionchange
35906 * Fires when the selection changes
35907 * @param {SelectionModel} this
35909 "selectionchange" : true,
35911 * @event afterselectionchange
35912 * Fires after the selection changes (eg. by key press or clicking)
35913 * @param {SelectionModel} this
35915 "afterselectionchange" : true,
35917 * @event beforerowselect
35918 * Fires when a row is selected being selected, return false to cancel.
35919 * @param {SelectionModel} this
35920 * @param {Number} rowIndex The selected index
35921 * @param {Boolean} keepExisting False if other selections will be cleared
35923 "beforerowselect" : true,
35926 * Fires when a row is selected.
35927 * @param {SelectionModel} this
35928 * @param {Number} rowIndex The selected index
35929 * @param {Roo.data.Record} r The record
35931 "rowselect" : true,
35933 * @event rowdeselect
35934 * Fires when a row is deselected.
35935 * @param {SelectionModel} this
35936 * @param {Number} rowIndex The selected index
35938 "rowdeselect" : true
35940 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35941 this.locked = false;
35944 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
35946 * @cfg {Boolean} singleSelect
35947 * True to allow selection of only one row at a time (defaults to false)
35949 singleSelect : false,
35952 initEvents : function(){
35954 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35955 this.grid.on("mousedown", this.handleMouseDown, this);
35956 }else{ // allow click to work like normal
35957 this.grid.on("rowclick", this.handleDragableRowClick, this);
35959 // bootstrap does not have a view..
35960 var view = this.grid.view ? this.grid.view : this.grid;
35961 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35962 "up" : function(e){
35964 this.selectPrevious(e.shiftKey);
35965 }else if(this.last !== false && this.lastActive !== false){
35966 var last = this.last;
35967 this.selectRange(this.last, this.lastActive-1);
35968 view.focusRow(this.lastActive);
35969 if(last !== false){
35973 this.selectFirstRow();
35975 this.fireEvent("afterselectionchange", this);
35977 "down" : function(e){
35979 this.selectNext(e.shiftKey);
35980 }else if(this.last !== false && this.lastActive !== false){
35981 var last = this.last;
35982 this.selectRange(this.last, this.lastActive+1);
35983 view.focusRow(this.lastActive);
35984 if(last !== false){
35988 this.selectFirstRow();
35990 this.fireEvent("afterselectionchange", this);
35996 view.on("refresh", this.onRefresh, this);
35997 view.on("rowupdated", this.onRowUpdated, this);
35998 view.on("rowremoved", this.onRemove, this);
36002 onRefresh : function(){
36003 var ds = this.grid.ds, i, v = this.grid.view;
36004 var s = this.selections;
36005 s.each(function(r){
36006 if((i = ds.indexOfId(r.id)) != -1){
36008 s.add(ds.getAt(i)); // updating the selection relate data
36016 onRemove : function(v, index, r){
36017 this.selections.remove(r);
36021 onRowUpdated : function(v, index, r){
36022 if(this.isSelected(r)){
36023 v.onRowSelect(index);
36029 * @param {Array} records The records to select
36030 * @param {Boolean} keepExisting (optional) True to keep existing selections
36032 selectRecords : function(records, keepExisting){
36034 this.clearSelections();
36036 var ds = this.grid.ds;
36037 for(var i = 0, len = records.length; i < len; i++){
36038 this.selectRow(ds.indexOf(records[i]), true);
36043 * Gets the number of selected rows.
36046 getCount : function(){
36047 return this.selections.length;
36051 * Selects the first row in the grid.
36053 selectFirstRow : function(){
36058 * Select the last row.
36059 * @param {Boolean} keepExisting (optional) True to keep existing selections
36061 selectLastRow : function(keepExisting){
36062 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36066 * Selects the row immediately following the last selected row.
36067 * @param {Boolean} keepExisting (optional) True to keep existing selections
36069 selectNext : function(keepExisting){
36070 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36071 this.selectRow(this.last+1, keepExisting);
36072 var view = this.grid.view ? this.grid.view : this.grid;
36073 view.focusRow(this.last);
36078 * Selects the row that precedes the last selected row.
36079 * @param {Boolean} keepExisting (optional) True to keep existing selections
36081 selectPrevious : function(keepExisting){
36083 this.selectRow(this.last-1, keepExisting);
36084 var view = this.grid.view ? this.grid.view : this.grid;
36085 view.focusRow(this.last);
36090 * Returns the selected records
36091 * @return {Array} Array of selected records
36093 getSelections : function(){
36094 return [].concat(this.selections.items);
36098 * Returns the first selected record.
36101 getSelected : function(){
36102 return this.selections.itemAt(0);
36107 * Clears all selections.
36109 clearSelections : function(fast){
36114 var ds = this.grid.ds;
36115 var s = this.selections;
36116 s.each(function(r){
36117 this.deselectRow(ds.indexOfId(r.id));
36121 this.selections.clear();
36128 * Selects all rows.
36130 selectAll : function(){
36134 this.selections.clear();
36135 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36136 this.selectRow(i, true);
36141 * Returns True if there is a selection.
36142 * @return {Boolean}
36144 hasSelection : function(){
36145 return this.selections.length > 0;
36149 * Returns True if the specified row is selected.
36150 * @param {Number/Record} record The record or index of the record to check
36151 * @return {Boolean}
36153 isSelected : function(index){
36154 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36155 return (r && this.selections.key(r.id) ? true : false);
36159 * Returns True if the specified record id is selected.
36160 * @param {String} id The id of record to check
36161 * @return {Boolean}
36163 isIdSelected : function(id){
36164 return (this.selections.key(id) ? true : false);
36168 handleMouseDown : function(e, t)
36170 var view = this.grid.view ? this.grid.view : this.grid;
36172 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36175 if(e.shiftKey && this.last !== false){
36176 var last = this.last;
36177 this.selectRange(last, rowIndex, e.ctrlKey);
36178 this.last = last; // reset the last
36179 view.focusRow(rowIndex);
36181 var isSelected = this.isSelected(rowIndex);
36182 if(e.button !== 0 && isSelected){
36183 view.focusRow(rowIndex);
36184 }else if(e.ctrlKey && isSelected){
36185 this.deselectRow(rowIndex);
36186 }else if(!isSelected){
36187 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36188 view.focusRow(rowIndex);
36191 this.fireEvent("afterselectionchange", this);
36194 handleDragableRowClick : function(grid, rowIndex, e)
36196 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36197 this.selectRow(rowIndex, false);
36198 var view = this.grid.view ? this.grid.view : this.grid;
36199 view.focusRow(rowIndex);
36200 this.fireEvent("afterselectionchange", this);
36205 * Selects multiple rows.
36206 * @param {Array} rows Array of the indexes of the row to select
36207 * @param {Boolean} keepExisting (optional) True to keep existing selections
36209 selectRows : function(rows, keepExisting){
36211 this.clearSelections();
36213 for(var i = 0, len = rows.length; i < len; i++){
36214 this.selectRow(rows[i], true);
36219 * Selects a range of rows. All rows in between startRow and endRow are also selected.
36220 * @param {Number} startRow The index of the first row in the range
36221 * @param {Number} endRow The index of the last row in the range
36222 * @param {Boolean} keepExisting (optional) True to retain existing selections
36224 selectRange : function(startRow, endRow, keepExisting){
36229 this.clearSelections();
36231 if(startRow <= endRow){
36232 for(var i = startRow; i <= endRow; i++){
36233 this.selectRow(i, true);
36236 for(var i = startRow; i >= endRow; i--){
36237 this.selectRow(i, true);
36243 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36244 * @param {Number} startRow The index of the first row in the range
36245 * @param {Number} endRow The index of the last row in the range
36247 deselectRange : function(startRow, endRow, preventViewNotify){
36251 for(var i = startRow; i <= endRow; i++){
36252 this.deselectRow(i, preventViewNotify);
36258 * @param {Number} row The index of the row to select
36259 * @param {Boolean} keepExisting (optional) True to keep existing selections
36261 selectRow : function(index, keepExisting, preventViewNotify){
36262 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36265 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36266 if(!keepExisting || this.singleSelect){
36267 this.clearSelections();
36269 var r = this.grid.ds.getAt(index);
36270 this.selections.add(r);
36271 this.last = this.lastActive = index;
36272 if(!preventViewNotify){
36273 var view = this.grid.view ? this.grid.view : this.grid;
36274 view.onRowSelect(index);
36276 this.fireEvent("rowselect", this, index, r);
36277 this.fireEvent("selectionchange", this);
36283 * @param {Number} row The index of the row to deselect
36285 deselectRow : function(index, preventViewNotify){
36289 if(this.last == index){
36292 if(this.lastActive == index){
36293 this.lastActive = false;
36295 var r = this.grid.ds.getAt(index);
36296 this.selections.remove(r);
36297 if(!preventViewNotify){
36298 var view = this.grid.view ? this.grid.view : this.grid;
36299 view.onRowDeselect(index);
36301 this.fireEvent("rowdeselect", this, index);
36302 this.fireEvent("selectionchange", this);
36306 restoreLast : function(){
36308 this.last = this._last;
36313 acceptsNav : function(row, col, cm){
36314 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36318 onEditorKey : function(field, e){
36319 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36324 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36326 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36328 }else if(k == e.ENTER && !e.ctrlKey){
36332 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36334 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36336 }else if(k == e.ESC){
36340 g.startEditing(newCell[0], newCell[1]);
36345 * Ext JS Library 1.1.1
36346 * Copyright(c) 2006-2007, Ext JS, LLC.
36348 * Originally Released Under LGPL - original licence link has changed is not relivant.
36351 * <script type="text/javascript">
36354 * @class Roo.grid.CellSelectionModel
36355 * @extends Roo.grid.AbstractSelectionModel
36356 * This class provides the basic implementation for cell selection in a grid.
36358 * @param {Object} config The object containing the configuration of this model.
36359 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36361 Roo.grid.CellSelectionModel = function(config){
36362 Roo.apply(this, config);
36364 this.selection = null;
36368 * @event beforerowselect
36369 * Fires before a cell is selected.
36370 * @param {SelectionModel} this
36371 * @param {Number} rowIndex The selected row index
36372 * @param {Number} colIndex The selected cell index
36374 "beforecellselect" : true,
36376 * @event cellselect
36377 * Fires when a cell is selected.
36378 * @param {SelectionModel} this
36379 * @param {Number} rowIndex The selected row index
36380 * @param {Number} colIndex The selected cell index
36382 "cellselect" : true,
36384 * @event selectionchange
36385 * Fires when the active selection changes.
36386 * @param {SelectionModel} this
36387 * @param {Object} selection null for no selection or an object (o) with two properties
36389 <li>o.record: the record object for the row the selection is in</li>
36390 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36393 "selectionchange" : true,
36396 * Fires when the tab (or enter) was pressed on the last editable cell
36397 * You can use this to trigger add new row.
36398 * @param {SelectionModel} this
36402 * @event beforeeditnext
36403 * Fires before the next editable sell is made active
36404 * You can use this to skip to another cell or fire the tabend
36405 * if you set cell to false
36406 * @param {Object} eventdata object : { cell : [ row, col ] }
36408 "beforeeditnext" : true
36410 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36413 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
36415 enter_is_tab: false,
36418 initEvents : function(){
36419 this.grid.on("mousedown", this.handleMouseDown, this);
36420 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36421 var view = this.grid.view;
36422 view.on("refresh", this.onViewChange, this);
36423 view.on("rowupdated", this.onRowUpdated, this);
36424 view.on("beforerowremoved", this.clearSelections, this);
36425 view.on("beforerowsinserted", this.clearSelections, this);
36426 if(this.grid.isEditor){
36427 this.grid.on("beforeedit", this.beforeEdit, this);
36432 beforeEdit : function(e){
36433 this.select(e.row, e.column, false, true, e.record);
36437 onRowUpdated : function(v, index, r){
36438 if(this.selection && this.selection.record == r){
36439 v.onCellSelect(index, this.selection.cell[1]);
36444 onViewChange : function(){
36445 this.clearSelections(true);
36449 * Returns the currently selected cell,.
36450 * @return {Array} The selected cell (row, column) or null if none selected.
36452 getSelectedCell : function(){
36453 return this.selection ? this.selection.cell : null;
36457 * Clears all selections.
36458 * @param {Boolean} true to prevent the gridview from being notified about the change.
36460 clearSelections : function(preventNotify){
36461 var s = this.selection;
36463 if(preventNotify !== true){
36464 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36466 this.selection = null;
36467 this.fireEvent("selectionchange", this, null);
36472 * Returns true if there is a selection.
36473 * @return {Boolean}
36475 hasSelection : function(){
36476 return this.selection ? true : false;
36480 handleMouseDown : function(e, t){
36481 var v = this.grid.getView();
36482 if(this.isLocked()){
36485 var row = v.findRowIndex(t);
36486 var cell = v.findCellIndex(t);
36487 if(row !== false && cell !== false){
36488 this.select(row, cell);
36494 * @param {Number} rowIndex
36495 * @param {Number} collIndex
36497 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36498 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36499 this.clearSelections();
36500 r = r || this.grid.dataSource.getAt(rowIndex);
36503 cell : [rowIndex, colIndex]
36505 if(!preventViewNotify){
36506 var v = this.grid.getView();
36507 v.onCellSelect(rowIndex, colIndex);
36508 if(preventFocus !== true){
36509 v.focusCell(rowIndex, colIndex);
36512 this.fireEvent("cellselect", this, rowIndex, colIndex);
36513 this.fireEvent("selectionchange", this, this.selection);
36518 isSelectable : function(rowIndex, colIndex, cm){
36519 return !cm.isHidden(colIndex);
36523 handleKeyDown : function(e){
36524 //Roo.log('Cell Sel Model handleKeyDown');
36525 if(!e.isNavKeyPress()){
36528 var g = this.grid, s = this.selection;
36531 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
36533 this.select(cell[0], cell[1]);
36538 var walk = function(row, col, step){
36539 return g.walkCells(row, col, step, sm.isSelectable, sm);
36541 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36548 // handled by onEditorKey
36549 if (g.isEditor && g.editing) {
36553 newCell = walk(r, c-1, -1);
36555 newCell = walk(r, c+1, 1);
36560 newCell = walk(r+1, c, 1);
36564 newCell = walk(r-1, c, -1);
36568 newCell = walk(r, c+1, 1);
36572 newCell = walk(r, c-1, -1);
36577 if(g.isEditor && !g.editing){
36578 g.startEditing(r, c);
36587 this.select(newCell[0], newCell[1]);
36593 acceptsNav : function(row, col, cm){
36594 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36598 * @param {Number} field (not used) - as it's normally used as a listener
36599 * @param {Number} e - event - fake it by using
36601 * var e = Roo.EventObjectImpl.prototype;
36602 * e.keyCode = e.TAB
36606 onEditorKey : function(field, e){
36608 var k = e.getKey(),
36611 ed = g.activeEditor,
36613 ///Roo.log('onEditorKey' + k);
36616 if (this.enter_is_tab && k == e.ENTER) {
36622 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36624 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36630 } else if(k == e.ENTER && !e.ctrlKey){
36633 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36635 } else if(k == e.ESC){
36640 var ecall = { cell : newCell, forward : forward };
36641 this.fireEvent('beforeeditnext', ecall );
36642 newCell = ecall.cell;
36643 forward = ecall.forward;
36647 //Roo.log('next cell after edit');
36648 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36649 } else if (forward) {
36650 // tabbed past last
36651 this.fireEvent.defer(100, this, ['tabend',this]);
36656 * Ext JS Library 1.1.1
36657 * Copyright(c) 2006-2007, Ext JS, LLC.
36659 * Originally Released Under LGPL - original licence link has changed is not relivant.
36662 * <script type="text/javascript">
36666 * @class Roo.grid.EditorGrid
36667 * @extends Roo.grid.Grid
36668 * Class for creating and editable grid.
36669 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36670 * The container MUST have some type of size defined for the grid to fill. The container will be
36671 * automatically set to position relative if it isn't already.
36672 * @param {Object} dataSource The data model to bind to
36673 * @param {Object} colModel The column model with info about this grid's columns
36675 Roo.grid.EditorGrid = function(container, config){
36676 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36677 this.getGridEl().addClass("xedit-grid");
36679 if(!this.selModel){
36680 this.selModel = new Roo.grid.CellSelectionModel();
36683 this.activeEditor = null;
36687 * @event beforeedit
36688 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36689 * <ul style="padding:5px;padding-left:16px;">
36690 * <li>grid - This grid</li>
36691 * <li>record - The record being edited</li>
36692 * <li>field - The field name being edited</li>
36693 * <li>value - The value for the field being edited.</li>
36694 * <li>row - The grid row index</li>
36695 * <li>column - The grid column index</li>
36696 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36698 * @param {Object} e An edit event (see above for description)
36700 "beforeedit" : true,
36703 * Fires after a cell is edited. <br />
36704 * <ul style="padding:5px;padding-left:16px;">
36705 * <li>grid - This grid</li>
36706 * <li>record - The record being edited</li>
36707 * <li>field - The field name being edited</li>
36708 * <li>value - The value being set</li>
36709 * <li>originalValue - The original value for the field, before the edit.</li>
36710 * <li>row - The grid row index</li>
36711 * <li>column - The grid column index</li>
36713 * @param {Object} e An edit event (see above for description)
36715 "afteredit" : true,
36717 * @event validateedit
36718 * Fires after a cell is edited, but before the value is set in the record.
36719 * You can use this to modify the value being set in the field, Return false
36720 * to cancel the change. The edit event object has the following properties <br />
36721 * <ul style="padding:5px;padding-left:16px;">
36722 * <li>editor - This editor</li>
36723 * <li>grid - This grid</li>
36724 * <li>record - The record being edited</li>
36725 * <li>field - The field name being edited</li>
36726 * <li>value - The value being set</li>
36727 * <li>originalValue - The original value for the field, before the edit.</li>
36728 * <li>row - The grid row index</li>
36729 * <li>column - The grid column index</li>
36730 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36732 * @param {Object} e An edit event (see above for description)
36734 "validateedit" : true
36736 this.on("bodyscroll", this.stopEditing, this);
36737 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36740 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36742 * @cfg {Number} clicksToEdit
36743 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36750 trackMouseOver: false, // causes very odd FF errors
36752 onCellDblClick : function(g, row, col){
36753 this.startEditing(row, col);
36756 onEditComplete : function(ed, value, startValue){
36757 this.editing = false;
36758 this.activeEditor = null;
36759 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36761 var field = this.colModel.getDataIndex(ed.col);
36766 originalValue: startValue,
36773 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36776 if(String(value) !== String(startValue)){
36778 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36779 r.set(field, e.value);
36780 // if we are dealing with a combo box..
36781 // then we also set the 'name' colum to be the displayField
36782 if (ed.field.displayField && ed.field.name) {
36783 r.set(ed.field.name, ed.field.el.dom.value);
36786 delete e.cancel; //?? why!!!
36787 this.fireEvent("afteredit", e);
36790 this.fireEvent("afteredit", e); // always fire it!
36792 this.view.focusCell(ed.row, ed.col);
36796 * Starts editing the specified for the specified row/column
36797 * @param {Number} rowIndex
36798 * @param {Number} colIndex
36800 startEditing : function(row, col){
36801 this.stopEditing();
36802 if(this.colModel.isCellEditable(col, row)){
36803 this.view.ensureVisible(row, col, true);
36805 var r = this.dataSource.getAt(row);
36806 var field = this.colModel.getDataIndex(col);
36807 var cell = Roo.get(this.view.getCell(row,col));
36812 value: r.data[field],
36817 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36818 this.editing = true;
36819 var ed = this.colModel.getCellEditor(col, row);
36825 ed.render(ed.parentEl || document.body);
36831 (function(){ // complex but required for focus issues in safari, ie and opera
36835 ed.on("complete", this.onEditComplete, this, {single: true});
36836 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36837 this.activeEditor = ed;
36838 var v = r.data[field];
36839 ed.startEdit(this.view.getCell(row, col), v);
36840 // combo's with 'displayField and name set
36841 if (ed.field.displayField && ed.field.name) {
36842 ed.field.el.dom.value = r.data[ed.field.name];
36846 }).defer(50, this);
36852 * Stops any active editing
36854 stopEditing : function(){
36855 if(this.activeEditor){
36856 this.activeEditor.completeEdit();
36858 this.activeEditor = null;
36862 * Called to get grid's drag proxy text, by default returns this.ddText.
36865 getDragDropText : function(){
36866 var count = this.selModel.getSelectedCell() ? 1 : 0;
36867 return String.format(this.ddText, count, count == 1 ? '' : 's');
36872 * Ext JS Library 1.1.1
36873 * Copyright(c) 2006-2007, Ext JS, LLC.
36875 * Originally Released Under LGPL - original licence link has changed is not relivant.
36878 * <script type="text/javascript">
36881 // private - not really -- you end up using it !
36882 // This is a support class used internally by the Grid components
36885 * @class Roo.grid.GridEditor
36886 * @extends Roo.Editor
36887 * Class for creating and editable grid elements.
36888 * @param {Object} config any settings (must include field)
36890 Roo.grid.GridEditor = function(field, config){
36891 if (!config && field.field) {
36893 field = Roo.factory(config.field, Roo.form);
36895 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36896 field.monitorTab = false;
36899 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36902 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36905 alignment: "tl-tl",
36908 cls: "x-small-editor x-grid-editor",
36913 * Ext JS Library 1.1.1
36914 * Copyright(c) 2006-2007, Ext JS, LLC.
36916 * Originally Released Under LGPL - original licence link has changed is not relivant.
36919 * <script type="text/javascript">
36924 Roo.grid.PropertyRecord = Roo.data.Record.create([
36925 {name:'name',type:'string'}, 'value'
36929 Roo.grid.PropertyStore = function(grid, source){
36931 this.store = new Roo.data.Store({
36932 recordType : Roo.grid.PropertyRecord
36934 this.store.on('update', this.onUpdate, this);
36936 this.setSource(source);
36938 Roo.grid.PropertyStore.superclass.constructor.call(this);
36943 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36944 setSource : function(o){
36946 this.store.removeAll();
36949 if(this.isEditableValue(o[k])){
36950 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36953 this.store.loadRecords({records: data}, {}, true);
36956 onUpdate : function(ds, record, type){
36957 if(type == Roo.data.Record.EDIT){
36958 var v = record.data['value'];
36959 var oldValue = record.modified['value'];
36960 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36961 this.source[record.id] = v;
36963 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36970 getProperty : function(row){
36971 return this.store.getAt(row);
36974 isEditableValue: function(val){
36975 if(val && val instanceof Date){
36977 }else if(typeof val == 'object' || typeof val == 'function'){
36983 setValue : function(prop, value){
36984 this.source[prop] = value;
36985 this.store.getById(prop).set('value', value);
36988 getSource : function(){
36989 return this.source;
36993 Roo.grid.PropertyColumnModel = function(grid, store){
36996 g.PropertyColumnModel.superclass.constructor.call(this, [
36997 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36998 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37000 this.store = store;
37001 this.bselect = Roo.DomHelper.append(document.body, {
37002 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37003 {tag: 'option', value: 'true', html: 'true'},
37004 {tag: 'option', value: 'false', html: 'false'}
37007 Roo.id(this.bselect);
37010 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37011 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37012 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37013 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37014 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37016 this.renderCellDelegate = this.renderCell.createDelegate(this);
37017 this.renderPropDelegate = this.renderProp.createDelegate(this);
37020 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37024 valueText : 'Value',
37026 dateFormat : 'm/j/Y',
37029 renderDate : function(dateVal){
37030 return dateVal.dateFormat(this.dateFormat);
37033 renderBool : function(bVal){
37034 return bVal ? 'true' : 'false';
37037 isCellEditable : function(colIndex, rowIndex){
37038 return colIndex == 1;
37041 getRenderer : function(col){
37043 this.renderCellDelegate : this.renderPropDelegate;
37046 renderProp : function(v){
37047 return this.getPropertyName(v);
37050 renderCell : function(val){
37052 if(val instanceof Date){
37053 rv = this.renderDate(val);
37054 }else if(typeof val == 'boolean'){
37055 rv = this.renderBool(val);
37057 return Roo.util.Format.htmlEncode(rv);
37060 getPropertyName : function(name){
37061 var pn = this.grid.propertyNames;
37062 return pn && pn[name] ? pn[name] : name;
37065 getCellEditor : function(colIndex, rowIndex){
37066 var p = this.store.getProperty(rowIndex);
37067 var n = p.data['name'], val = p.data['value'];
37069 if(typeof(this.grid.customEditors[n]) == 'string'){
37070 return this.editors[this.grid.customEditors[n]];
37072 if(typeof(this.grid.customEditors[n]) != 'undefined'){
37073 return this.grid.customEditors[n];
37075 if(val instanceof Date){
37076 return this.editors['date'];
37077 }else if(typeof val == 'number'){
37078 return this.editors['number'];
37079 }else if(typeof val == 'boolean'){
37080 return this.editors['boolean'];
37082 return this.editors['string'];
37088 * @class Roo.grid.PropertyGrid
37089 * @extends Roo.grid.EditorGrid
37090 * This class represents the interface of a component based property grid control.
37091 * <br><br>Usage:<pre><code>
37092 var grid = new Roo.grid.PropertyGrid("my-container-id", {
37100 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37101 * The container MUST have some type of size defined for the grid to fill. The container will be
37102 * automatically set to position relative if it isn't already.
37103 * @param {Object} config A config object that sets properties on this grid.
37105 Roo.grid.PropertyGrid = function(container, config){
37106 config = config || {};
37107 var store = new Roo.grid.PropertyStore(this);
37108 this.store = store;
37109 var cm = new Roo.grid.PropertyColumnModel(this, store);
37110 store.store.sort('name', 'ASC');
37111 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37114 enableColLock:false,
37115 enableColumnMove:false,
37117 trackMouseOver: false,
37120 this.getGridEl().addClass('x-props-grid');
37121 this.lastEditRow = null;
37122 this.on('columnresize', this.onColumnResize, this);
37125 * @event beforepropertychange
37126 * Fires before a property changes (return false to stop?)
37127 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37128 * @param {String} id Record Id
37129 * @param {String} newval New Value
37130 * @param {String} oldval Old Value
37132 "beforepropertychange": true,
37134 * @event propertychange
37135 * Fires after a property changes
37136 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37137 * @param {String} id Record Id
37138 * @param {String} newval New Value
37139 * @param {String} oldval Old Value
37141 "propertychange": true
37143 this.customEditors = this.customEditors || {};
37145 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37148 * @cfg {Object} customEditors map of colnames=> custom editors.
37149 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37150 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37151 * false disables editing of the field.
37155 * @cfg {Object} propertyNames map of property Names to their displayed value
37158 render : function(){
37159 Roo.grid.PropertyGrid.superclass.render.call(this);
37160 this.autoSize.defer(100, this);
37163 autoSize : function(){
37164 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37166 this.view.fitColumns();
37170 onColumnResize : function(){
37171 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37175 * Sets the data for the Grid
37176 * accepts a Key => Value object of all the elements avaiable.
37177 * @param {Object} data to appear in grid.
37179 setSource : function(source){
37180 this.store.setSource(source);
37184 * Gets all the data from the grid.
37185 * @return {Object} data data stored in grid
37187 getSource : function(){
37188 return this.store.getSource();
37197 * @class Roo.grid.Calendar
37198 * @extends Roo.util.Grid
37199 * This class extends the Grid to provide a calendar widget
37200 * <br><br>Usage:<pre><code>
37201 var grid = new Roo.grid.Calendar("my-container-id", {
37204 selModel: mySelectionModel,
37205 autoSizeColumns: true,
37206 monitorWindowResize: false,
37207 trackMouseOver: true
37208 eventstore : real data store..
37214 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37215 * The container MUST have some type of size defined for the grid to fill. The container will be
37216 * automatically set to position relative if it isn't already.
37217 * @param {Object} config A config object that sets properties on this grid.
37219 Roo.grid.Calendar = function(container, config){
37220 // initialize the container
37221 this.container = Roo.get(container);
37222 this.container.update("");
37223 this.container.setStyle("overflow", "hidden");
37224 this.container.addClass('x-grid-container');
37226 this.id = this.container.id;
37228 Roo.apply(this, config);
37229 // check and correct shorthanded configs
37233 for (var r = 0;r < 6;r++) {
37236 for (var c =0;c < 7;c++) {
37240 if (this.eventStore) {
37241 this.eventStore= Roo.factory(this.eventStore, Roo.data);
37242 this.eventStore.on('load',this.onLoad, this);
37243 this.eventStore.on('beforeload',this.clearEvents, this);
37247 this.dataSource = new Roo.data.Store({
37248 proxy: new Roo.data.MemoryProxy(rows),
37249 reader: new Roo.data.ArrayReader({}, [
37250 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37253 this.dataSource.load();
37254 this.ds = this.dataSource;
37255 this.ds.xmodule = this.xmodule || false;
37258 var cellRender = function(v,x,r)
37260 return String.format(
37261 '<div class="fc-day fc-widget-content"><div>' +
37262 '<div class="fc-event-container"></div>' +
37263 '<div class="fc-day-number">{0}</div>'+
37265 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37266 '</div></div>', v);
37271 this.colModel = new Roo.grid.ColumnModel( [
37273 xtype: 'ColumnModel',
37275 dataIndex : 'weekday0',
37277 renderer : cellRender
37280 xtype: 'ColumnModel',
37282 dataIndex : 'weekday1',
37284 renderer : cellRender
37287 xtype: 'ColumnModel',
37289 dataIndex : 'weekday2',
37290 header : 'Tuesday',
37291 renderer : cellRender
37294 xtype: 'ColumnModel',
37296 dataIndex : 'weekday3',
37297 header : 'Wednesday',
37298 renderer : cellRender
37301 xtype: 'ColumnModel',
37303 dataIndex : 'weekday4',
37304 header : 'Thursday',
37305 renderer : cellRender
37308 xtype: 'ColumnModel',
37310 dataIndex : 'weekday5',
37312 renderer : cellRender
37315 xtype: 'ColumnModel',
37317 dataIndex : 'weekday6',
37318 header : 'Saturday',
37319 renderer : cellRender
37322 this.cm = this.colModel;
37323 this.cm.xmodule = this.xmodule || false;
37327 //this.selModel = new Roo.grid.CellSelectionModel();
37328 //this.sm = this.selModel;
37329 //this.selModel.init(this);
37333 this.container.setWidth(this.width);
37337 this.container.setHeight(this.height);
37344 * The raw click event for the entire grid.
37345 * @param {Roo.EventObject} e
37350 * The raw dblclick event for the entire grid.
37351 * @param {Roo.EventObject} e
37355 * @event contextmenu
37356 * The raw contextmenu event for the entire grid.
37357 * @param {Roo.EventObject} e
37359 "contextmenu" : true,
37362 * The raw mousedown event for the entire grid.
37363 * @param {Roo.EventObject} e
37365 "mousedown" : true,
37368 * The raw mouseup event for the entire grid.
37369 * @param {Roo.EventObject} e
37374 * The raw mouseover event for the entire grid.
37375 * @param {Roo.EventObject} e
37377 "mouseover" : true,
37380 * The raw mouseout event for the entire grid.
37381 * @param {Roo.EventObject} e
37386 * The raw keypress event for the entire grid.
37387 * @param {Roo.EventObject} e
37392 * The raw keydown event for the entire grid.
37393 * @param {Roo.EventObject} e
37401 * Fires when a cell is clicked
37402 * @param {Grid} this
37403 * @param {Number} rowIndex
37404 * @param {Number} columnIndex
37405 * @param {Roo.EventObject} e
37407 "cellclick" : true,
37409 * @event celldblclick
37410 * Fires when a cell is double clicked
37411 * @param {Grid} this
37412 * @param {Number} rowIndex
37413 * @param {Number} columnIndex
37414 * @param {Roo.EventObject} e
37416 "celldblclick" : true,
37419 * Fires when a row is clicked
37420 * @param {Grid} this
37421 * @param {Number} rowIndex
37422 * @param {Roo.EventObject} e
37426 * @event rowdblclick
37427 * Fires when a row is double clicked
37428 * @param {Grid} this
37429 * @param {Number} rowIndex
37430 * @param {Roo.EventObject} e
37432 "rowdblclick" : true,
37434 * @event headerclick
37435 * Fires when a header is clicked
37436 * @param {Grid} this
37437 * @param {Number} columnIndex
37438 * @param {Roo.EventObject} e
37440 "headerclick" : true,
37442 * @event headerdblclick
37443 * Fires when a header cell is double clicked
37444 * @param {Grid} this
37445 * @param {Number} columnIndex
37446 * @param {Roo.EventObject} e
37448 "headerdblclick" : true,
37450 * @event rowcontextmenu
37451 * Fires when a row is right clicked
37452 * @param {Grid} this
37453 * @param {Number} rowIndex
37454 * @param {Roo.EventObject} e
37456 "rowcontextmenu" : true,
37458 * @event cellcontextmenu
37459 * Fires when a cell is right clicked
37460 * @param {Grid} this
37461 * @param {Number} rowIndex
37462 * @param {Number} cellIndex
37463 * @param {Roo.EventObject} e
37465 "cellcontextmenu" : true,
37467 * @event headercontextmenu
37468 * Fires when a header is right clicked
37469 * @param {Grid} this
37470 * @param {Number} columnIndex
37471 * @param {Roo.EventObject} e
37473 "headercontextmenu" : true,
37475 * @event bodyscroll
37476 * Fires when the body element is scrolled
37477 * @param {Number} scrollLeft
37478 * @param {Number} scrollTop
37480 "bodyscroll" : true,
37482 * @event columnresize
37483 * Fires when the user resizes a column
37484 * @param {Number} columnIndex
37485 * @param {Number} newSize
37487 "columnresize" : true,
37489 * @event columnmove
37490 * Fires when the user moves a column
37491 * @param {Number} oldIndex
37492 * @param {Number} newIndex
37494 "columnmove" : true,
37497 * Fires when row(s) start being dragged
37498 * @param {Grid} this
37499 * @param {Roo.GridDD} dd The drag drop object
37500 * @param {event} e The raw browser event
37502 "startdrag" : true,
37505 * Fires when a drag operation is complete
37506 * @param {Grid} this
37507 * @param {Roo.GridDD} dd The drag drop object
37508 * @param {event} e The raw browser event
37513 * Fires when dragged row(s) are dropped on a valid DD target
37514 * @param {Grid} this
37515 * @param {Roo.GridDD} dd The drag drop object
37516 * @param {String} targetId The target drag drop object
37517 * @param {event} e The raw browser event
37522 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37523 * @param {Grid} this
37524 * @param {Roo.GridDD} dd The drag drop object
37525 * @param {String} targetId The target drag drop object
37526 * @param {event} e The raw browser event
37531 * Fires when the dragged row(s) first cross another DD target while being dragged
37532 * @param {Grid} this
37533 * @param {Roo.GridDD} dd The drag drop object
37534 * @param {String} targetId The target drag drop object
37535 * @param {event} e The raw browser event
37537 "dragenter" : true,
37540 * Fires when the dragged row(s) leave another DD target while being dragged
37541 * @param {Grid} this
37542 * @param {Roo.GridDD} dd The drag drop object
37543 * @param {String} targetId The target drag drop object
37544 * @param {event} e The raw browser event
37549 * Fires when a row is rendered, so you can change add a style to it.
37550 * @param {GridView} gridview The grid view
37551 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37557 * Fires when the grid is rendered
37558 * @param {Grid} grid
37563 * Fires when a date is selected
37564 * @param {DatePicker} this
37565 * @param {Date} date The selected date
37569 * @event monthchange
37570 * Fires when the displayed month changes
37571 * @param {DatePicker} this
37572 * @param {Date} date The selected month
37574 'monthchange': true,
37576 * @event evententer
37577 * Fires when mouse over an event
37578 * @param {Calendar} this
37579 * @param {event} Event
37581 'evententer': true,
37583 * @event eventleave
37584 * Fires when the mouse leaves an
37585 * @param {Calendar} this
37588 'eventleave': true,
37590 * @event eventclick
37591 * Fires when the mouse click an
37592 * @param {Calendar} this
37595 'eventclick': true,
37597 * @event eventrender
37598 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37599 * @param {Calendar} this
37600 * @param {data} data to be modified
37602 'eventrender': true
37606 Roo.grid.Grid.superclass.constructor.call(this);
37607 this.on('render', function() {
37608 this.view.el.addClass('x-grid-cal');
37610 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37614 if (!Roo.grid.Calendar.style) {
37615 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37618 '.x-grid-cal .x-grid-col' : {
37619 height: 'auto !important',
37620 'vertical-align': 'top'
37622 '.x-grid-cal .fc-event-hori' : {
37633 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37635 * @cfg {Store} eventStore The store that loads events.
37640 activeDate : false,
37643 monitorWindowResize : false,
37646 resizeColumns : function() {
37647 var col = (this.view.el.getWidth() / 7) - 3;
37648 // loop through cols, and setWidth
37649 for(var i =0 ; i < 7 ; i++){
37650 this.cm.setColumnWidth(i, col);
37653 setDate :function(date) {
37655 Roo.log('setDate?');
37657 this.resizeColumns();
37658 var vd = this.activeDate;
37659 this.activeDate = date;
37660 // if(vd && this.el){
37661 // var t = date.getTime();
37662 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37663 // Roo.log('using add remove');
37665 // this.fireEvent('monthchange', this, date);
37667 // this.cells.removeClass("fc-state-highlight");
37668 // this.cells.each(function(c){
37669 // if(c.dateValue == t){
37670 // c.addClass("fc-state-highlight");
37671 // setTimeout(function(){
37672 // try{c.dom.firstChild.focus();}catch(e){}
37682 var days = date.getDaysInMonth();
37684 var firstOfMonth = date.getFirstDateOfMonth();
37685 var startingPos = firstOfMonth.getDay()-this.startDay;
37687 if(startingPos < this.startDay){
37691 var pm = date.add(Date.MONTH, -1);
37692 var prevStart = pm.getDaysInMonth()-startingPos;
37696 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37698 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37699 //this.cells.addClassOnOver('fc-state-hover');
37701 var cells = this.cells.elements;
37702 var textEls = this.textNodes;
37704 //Roo.each(cells, function(cell){
37705 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37708 days += startingPos;
37710 // convert everything to numbers so it's fast
37711 var day = 86400000;
37712 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37715 //Roo.log(prevStart);
37717 var today = new Date().clearTime().getTime();
37718 var sel = date.clearTime().getTime();
37719 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37720 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37721 var ddMatch = this.disabledDatesRE;
37722 var ddText = this.disabledDatesText;
37723 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37724 var ddaysText = this.disabledDaysText;
37725 var format = this.format;
37727 var setCellClass = function(cal, cell){
37729 //Roo.log('set Cell Class');
37731 var t = d.getTime();
37736 cell.dateValue = t;
37738 cell.className += " fc-today";
37739 cell.className += " fc-state-highlight";
37740 cell.title = cal.todayText;
37743 // disable highlight in other month..
37744 cell.className += " fc-state-highlight";
37749 //cell.className = " fc-state-disabled";
37750 cell.title = cal.minText;
37754 //cell.className = " fc-state-disabled";
37755 cell.title = cal.maxText;
37759 if(ddays.indexOf(d.getDay()) != -1){
37760 // cell.title = ddaysText;
37761 // cell.className = " fc-state-disabled";
37764 if(ddMatch && format){
37765 var fvalue = d.dateFormat(format);
37766 if(ddMatch.test(fvalue)){
37767 cell.title = ddText.replace("%0", fvalue);
37768 cell.className = " fc-state-disabled";
37772 if (!cell.initialClassName) {
37773 cell.initialClassName = cell.dom.className;
37776 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37781 for(; i < startingPos; i++) {
37782 cells[i].dayName = (++prevStart);
37783 Roo.log(textEls[i]);
37784 d.setDate(d.getDate()+1);
37786 //cells[i].className = "fc-past fc-other-month";
37787 setCellClass(this, cells[i]);
37792 for(; i < days; i++){
37793 intDay = i - startingPos + 1;
37794 cells[i].dayName = (intDay);
37795 d.setDate(d.getDate()+1);
37797 cells[i].className = ''; // "x-date-active";
37798 setCellClass(this, cells[i]);
37802 for(; i < 42; i++) {
37803 //textEls[i].innerHTML = (++extraDays);
37805 d.setDate(d.getDate()+1);
37806 cells[i].dayName = (++extraDays);
37807 cells[i].className = "fc-future fc-other-month";
37808 setCellClass(this, cells[i]);
37811 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37813 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37815 // this will cause all the cells to mis
37818 for (var r = 0;r < 6;r++) {
37819 for (var c =0;c < 7;c++) {
37820 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37824 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37825 for(i=0;i<cells.length;i++) {
37827 this.cells.elements[i].dayName = cells[i].dayName ;
37828 this.cells.elements[i].className = cells[i].className;
37829 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37830 this.cells.elements[i].title = cells[i].title ;
37831 this.cells.elements[i].dateValue = cells[i].dateValue ;
37837 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37838 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37840 ////if(totalRows != 6){
37841 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37842 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37845 this.fireEvent('monthchange', this, date);
37850 * Returns the grid's SelectionModel.
37851 * @return {SelectionModel}
37853 getSelectionModel : function(){
37854 if(!this.selModel){
37855 this.selModel = new Roo.grid.CellSelectionModel();
37857 return this.selModel;
37861 this.eventStore.load()
37867 findCell : function(dt) {
37868 dt = dt.clearTime().getTime();
37870 this.cells.each(function(c){
37871 //Roo.log("check " +c.dateValue + '?=' + dt);
37872 if(c.dateValue == dt){
37882 findCells : function(rec) {
37883 var s = rec.data.start_dt.clone().clearTime().getTime();
37885 var e= rec.data.end_dt.clone().clearTime().getTime();
37888 this.cells.each(function(c){
37889 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37891 if(c.dateValue > e){
37894 if(c.dateValue < s){
37903 findBestRow: function(cells)
37907 for (var i =0 ; i < cells.length;i++) {
37908 ret = Math.max(cells[i].rows || 0,ret);
37915 addItem : function(rec)
37917 // look for vertical location slot in
37918 var cells = this.findCells(rec);
37920 rec.row = this.findBestRow(cells);
37922 // work out the location.
37926 for(var i =0; i < cells.length; i++) {
37934 if (crow.start.getY() == cells[i].getY()) {
37936 crow.end = cells[i];
37952 for (var i = 0; i < cells.length;i++) {
37953 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37960 clearEvents: function() {
37962 if (!this.eventStore.getCount()) {
37965 // reset number of rows in cells.
37966 Roo.each(this.cells.elements, function(c){
37970 this.eventStore.each(function(e) {
37971 this.clearEvent(e);
37976 clearEvent : function(ev)
37979 Roo.each(ev.els, function(el) {
37980 el.un('mouseenter' ,this.onEventEnter, this);
37981 el.un('mouseleave' ,this.onEventLeave, this);
37989 renderEvent : function(ev,ctr) {
37991 ctr = this.view.el.select('.fc-event-container',true).first();
37995 this.clearEvent(ev);
38001 var cells = ev.cells;
38002 var rows = ev.rows;
38003 this.fireEvent('eventrender', this, ev);
38005 for(var i =0; i < rows.length; i++) {
38009 cls += ' fc-event-start';
38011 if ((i+1) == rows.length) {
38012 cls += ' fc-event-end';
38015 //Roo.log(ev.data);
38016 // how many rows should it span..
38017 var cg = this.eventTmpl.append(ctr,Roo.apply({
38020 }, ev.data) , true);
38023 cg.on('mouseenter' ,this.onEventEnter, this, ev);
38024 cg.on('mouseleave' ,this.onEventLeave, this, ev);
38025 cg.on('click', this.onEventClick, this, ev);
38029 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38030 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38033 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
38034 cg.setWidth(ebox.right - sbox.x -2);
38038 renderEvents: function()
38040 // first make sure there is enough space..
38042 if (!this.eventTmpl) {
38043 this.eventTmpl = new Roo.Template(
38044 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
38045 '<div class="fc-event-inner">' +
38046 '<span class="fc-event-time">{time}</span>' +
38047 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38049 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
38057 this.cells.each(function(c) {
38058 //Roo.log(c.select('.fc-day-content div',true).first());
38059 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38062 var ctr = this.view.el.select('.fc-event-container',true).first();
38065 this.eventStore.each(function(ev){
38067 this.renderEvent(ev);
38071 this.view.layout();
38075 onEventEnter: function (e, el,event,d) {
38076 this.fireEvent('evententer', this, el, event);
38079 onEventLeave: function (e, el,event,d) {
38080 this.fireEvent('eventleave', this, el, event);
38083 onEventClick: function (e, el,event,d) {
38084 this.fireEvent('eventclick', this, el, event);
38087 onMonthChange: function () {
38091 onLoad: function () {
38093 //Roo.log('calendar onload');
38095 if(this.eventStore.getCount() > 0){
38099 this.eventStore.each(function(d){
38104 if (typeof(add.end_dt) == 'undefined') {
38105 Roo.log("Missing End time in calendar data: ");
38109 if (typeof(add.start_dt) == 'undefined') {
38110 Roo.log("Missing Start time in calendar data: ");
38114 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38115 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38116 add.id = add.id || d.id;
38117 add.title = add.title || '??';
38125 this.renderEvents();
38135 render : function ()
38139 if (!this.view.el.hasClass('course-timesheet')) {
38140 this.view.el.addClass('course-timesheet');
38142 if (this.tsStyle) {
38147 Roo.log(_this.grid.view.el.getWidth());
38150 this.tsStyle = Roo.util.CSS.createStyleSheet({
38151 '.course-timesheet .x-grid-row' : {
38154 '.x-grid-row td' : {
38155 'vertical-align' : 0
38157 '.course-edit-link' : {
38159 'text-overflow' : 'ellipsis',
38160 'overflow' : 'hidden',
38161 'white-space' : 'nowrap',
38162 'cursor' : 'pointer'
38167 '.de-act-sup-link' : {
38168 'color' : 'purple',
38169 'text-decoration' : 'line-through'
38173 'text-decoration' : 'line-through'
38175 '.course-timesheet .course-highlight' : {
38176 'border-top-style': 'dashed !important',
38177 'border-bottom-bottom': 'dashed !important'
38179 '.course-timesheet .course-item' : {
38180 'font-family' : 'tahoma, arial, helvetica',
38181 'font-size' : '11px',
38182 'overflow' : 'hidden',
38183 'padding-left' : '10px',
38184 'padding-right' : '10px',
38185 'padding-top' : '10px'
38193 monitorWindowResize : false,
38194 cellrenderer : function(v,x,r)
38199 xtype: 'CellSelectionModel',
38206 beforeload : function (_self, options)
38208 options.params = options.params || {};
38209 options.params._month = _this.monthField.getValue();
38210 options.params.limit = 9999;
38211 options.params['sort'] = 'when_dt';
38212 options.params['dir'] = 'ASC';
38213 this.proxy.loadResponse = this.loadResponse;
38215 //this.addColumns();
38217 load : function (_self, records, options)
38219 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38220 // if you click on the translation.. you can edit it...
38221 var el = Roo.get(this);
38222 var id = el.dom.getAttribute('data-id');
38223 var d = el.dom.getAttribute('data-date');
38224 var t = el.dom.getAttribute('data-time');
38225 //var id = this.child('span').dom.textContent;
38228 Pman.Dialog.CourseCalendar.show({
38232 productitem_active : id ? 1 : 0
38234 _this.grid.ds.load({});
38239 _this.panel.fireEvent('resize', [ '', '' ]);
38242 loadResponse : function(o, success, response){
38243 // this is overridden on before load..
38245 Roo.log("our code?");
38246 //Roo.log(success);
38247 //Roo.log(response)
38248 delete this.activeRequest;
38250 this.fireEvent("loadexception", this, o, response);
38251 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38256 result = o.reader.read(response);
38258 Roo.log("load exception?");
38259 this.fireEvent("loadexception", this, o, response, e);
38260 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38263 Roo.log("ready...");
38264 // loop through result.records;
38265 // and set this.tdate[date] = [] << array of records..
38267 Roo.each(result.records, function(r){
38269 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38270 _this.tdata[r.data.when_dt.format('j')] = [];
38272 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38275 //Roo.log(_this.tdata);
38277 result.records = [];
38278 result.totalRecords = 6;
38280 // let's generate some duumy records for the rows.
38281 //var st = _this.dateField.getValue();
38283 // work out monday..
38284 //st = st.add(Date.DAY, -1 * st.format('w'));
38286 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38288 var firstOfMonth = date.getFirstDayOfMonth();
38289 var days = date.getDaysInMonth();
38291 var firstAdded = false;
38292 for (var i = 0; i < result.totalRecords ; i++) {
38293 //var d= st.add(Date.DAY, i);
38296 for(var w = 0 ; w < 7 ; w++){
38297 if(!firstAdded && firstOfMonth != w){
38304 var dd = (d > 0 && d < 10) ? "0"+d : d;
38305 row['weekday'+w] = String.format(
38306 '<span style="font-size: 16px;"><b>{0}</b></span>'+
38307 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38309 date.format('Y-m-')+dd
38312 if(typeof(_this.tdata[d]) != 'undefined'){
38313 Roo.each(_this.tdata[d], function(r){
38317 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38318 if(r.parent_id*1>0){
38319 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38322 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38323 deactive = 'de-act-link';
38326 row['weekday'+w] += String.format(
38327 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38329 r.product_id_name, //1
38330 r.when_dt.format('h:ia'), //2
38340 // only do this if something added..
38342 result.records.push(_this.grid.dataSource.reader.newRow(row));
38346 // push it twice. (second one with an hour..
38350 this.fireEvent("load", this, o, o.request.arg);
38351 o.request.callback.call(o.request.scope, result, o.request.arg, true);
38353 sortInfo : {field: 'when_dt', direction : 'ASC' },
38355 xtype: 'HttpProxy',
38358 url : baseURL + '/Roo/Shop_course.php'
38361 xtype: 'JsonReader',
38378 'name': 'parent_id',
38382 'name': 'product_id',
38386 'name': 'productitem_id',
38404 click : function (_self, e)
38406 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38407 sd.setMonth(sd.getMonth()-1);
38408 _this.monthField.setValue(sd.format('Y-m-d'));
38409 _this.grid.ds.load({});
38415 xtype: 'Separator',
38419 xtype: 'MonthField',
38422 render : function (_self)
38424 _this.monthField = _self;
38425 // _this.monthField.set today
38427 select : function (combo, date)
38429 _this.grid.ds.load({});
38432 value : (function() { return new Date(); })()
38435 xtype: 'Separator',
38441 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38451 click : function (_self, e)
38453 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38454 sd.setMonth(sd.getMonth()+1);
38455 _this.monthField.setValue(sd.format('Y-m-d'));
38456 _this.grid.ds.load({});
38469 * Ext JS Library 1.1.1
38470 * Copyright(c) 2006-2007, Ext JS, LLC.
38472 * Originally Released Under LGPL - original licence link has changed is not relivant.
38475 * <script type="text/javascript">
38479 * @class Roo.LoadMask
38480 * A simple utility class for generically masking elements while loading data. If the element being masked has
38481 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38482 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38483 * element's UpdateManager load indicator and will be destroyed after the initial load.
38485 * Create a new LoadMask
38486 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38487 * @param {Object} config The config object
38489 Roo.LoadMask = function(el, config){
38490 this.el = Roo.get(el);
38491 Roo.apply(this, config);
38493 this.store.on('beforeload', this.onBeforeLoad, this);
38494 this.store.on('load', this.onLoad, this);
38495 this.store.on('loadexception', this.onLoadException, this);
38496 this.removeMask = false;
38498 var um = this.el.getUpdateManager();
38499 um.showLoadIndicator = false; // disable the default indicator
38500 um.on('beforeupdate', this.onBeforeLoad, this);
38501 um.on('update', this.onLoad, this);
38502 um.on('failure', this.onLoad, this);
38503 this.removeMask = true;
38507 Roo.LoadMask.prototype = {
38509 * @cfg {Boolean} removeMask
38510 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38511 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38513 removeMask : false,
38515 * @cfg {String} msg
38516 * The text to display in a centered loading message box (defaults to 'Loading...')
38518 msg : 'Loading...',
38520 * @cfg {String} msgCls
38521 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38523 msgCls : 'x-mask-loading',
38526 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38532 * Disables the mask to prevent it from being displayed
38534 disable : function(){
38535 this.disabled = true;
38539 * Enables the mask so that it can be displayed
38541 enable : function(){
38542 this.disabled = false;
38545 onLoadException : function()
38547 Roo.log(arguments);
38549 if (typeof(arguments[3]) != 'undefined') {
38550 Roo.MessageBox.alert("Error loading",arguments[3]);
38554 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38555 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38562 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38565 onLoad : function()
38567 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38571 onBeforeLoad : function(){
38572 if(!this.disabled){
38573 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38578 destroy : function(){
38580 this.store.un('beforeload', this.onBeforeLoad, this);
38581 this.store.un('load', this.onLoad, this);
38582 this.store.un('loadexception', this.onLoadException, this);
38584 var um = this.el.getUpdateManager();
38585 um.un('beforeupdate', this.onBeforeLoad, this);
38586 um.un('update', this.onLoad, this);
38587 um.un('failure', this.onLoad, this);
38592 * Ext JS Library 1.1.1
38593 * Copyright(c) 2006-2007, Ext JS, LLC.
38595 * Originally Released Under LGPL - original licence link has changed is not relivant.
38598 * <script type="text/javascript">
38603 * @class Roo.XTemplate
38604 * @extends Roo.Template
38605 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38607 var t = new Roo.XTemplate(
38608 '<select name="{name}">',
38609 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38613 // then append, applying the master template values
38616 * Supported features:
38621 {a_variable} - output encoded.
38622 {a_variable.format:("Y-m-d")} - call a method on the variable
38623 {a_variable:raw} - unencoded output
38624 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38625 {a_variable:this.method_on_template(...)} - call a method on the template object.
38630 <tpl for="a_variable or condition.."></tpl>
38631 <tpl if="a_variable or condition"></tpl>
38632 <tpl exec="some javascript"></tpl>
38633 <tpl name="named_template"></tpl> (experimental)
38635 <tpl for="."></tpl> - just iterate the property..
38636 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38640 Roo.XTemplate = function()
38642 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38649 Roo.extend(Roo.XTemplate, Roo.Template, {
38652 * The various sub templates
38657 * basic tag replacing syntax
38660 * // you can fake an object call by doing this
38664 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38667 * compile the template
38669 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38672 compile: function()
38676 s = ['<tpl>', s, '</tpl>'].join('');
38678 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38679 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38680 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38681 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38682 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38687 while(true == !!(m = s.match(re))){
38688 var forMatch = m[0].match(nameRe),
38689 ifMatch = m[0].match(ifRe),
38690 execMatch = m[0].match(execRe),
38691 namedMatch = m[0].match(namedRe),
38696 name = forMatch && forMatch[1] ? forMatch[1] : '';
38699 // if - puts fn into test..
38700 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38702 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38707 // exec - calls a function... returns empty if true is returned.
38708 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38710 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38718 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38719 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38720 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38723 var uid = namedMatch ? namedMatch[1] : id;
38727 id: namedMatch ? namedMatch[1] : id,
38734 s = s.replace(m[0], '');
38736 s = s.replace(m[0], '{xtpl'+ id + '}');
38741 for(var i = tpls.length-1; i >= 0; --i){
38742 this.compileTpl(tpls[i]);
38743 this.tpls[tpls[i].id] = tpls[i];
38745 this.master = tpls[tpls.length-1];
38749 * same as applyTemplate, except it's done to one of the subTemplates
38750 * when using named templates, you can do:
38752 * var str = pl.applySubTemplate('your-name', values);
38755 * @param {Number} id of the template
38756 * @param {Object} values to apply to template
38757 * @param {Object} parent (normaly the instance of this object)
38759 applySubTemplate : function(id, values, parent)
38763 var t = this.tpls[id];
38767 if(t.test && !t.test.call(this, values, parent)){
38771 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38772 Roo.log(e.toString());
38778 if(t.exec && t.exec.call(this, values, parent)){
38782 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38783 Roo.log(e.toString());
38788 var vs = t.target ? t.target.call(this, values, parent) : values;
38789 parent = t.target ? values : parent;
38790 if(t.target && vs instanceof Array){
38792 for(var i = 0, len = vs.length; i < len; i++){
38793 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38795 return buf.join('');
38797 return t.compiled.call(this, vs, parent);
38799 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38800 Roo.log(e.toString());
38801 Roo.log(t.compiled);
38806 compileTpl : function(tpl)
38808 var fm = Roo.util.Format;
38809 var useF = this.disableFormats !== true;
38810 var sep = Roo.isGecko ? "+" : ",";
38811 var undef = function(str) {
38812 Roo.log("Property not found :" + str);
38816 var fn = function(m, name, format, args)
38818 //Roo.log(arguments);
38819 args = args ? args.replace(/\\'/g,"'") : args;
38820 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38821 if (typeof(format) == 'undefined') {
38822 format= 'htmlEncode';
38824 if (format == 'raw' ) {
38828 if(name.substr(0, 4) == 'xtpl'){
38829 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38832 // build an array of options to determine if value is undefined..
38834 // basically get 'xxxx.yyyy' then do
38835 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38836 // (function () { Roo.log("Property not found"); return ''; })() :
38841 Roo.each(name.split('.'), function(st) {
38842 lookfor += (lookfor.length ? '.': '') + st;
38843 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38846 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38849 if(format && useF){
38851 args = args ? ',' + args : "";
38853 if(format.substr(0, 5) != "this."){
38854 format = "fm." + format + '(';
38856 format = 'this.call("'+ format.substr(5) + '", ';
38860 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38864 // called with xxyx.yuu:(test,test)
38866 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38868 // raw.. - :raw modifier..
38869 return "'"+ sep + udef_st + name + ")"+sep+"'";
38873 // branched to use + in gecko and [].join() in others
38875 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38876 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38879 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38880 body.push(tpl.body.replace(/(\r\n|\n)/g,
38881 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38882 body.push("'].join('');};};");
38883 body = body.join('');
38886 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38888 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38894 applyTemplate : function(values){
38895 return this.master.compiled.call(this, values, {});
38896 //var s = this.subs;
38899 apply : function(){
38900 return this.applyTemplate.apply(this, arguments);
38905 Roo.XTemplate.from = function(el){
38906 el = Roo.getDom(el);
38907 return new Roo.XTemplate(el.value || el.innerHTML);