roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
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
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
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>
118  * <p>
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
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
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.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
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>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</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>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
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'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
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';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
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.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
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.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
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.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
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.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
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
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
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>
353  * <p>
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>
356  * <p>
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.
359  * @constructor
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.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
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;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
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
408          */
409         datachanged : true,
410         /**
411          * @event metachange
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
415          */
416         metachange : true,
417         /**
418          * @event add
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
423          */
424         add : true,
425         /**
426          * @event remove
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
431          */
432         remove : true,
433         /**
434          * @event update
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:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
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)
458          */
459         beforeload : true,
460         /**
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)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
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
475          */
476         load : true,
477         /**
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
482          * 
483          * @param {Proxy} 
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)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
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.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader 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.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
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).
537     */
538     remoteSort : false,
539
540     /**
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).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
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.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
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.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
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.
592      */
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);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
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.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
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.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
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.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
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.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
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
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
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.
662      * <p>
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>
666      * <p>
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>
676      * </ul>
677      */
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) {
685                 p._requestMeta = 1;
686             }
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;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
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).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
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);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
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);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
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.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Object} an existing reader (eg. copied from another store)
1092  * @cfg {Array} data The multi-dimensional array of data
1093  * @constructor
1094  * @param {Object} config
1095  */
1096 Roo.data.SimpleStore = function(config)
1097 {
1098     Roo.data.SimpleStore.superclass.constructor.call(this, {
1099         isLocal : true,
1100         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1101                 id: config.id
1102             },
1103             Roo.data.Record.create(config.fields)
1104         ),
1105         proxy : new Roo.data.MemoryProxy(config.data)
1106     });
1107     this.load();
1108 };
1109 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1110  * Based on:
1111  * Ext JS Library 1.1.1
1112  * Copyright(c) 2006-2007, Ext JS, LLC.
1113  *
1114  * Originally Released Under LGPL - original licence link has changed is not relivant.
1115  *
1116  * Fork - LGPL
1117  * <script type="text/javascript">
1118  */
1119
1120 /**
1121 /**
1122  * @extends Roo.data.Store
1123  * @class Roo.data.JsonStore
1124  * Small helper class to make creating Stores for JSON data easier. <br/>
1125 <pre><code>
1126 var store = new Roo.data.JsonStore({
1127     url: 'get-images.php',
1128     root: 'images',
1129     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1130 });
1131 </code></pre>
1132  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1133  * JsonReader and HttpProxy (unless inline data is provided).</b>
1134  * @cfg {Array} fields An array of field definition objects, or field name strings.
1135  * @constructor
1136  * @param {Object} config
1137  */
1138 Roo.data.JsonStore = function(c){
1139     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1140         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1141         reader: new Roo.data.JsonReader(c, c.fields)
1142     }));
1143 };
1144 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1145  * Based on:
1146  * Ext JS Library 1.1.1
1147  * Copyright(c) 2006-2007, Ext JS, LLC.
1148  *
1149  * Originally Released Under LGPL - original licence link has changed is not relivant.
1150  *
1151  * Fork - LGPL
1152  * <script type="text/javascript">
1153  */
1154
1155  
1156 Roo.data.Field = function(config){
1157     if(typeof config == "string"){
1158         config = {name: config};
1159     }
1160     Roo.apply(this, config);
1161     
1162     if(!this.type){
1163         this.type = "auto";
1164     }
1165     
1166     var st = Roo.data.SortTypes;
1167     // named sortTypes are supported, here we look them up
1168     if(typeof this.sortType == "string"){
1169         this.sortType = st[this.sortType];
1170     }
1171     
1172     // set default sortType for strings and dates
1173     if(!this.sortType){
1174         switch(this.type){
1175             case "string":
1176                 this.sortType = st.asUCString;
1177                 break;
1178             case "date":
1179                 this.sortType = st.asDate;
1180                 break;
1181             default:
1182                 this.sortType = st.none;
1183         }
1184     }
1185
1186     // define once
1187     var stripRe = /[\$,%]/g;
1188
1189     // prebuilt conversion function for this field, instead of
1190     // switching every time we're reading a value
1191     if(!this.convert){
1192         var cv, dateFormat = this.dateFormat;
1193         switch(this.type){
1194             case "":
1195             case "auto":
1196             case undefined:
1197                 cv = function(v){ return v; };
1198                 break;
1199             case "string":
1200                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1201                 break;
1202             case "int":
1203                 cv = function(v){
1204                     return v !== undefined && v !== null && v !== '' ?
1205                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1206                     };
1207                 break;
1208             case "float":
1209                 cv = function(v){
1210                     return v !== undefined && v !== null && v !== '' ?
1211                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1212                     };
1213                 break;
1214             case "bool":
1215             case "boolean":
1216                 cv = function(v){ return v === true || v === "true" || v == 1; };
1217                 break;
1218             case "date":
1219                 cv = function(v){
1220                     if(!v){
1221                         return '';
1222                     }
1223                     if(v instanceof Date){
1224                         return v;
1225                     }
1226                     if(dateFormat){
1227                         if(dateFormat == "timestamp"){
1228                             return new Date(v*1000);
1229                         }
1230                         return Date.parseDate(v, dateFormat);
1231                     }
1232                     var parsed = Date.parse(v);
1233                     return parsed ? new Date(parsed) : null;
1234                 };
1235              break;
1236             
1237         }
1238         this.convert = cv;
1239     }
1240 };
1241
1242 Roo.data.Field.prototype = {
1243     dateFormat: null,
1244     defaultValue: "",
1245     mapping: null,
1246     sortType : null,
1247     sortDir : "ASC"
1248 };/*
1249  * Based on:
1250  * Ext JS Library 1.1.1
1251  * Copyright(c) 2006-2007, Ext JS, LLC.
1252  *
1253  * Originally Released Under LGPL - original licence link has changed is not relivant.
1254  *
1255  * Fork - LGPL
1256  * <script type="text/javascript">
1257  */
1258  
1259 // Base class for reading structured data from a data source.  This class is intended to be
1260 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1261
1262 /**
1263  * @class Roo.data.DataReader
1264  * Base class for reading structured data from a data source.  This class is intended to be
1265  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1266  */
1267
1268 Roo.data.DataReader = function(meta, recordType){
1269     
1270     this.meta = meta;
1271     
1272     this.recordType = recordType instanceof Array ? 
1273         Roo.data.Record.create(recordType) : recordType;
1274 };
1275
1276 Roo.data.DataReader.prototype = {
1277     
1278     
1279     readerType : 'Data',
1280      /**
1281      * Create an empty record
1282      * @param {Object} data (optional) - overlay some values
1283      * @return {Roo.data.Record} record created.
1284      */
1285     newRow :  function(d) {
1286         var da =  {};
1287         this.recordType.prototype.fields.each(function(c) {
1288             switch( c.type) {
1289                 case 'int' : da[c.name] = 0; break;
1290                 case 'date' : da[c.name] = new Date(); break;
1291                 case 'float' : da[c.name] = 0.0; break;
1292                 case 'boolean' : da[c.name] = false; break;
1293                 default : da[c.name] = ""; break;
1294             }
1295             
1296         });
1297         return new this.recordType(Roo.apply(da, d));
1298     }
1299     
1300     
1301 };/*
1302  * Based on:
1303  * Ext JS Library 1.1.1
1304  * Copyright(c) 2006-2007, Ext JS, LLC.
1305  *
1306  * Originally Released Under LGPL - original licence link has changed is not relivant.
1307  *
1308  * Fork - LGPL
1309  * <script type="text/javascript">
1310  */
1311
1312 /**
1313  * @class Roo.data.DataProxy
1314  * @extends Roo.data.Observable
1315  * This class is an abstract base class for implementations which provide retrieval of
1316  * unformatted data objects.<br>
1317  * <p>
1318  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1319  * (of the appropriate type which knows how to parse the data object) to provide a block of
1320  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1321  * <p>
1322  * Custom implementations must implement the load method as described in
1323  * {@link Roo.data.HttpProxy#load}.
1324  */
1325 Roo.data.DataProxy = function(){
1326     this.addEvents({
1327         /**
1328          * @event beforeload
1329          * Fires before a network request is made to retrieve a data object.
1330          * @param {Object} This DataProxy object.
1331          * @param {Object} params The params parameter to the load function.
1332          */
1333         beforeload : true,
1334         /**
1335          * @event load
1336          * Fires before the load method's callback is called.
1337          * @param {Object} This DataProxy object.
1338          * @param {Object} o The data object.
1339          * @param {Object} arg The callback argument object passed to the load function.
1340          */
1341         load : true,
1342         /**
1343          * @event loadexception
1344          * Fires if an Exception occurs during data retrieval.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} o The data object.
1347          * @param {Object} arg The callback argument object passed to the load function.
1348          * @param {Object} e The Exception.
1349          */
1350         loadexception : true
1351     });
1352     Roo.data.DataProxy.superclass.constructor.call(this);
1353 };
1354
1355 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1356
1357     /**
1358      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1359      */
1360 /*
1361  * Based on:
1362  * Ext JS Library 1.1.1
1363  * Copyright(c) 2006-2007, Ext JS, LLC.
1364  *
1365  * Originally Released Under LGPL - original licence link has changed is not relivant.
1366  *
1367  * Fork - LGPL
1368  * <script type="text/javascript">
1369  */
1370 /**
1371  * @class Roo.data.MemoryProxy
1372  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1373  * to the Reader when its load method is called.
1374  * @constructor
1375  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1376  */
1377 Roo.data.MemoryProxy = function(data){
1378     if (data.data) {
1379         data = data.data;
1380     }
1381     Roo.data.MemoryProxy.superclass.constructor.call(this);
1382     this.data = data;
1383 };
1384
1385 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1386     
1387     /**
1388      * Load data from the requested source (in this case an in-memory
1389      * data object passed to the constructor), read the data object into
1390      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1391      * process that block using the passed callback.
1392      * @param {Object} params This parameter is not used by the MemoryProxy class.
1393      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1394      * object into a block of Roo.data.Records.
1395      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1396      * The function must be passed <ul>
1397      * <li>The Record block object</li>
1398      * <li>The "arg" argument from the load function</li>
1399      * <li>A boolean success indicator</li>
1400      * </ul>
1401      * @param {Object} scope The scope in which to call the callback
1402      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1403      */
1404     load : function(params, reader, callback, scope, arg){
1405         params = params || {};
1406         var result;
1407         try {
1408             result = reader.readRecords(params.data ? params.data :this.data);
1409         }catch(e){
1410             this.fireEvent("loadexception", this, arg, null, e);
1411             callback.call(scope, null, arg, false);
1412             return;
1413         }
1414         callback.call(scope, result, arg, true);
1415     },
1416     
1417     // private
1418     update : function(params, records){
1419         
1420     }
1421 });/*
1422  * Based on:
1423  * Ext JS Library 1.1.1
1424  * Copyright(c) 2006-2007, Ext JS, LLC.
1425  *
1426  * Originally Released Under LGPL - original licence link has changed is not relivant.
1427  *
1428  * Fork - LGPL
1429  * <script type="text/javascript">
1430  */
1431 /**
1432  * @class Roo.data.HttpProxy
1433  * @extends Roo.data.DataProxy
1434  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1435  * configured to reference a certain URL.<br><br>
1436  * <p>
1437  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1438  * from which the running page was served.<br><br>
1439  * <p>
1440  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1441  * <p>
1442  * Be aware that to enable the browser to parse an XML document, the server must set
1443  * the Content-Type header in the HTTP response to "text/xml".
1444  * @constructor
1445  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1446  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1447  * will be used to make the request.
1448  */
1449 Roo.data.HttpProxy = function(conn){
1450     Roo.data.HttpProxy.superclass.constructor.call(this);
1451     // is conn a conn config or a real conn?
1452     this.conn = conn;
1453     this.useAjax = !conn || !conn.events;
1454   
1455 };
1456
1457 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1458     // thse are take from connection...
1459     
1460     /**
1461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1462      */
1463     /**
1464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1465      * extra parameters to each request made by this object. (defaults to undefined)
1466      */
1467     /**
1468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1469      *  to each request made by this object. (defaults to undefined)
1470      */
1471     /**
1472      * @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)
1473      */
1474     /**
1475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1476      */
1477      /**
1478      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1479      * @type Boolean
1480      */
1481   
1482
1483     /**
1484      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1485      * @type Boolean
1486      */
1487     /**
1488      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1489      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1490      * a finer-grained basis than the DataProxy events.
1491      */
1492     getConnection : function(){
1493         return this.useAjax ? Roo.Ajax : this.conn;
1494     },
1495
1496     /**
1497      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1498      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1499      * process that block using the passed callback.
1500      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1501      * for the request to the remote server.
1502      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1503      * object into a block of Roo.data.Records.
1504      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1505      * The function must be passed <ul>
1506      * <li>The Record block object</li>
1507      * <li>The "arg" argument from the load function</li>
1508      * <li>A boolean success indicator</li>
1509      * </ul>
1510      * @param {Object} scope The scope in which to call the callback
1511      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1512      */
1513     load : function(params, reader, callback, scope, arg){
1514         if(this.fireEvent("beforeload", this, params) !== false){
1515             var  o = {
1516                 params : params || {},
1517                 request: {
1518                     callback : callback,
1519                     scope : scope,
1520                     arg : arg
1521                 },
1522                 reader: reader,
1523                 callback : this.loadResponse,
1524                 scope: this
1525             };
1526             if(this.useAjax){
1527                 Roo.applyIf(o, this.conn);
1528                 if(this.activeRequest){
1529                     Roo.Ajax.abort(this.activeRequest);
1530                 }
1531                 this.activeRequest = Roo.Ajax.request(o);
1532             }else{
1533                 this.conn.request(o);
1534             }
1535         }else{
1536             callback.call(scope||this, null, arg, false);
1537         }
1538     },
1539
1540     // private
1541     loadResponse : function(o, success, response){
1542         delete this.activeRequest;
1543         if(!success){
1544             this.fireEvent("loadexception", this, o, response);
1545             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1546             return;
1547         }
1548         var result;
1549         try {
1550             result = o.reader.read(response);
1551         }catch(e){
1552             this.fireEvent("loadexception", this, o, response, e);
1553             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1554             return;
1555         }
1556         
1557         this.fireEvent("load", this, o, o.request.arg);
1558         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1559     },
1560
1561     // private
1562     update : function(dataSet){
1563
1564     },
1565
1566     // private
1567     updateResponse : function(dataSet){
1568
1569     }
1570 });/*
1571  * Based on:
1572  * Ext JS Library 1.1.1
1573  * Copyright(c) 2006-2007, Ext JS, LLC.
1574  *
1575  * Originally Released Under LGPL - original licence link has changed is not relivant.
1576  *
1577  * Fork - LGPL
1578  * <script type="text/javascript">
1579  */
1580
1581 /**
1582  * @class Roo.data.ScriptTagProxy
1583  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1584  * other than the originating domain of the running page.<br><br>
1585  * <p>
1586  * <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
1587  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1588  * <p>
1589  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1590  * source code that is used as the source inside a &lt;script> tag.<br><br>
1591  * <p>
1592  * In order for the browser to process the returned data, the server must wrap the data object
1593  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1594  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1595  * depending on whether the callback name was passed:
1596  * <p>
1597  * <pre><code>
1598 boolean scriptTag = false;
1599 String cb = request.getParameter("callback");
1600 if (cb != null) {
1601     scriptTag = true;
1602     response.setContentType("text/javascript");
1603 } else {
1604     response.setContentType("application/x-json");
1605 }
1606 Writer out = response.getWriter();
1607 if (scriptTag) {
1608     out.write(cb + "(");
1609 }
1610 out.print(dataBlock.toJsonString());
1611 if (scriptTag) {
1612     out.write(");");
1613 }
1614 </pre></code>
1615  *
1616  * @constructor
1617  * @param {Object} config A configuration object.
1618  */
1619 Roo.data.ScriptTagProxy = function(config){
1620     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1621     Roo.apply(this, config);
1622     this.head = document.getElementsByTagName("head")[0];
1623 };
1624
1625 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1626
1627 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1628     /**
1629      * @cfg {String} url The URL from which to request the data object.
1630      */
1631     /**
1632      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1633      */
1634     timeout : 30000,
1635     /**
1636      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1637      * the server the name of the callback function set up by the load call to process the returned data object.
1638      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1639      * javascript output which calls this named function passing the data object as its only parameter.
1640      */
1641     callbackParam : "callback",
1642     /**
1643      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1644      * name to the request.
1645      */
1646     nocache : true,
1647
1648     /**
1649      * Load data from the configured URL, read the data object into
1650      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1651      * process that block using the passed callback.
1652      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1653      * for the request to the remote server.
1654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1655      * object into a block of Roo.data.Records.
1656      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1657      * The function must be passed <ul>
1658      * <li>The Record block object</li>
1659      * <li>The "arg" argument from the load function</li>
1660      * <li>A boolean success indicator</li>
1661      * </ul>
1662      * @param {Object} scope The scope in which to call the callback
1663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1664      */
1665     load : function(params, reader, callback, scope, arg){
1666         if(this.fireEvent("beforeload", this, params) !== false){
1667
1668             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1669
1670             var url = this.url;
1671             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1672             if(this.nocache){
1673                 url += "&_dc=" + (new Date().getTime());
1674             }
1675             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1676             var trans = {
1677                 id : transId,
1678                 cb : "stcCallback"+transId,
1679                 scriptId : "stcScript"+transId,
1680                 params : params,
1681                 arg : arg,
1682                 url : url,
1683                 callback : callback,
1684                 scope : scope,
1685                 reader : reader
1686             };
1687             var conn = this;
1688
1689             window[trans.cb] = function(o){
1690                 conn.handleResponse(o, trans);
1691             };
1692
1693             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1694
1695             if(this.autoAbort !== false){
1696                 this.abort();
1697             }
1698
1699             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1700
1701             var script = document.createElement("script");
1702             script.setAttribute("src", url);
1703             script.setAttribute("type", "text/javascript");
1704             script.setAttribute("id", trans.scriptId);
1705             this.head.appendChild(script);
1706
1707             this.trans = trans;
1708         }else{
1709             callback.call(scope||this, null, arg, false);
1710         }
1711     },
1712
1713     // private
1714     isLoading : function(){
1715         return this.trans ? true : false;
1716     },
1717
1718     /**
1719      * Abort the current server request.
1720      */
1721     abort : function(){
1722         if(this.isLoading()){
1723             this.destroyTrans(this.trans);
1724         }
1725     },
1726
1727     // private
1728     destroyTrans : function(trans, isLoaded){
1729         this.head.removeChild(document.getElementById(trans.scriptId));
1730         clearTimeout(trans.timeoutId);
1731         if(isLoaded){
1732             window[trans.cb] = undefined;
1733             try{
1734                 delete window[trans.cb];
1735             }catch(e){}
1736         }else{
1737             // if hasn't been loaded, wait for load to remove it to prevent script error
1738             window[trans.cb] = function(){
1739                 window[trans.cb] = undefined;
1740                 try{
1741                     delete window[trans.cb];
1742                 }catch(e){}
1743             };
1744         }
1745     },
1746
1747     // private
1748     handleResponse : function(o, trans){
1749         this.trans = false;
1750         this.destroyTrans(trans, true);
1751         var result;
1752         try {
1753             result = trans.reader.readRecords(o);
1754         }catch(e){
1755             this.fireEvent("loadexception", this, o, trans.arg, e);
1756             trans.callback.call(trans.scope||window, null, trans.arg, false);
1757             return;
1758         }
1759         this.fireEvent("load", this, o, trans.arg);
1760         trans.callback.call(trans.scope||window, result, trans.arg, true);
1761     },
1762
1763     // private
1764     handleFailure : function(trans){
1765         this.trans = false;
1766         this.destroyTrans(trans, false);
1767         this.fireEvent("loadexception", this, null, trans.arg);
1768         trans.callback.call(trans.scope||window, null, trans.arg, false);
1769     }
1770 });/*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780
1781 /**
1782  * @class Roo.data.JsonReader
1783  * @extends Roo.data.DataReader
1784  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1785  * based on mappings in a provided Roo.data.Record constructor.
1786  * 
1787  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1788  * in the reply previously. 
1789  * 
1790  * <p>
1791  * Example code:
1792  * <pre><code>
1793 var RecordDef = Roo.data.Record.create([
1794     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1795     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1796 ]);
1797 var myReader = new Roo.data.JsonReader({
1798     totalProperty: "results",    // The property which contains the total dataset size (optional)
1799     root: "rows",                // The property which contains an Array of row objects
1800     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1801 }, RecordDef);
1802 </code></pre>
1803  * <p>
1804  * This would consume a JSON file like this:
1805  * <pre><code>
1806 { 'results': 2, 'rows': [
1807     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1808     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1809 }
1810 </code></pre>
1811  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1812  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1813  * paged from the remote server.
1814  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1815  * @cfg {String} root name of the property which contains the Array of row objects.
1816  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1817  * @cfg {Array} fields Array of field definition objects
1818  * @constructor
1819  * Create a new JsonReader
1820  * @param {Object} meta Metadata configuration options
1821  * @param {Object} recordType Either an Array of field definition objects,
1822  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1823  */
1824 Roo.data.JsonReader = function(meta, recordType){
1825     
1826     meta = meta || {};
1827     // set some defaults:
1828     Roo.applyIf(meta, {
1829         totalProperty: 'total',
1830         successProperty : 'success',
1831         root : 'data',
1832         id : 'id'
1833     });
1834     
1835     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1836 };
1837 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1838     
1839     readerType : 'Json',
1840     
1841     /**
1842      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1843      * Used by Store query builder to append _requestMeta to params.
1844      * 
1845      */
1846     metaFromRemote : false,
1847     /**
1848      * This method is only used by a DataProxy which has retrieved data from a remote server.
1849      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1850      * @return {Object} data A data block which is used by an Roo.data.Store object as
1851      * a cache of Roo.data.Records.
1852      */
1853     read : function(response){
1854         var json = response.responseText;
1855        
1856         var o = /* eval:var:o */ eval("("+json+")");
1857         if(!o) {
1858             throw {message: "JsonReader.read: Json object not found"};
1859         }
1860         
1861         if(o.metaData){
1862             
1863             delete this.ef;
1864             this.metaFromRemote = true;
1865             this.meta = o.metaData;
1866             this.recordType = Roo.data.Record.create(o.metaData.fields);
1867             this.onMetaChange(this.meta, this.recordType, o);
1868         }
1869         return this.readRecords(o);
1870     },
1871
1872     // private function a store will implement
1873     onMetaChange : function(meta, recordType, o){
1874
1875     },
1876
1877     /**
1878          * @ignore
1879          */
1880     simpleAccess: function(obj, subsc) {
1881         return obj[subsc];
1882     },
1883
1884         /**
1885          * @ignore
1886          */
1887     getJsonAccessor: function(){
1888         var re = /[\[\.]/;
1889         return function(expr) {
1890             try {
1891                 return(re.test(expr))
1892                     ? new Function("obj", "return obj." + expr)
1893                     : function(obj){
1894                         return obj[expr];
1895                     };
1896             } catch(e){}
1897             return Roo.emptyFn;
1898         };
1899     }(),
1900
1901     /**
1902      * Create a data block containing Roo.data.Records from an XML document.
1903      * @param {Object} o An object which contains an Array of row objects in the property specified
1904      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1905      * which contains the total size of the dataset.
1906      * @return {Object} data A data block which is used by an Roo.data.Store object as
1907      * a cache of Roo.data.Records.
1908      */
1909     readRecords : function(o){
1910         /**
1911          * After any data loads, the raw JSON data is available for further custom processing.
1912          * @type Object
1913          */
1914         this.o = o;
1915         var s = this.meta, Record = this.recordType,
1916             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1917
1918 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1919         if (!this.ef) {
1920             if(s.totalProperty) {
1921                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1922                 }
1923                 if(s.successProperty) {
1924                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1925                 }
1926                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1927                 if (s.id) {
1928                         var g = this.getJsonAccessor(s.id);
1929                         this.getId = function(rec) {
1930                                 var r = g(rec);  
1931                                 return (r === undefined || r === "") ? null : r;
1932                         };
1933                 } else {
1934                         this.getId = function(){return null;};
1935                 }
1936             this.ef = [];
1937             for(var jj = 0; jj < fl; jj++){
1938                 f = fi[jj];
1939                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1940                 this.ef[jj] = this.getJsonAccessor(map);
1941             }
1942         }
1943
1944         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1945         if(s.totalProperty){
1946             var vt = parseInt(this.getTotal(o), 10);
1947             if(!isNaN(vt)){
1948                 totalRecords = vt;
1949             }
1950         }
1951         if(s.successProperty){
1952             var vs = this.getSuccess(o);
1953             if(vs === false || vs === 'false'){
1954                 success = false;
1955             }
1956         }
1957         var records = [];
1958         for(var i = 0; i < c; i++){
1959                 var n = root[i];
1960             var values = {};
1961             var id = this.getId(n);
1962             for(var j = 0; j < fl; j++){
1963                 f = fi[j];
1964             var v = this.ef[j](n);
1965             if (!f.convert) {
1966                 Roo.log('missing convert for ' + f.name);
1967                 Roo.log(f);
1968                 continue;
1969             }
1970             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1971             }
1972             var record = new Record(values, id);
1973             record.json = n;
1974             records[i] = record;
1975         }
1976         return {
1977             raw : o,
1978             success : success,
1979             records : records,
1980             totalRecords : totalRecords
1981         };
1982     },
1983     /**
1984      * using 'cn' the nested child reader read the child array into it's child stores.
1985      * @param {Object} rec The record with a 'children array
1986      */
1987     loadDataFromChildren: function(rec)
1988     {
1989         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1990         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1991         return this.loadData({ data : data, total : data.length });
1992         
1993     }
1994 });/*
1995  * Based on:
1996  * Ext JS Library 1.1.1
1997  * Copyright(c) 2006-2007, Ext JS, LLC.
1998  *
1999  * Originally Released Under LGPL - original licence link has changed is not relivant.
2000  *
2001  * Fork - LGPL
2002  * <script type="text/javascript">
2003  */
2004
2005 /**
2006  * @class Roo.data.XmlReader
2007  * @extends Roo.data.DataReader
2008  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2009  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2010  * <p>
2011  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2012  * header in the HTTP response must be set to "text/xml".</em>
2013  * <p>
2014  * Example code:
2015  * <pre><code>
2016 var RecordDef = Roo.data.Record.create([
2017    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2018    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2019 ]);
2020 var myReader = new Roo.data.XmlReader({
2021    totalRecords: "results", // The element which contains the total dataset size (optional)
2022    record: "row",           // The repeated element which contains row information
2023    id: "id"                 // The element within the row that provides an ID for the record (optional)
2024 }, RecordDef);
2025 </code></pre>
2026  * <p>
2027  * This would consume an XML file like this:
2028  * <pre><code>
2029 &lt;?xml?>
2030 &lt;dataset>
2031  &lt;results>2&lt;/results>
2032  &lt;row>
2033    &lt;id>1&lt;/id>
2034    &lt;name>Bill&lt;/name>
2035    &lt;occupation>Gardener&lt;/occupation>
2036  &lt;/row>
2037  &lt;row>
2038    &lt;id>2&lt;/id>
2039    &lt;name>Ben&lt;/name>
2040    &lt;occupation>Horticulturalist&lt;/occupation>
2041  &lt;/row>
2042 &lt;/dataset>
2043 </code></pre>
2044  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2045  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2046  * paged from the remote server.
2047  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2048  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2049  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2050  * a record identifier value.
2051  * @constructor
2052  * Create a new XmlReader
2053  * @param {Object} meta Metadata configuration options
2054  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2055  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2056  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2057  */
2058 Roo.data.XmlReader = function(meta, recordType){
2059     meta = meta || {};
2060     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2061 };
2062 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2063     
2064     readerType : 'Xml',
2065     
2066     /**
2067      * This method is only used by a DataProxy which has retrieved data from a remote server.
2068          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2069          * to contain a method called 'responseXML' that returns an XML document object.
2070      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2071      * a cache of Roo.data.Records.
2072      */
2073     read : function(response){
2074         var doc = response.responseXML;
2075         if(!doc) {
2076             throw {message: "XmlReader.read: XML Document not available"};
2077         }
2078         return this.readRecords(doc);
2079     },
2080
2081     /**
2082      * Create a data block containing Roo.data.Records from an XML document.
2083          * @param {Object} doc A parsed XML document.
2084      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2085      * a cache of Roo.data.Records.
2086      */
2087     readRecords : function(doc){
2088         /**
2089          * After any data loads/reads, the raw XML Document is available for further custom processing.
2090          * @type XMLDocument
2091          */
2092         this.xmlData = doc;
2093         var root = doc.documentElement || doc;
2094         var q = Roo.DomQuery;
2095         var recordType = this.recordType, fields = recordType.prototype.fields;
2096         var sid = this.meta.id;
2097         var totalRecords = 0, success = true;
2098         if(this.meta.totalRecords){
2099             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2100         }
2101         
2102         if(this.meta.success){
2103             var sv = q.selectValue(this.meta.success, root, true);
2104             success = sv !== false && sv !== 'false';
2105         }
2106         var records = [];
2107         var ns = q.select(this.meta.record, root);
2108         for(var i = 0, len = ns.length; i < len; i++) {
2109                 var n = ns[i];
2110                 var values = {};
2111                 var id = sid ? q.selectValue(sid, n) : undefined;
2112                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2113                     var f = fields.items[j];
2114                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2115                     v = f.convert(v);
2116                     values[f.name] = v;
2117                 }
2118                 var record = new recordType(values, id);
2119                 record.node = n;
2120                 records[records.length] = record;
2121             }
2122
2123             return {
2124                 success : success,
2125                 records : records,
2126                 totalRecords : totalRecords || records.length
2127             };
2128     }
2129 });/*
2130  * Based on:
2131  * Ext JS Library 1.1.1
2132  * Copyright(c) 2006-2007, Ext JS, LLC.
2133  *
2134  * Originally Released Under LGPL - original licence link has changed is not relivant.
2135  *
2136  * Fork - LGPL
2137  * <script type="text/javascript">
2138  */
2139
2140 /**
2141  * @class Roo.data.ArrayReader
2142  * @extends Roo.data.DataReader
2143  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2144  * Each element of that Array represents a row of data fields. The
2145  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2146  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2147  * <p>
2148  * Example code:.
2149  * <pre><code>
2150 var RecordDef = Roo.data.Record.create([
2151     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2152     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2153 ]);
2154 var myReader = new Roo.data.ArrayReader({
2155     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2156 }, RecordDef);
2157 </code></pre>
2158  * <p>
2159  * This would consume an Array like this:
2160  * <pre><code>
2161 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2162   </code></pre>
2163  
2164  * @constructor
2165  * Create a new JsonReader
2166  * @param {Object} meta Metadata configuration options.
2167  * @param {Object|Array} recordType Either an Array of field definition objects
2168  * 
2169  * @cfg {Array} fields Array of field definition objects
2170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2171  * as specified to {@link Roo.data.Record#create},
2172  * or an {@link Roo.data.Record} object
2173  *
2174  * 
2175  * created using {@link Roo.data.Record#create}.
2176  */
2177 Roo.data.ArrayReader = function(meta, recordType)
2178 {    
2179     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2180 };
2181
2182 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2183     
2184       /**
2185      * Create a data block containing Roo.data.Records from an XML document.
2186      * @param {Object} o An Array of row objects which represents the dataset.
2187      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2188      * a cache of Roo.data.Records.
2189      */
2190     readRecords : function(o)
2191     {
2192         var sid = this.meta ? this.meta.id : null;
2193         var recordType = this.recordType, fields = recordType.prototype.fields;
2194         var records = [];
2195         var root = o;
2196         for(var i = 0; i < root.length; i++){
2197                 var n = root[i];
2198             var values = {};
2199             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2200             for(var j = 0, jlen = fields.length; j < jlen; j++){
2201                 var f = fields.items[j];
2202                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2203                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2204                 v = f.convert(v);
2205                 values[f.name] = v;
2206             }
2207             var record = new recordType(values, id);
2208             record.json = n;
2209             records[records.length] = record;
2210         }
2211         return {
2212             records : records,
2213             totalRecords : records.length
2214         };
2215     },
2216     /**
2217      * using 'cn' the nested child reader read the child array into it's child stores.
2218      * @param {Object} rec The record with a 'children array
2219      */
2220     loadDataFromChildren: function(rec)
2221     {
2222         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2223         return this.loadData(typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
2224         
2225     }
2226     
2227     
2228 });/*
2229  * Based on:
2230  * Ext JS Library 1.1.1
2231  * Copyright(c) 2006-2007, Ext JS, LLC.
2232  *
2233  * Originally Released Under LGPL - original licence link has changed is not relivant.
2234  *
2235  * Fork - LGPL
2236  * <script type="text/javascript">
2237  */
2238
2239
2240 /**
2241  * @class Roo.data.Tree
2242  * @extends Roo.util.Observable
2243  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2244  * in the tree have most standard DOM functionality.
2245  * @constructor
2246  * @param {Node} root (optional) The root node
2247  */
2248 Roo.data.Tree = function(root){
2249    this.nodeHash = {};
2250    /**
2251     * The root node for this tree
2252     * @type Node
2253     */
2254    this.root = null;
2255    if(root){
2256        this.setRootNode(root);
2257    }
2258    this.addEvents({
2259        /**
2260         * @event append
2261         * Fires when a new child node is appended to a node in this tree.
2262         * @param {Tree} tree The owner tree
2263         * @param {Node} parent The parent node
2264         * @param {Node} node The newly appended node
2265         * @param {Number} index The index of the newly appended node
2266         */
2267        "append" : true,
2268        /**
2269         * @event remove
2270         * Fires when a child node is removed from a node in this tree.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} parent The parent node
2273         * @param {Node} node The child node removed
2274         */
2275        "remove" : true,
2276        /**
2277         * @event move
2278         * Fires when a node is moved to a new location in the tree
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} node The node moved
2281         * @param {Node} oldParent The old parent of this node
2282         * @param {Node} newParent The new parent of this node
2283         * @param {Number} index The index it was moved to
2284         */
2285        "move" : true,
2286        /**
2287         * @event insert
2288         * Fires when a new child node is inserted in a node in this tree.
2289         * @param {Tree} tree The owner tree
2290         * @param {Node} parent The parent node
2291         * @param {Node} node The child node inserted
2292         * @param {Node} refNode The child node the node was inserted before
2293         */
2294        "insert" : true,
2295        /**
2296         * @event beforeappend
2297         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2298         * @param {Tree} tree The owner tree
2299         * @param {Node} parent The parent node
2300         * @param {Node} node The child node to be appended
2301         */
2302        "beforeappend" : true,
2303        /**
2304         * @event beforeremove
2305         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2306         * @param {Tree} tree The owner tree
2307         * @param {Node} parent The parent node
2308         * @param {Node} node The child node to be removed
2309         */
2310        "beforeremove" : true,
2311        /**
2312         * @event beforemove
2313         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2314         * @param {Tree} tree The owner tree
2315         * @param {Node} node The node being moved
2316         * @param {Node} oldParent The parent of the node
2317         * @param {Node} newParent The new parent the node is moving to
2318         * @param {Number} index The index it is being moved to
2319         */
2320        "beforemove" : true,
2321        /**
2322         * @event beforeinsert
2323         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2324         * @param {Tree} tree The owner tree
2325         * @param {Node} parent The parent node
2326         * @param {Node} node The child node to be inserted
2327         * @param {Node} refNode The child node the node is being inserted before
2328         */
2329        "beforeinsert" : true
2330    });
2331
2332     Roo.data.Tree.superclass.constructor.call(this);
2333 };
2334
2335 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2336     pathSeparator: "/",
2337
2338     proxyNodeEvent : function(){
2339         return this.fireEvent.apply(this, arguments);
2340     },
2341
2342     /**
2343      * Returns the root node for this tree.
2344      * @return {Node}
2345      */
2346     getRootNode : function(){
2347         return this.root;
2348     },
2349
2350     /**
2351      * Sets the root node for this tree.
2352      * @param {Node} node
2353      * @return {Node}
2354      */
2355     setRootNode : function(node){
2356         this.root = node;
2357         node.ownerTree = this;
2358         node.isRoot = true;
2359         this.registerNode(node);
2360         return node;
2361     },
2362
2363     /**
2364      * Gets a node in this tree by its id.
2365      * @param {String} id
2366      * @return {Node}
2367      */
2368     getNodeById : function(id){
2369         return this.nodeHash[id];
2370     },
2371
2372     registerNode : function(node){
2373         this.nodeHash[node.id] = node;
2374     },
2375
2376     unregisterNode : function(node){
2377         delete this.nodeHash[node.id];
2378     },
2379
2380     toString : function(){
2381         return "[Tree"+(this.id?" "+this.id:"")+"]";
2382     }
2383 });
2384
2385 /**
2386  * @class Roo.data.Node
2387  * @extends Roo.util.Observable
2388  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2389  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2390  * @constructor
2391  * @param {Object} attributes The attributes/config for the node
2392  */
2393 Roo.data.Node = function(attributes){
2394     /**
2395      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2396      * @type {Object}
2397      */
2398     this.attributes = attributes || {};
2399     this.leaf = this.attributes.leaf;
2400     /**
2401      * The node id. @type String
2402      */
2403     this.id = this.attributes.id;
2404     if(!this.id){
2405         this.id = Roo.id(null, "ynode-");
2406         this.attributes.id = this.id;
2407     }
2408      
2409     
2410     /**
2411      * All child nodes of this node. @type Array
2412      */
2413     this.childNodes = [];
2414     if(!this.childNodes.indexOf){ // indexOf is a must
2415         this.childNodes.indexOf = function(o){
2416             for(var i = 0, len = this.length; i < len; i++){
2417                 if(this[i] == o) {
2418                     return i;
2419                 }
2420             }
2421             return -1;
2422         };
2423     }
2424     /**
2425      * The parent node for this node. @type Node
2426      */
2427     this.parentNode = null;
2428     /**
2429      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2430      */
2431     this.firstChild = null;
2432     /**
2433      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2434      */
2435     this.lastChild = null;
2436     /**
2437      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2438      */
2439     this.previousSibling = null;
2440     /**
2441      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2442      */
2443     this.nextSibling = null;
2444
2445     this.addEvents({
2446        /**
2447         * @event append
2448         * Fires when a new child node is appended
2449         * @param {Tree} tree The owner tree
2450         * @param {Node} this This node
2451         * @param {Node} node The newly appended node
2452         * @param {Number} index The index of the newly appended node
2453         */
2454        "append" : true,
2455        /**
2456         * @event remove
2457         * Fires when a child node is removed
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} node The removed node
2461         */
2462        "remove" : true,
2463        /**
2464         * @event move
2465         * Fires when this node is moved to a new location in the tree
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} oldParent The old parent of this node
2469         * @param {Node} newParent The new parent of this node
2470         * @param {Number} index The index it was moved to
2471         */
2472        "move" : true,
2473        /**
2474         * @event insert
2475         * Fires when a new child node is inserted.
2476         * @param {Tree} tree The owner tree
2477         * @param {Node} this This node
2478         * @param {Node} node The child node inserted
2479         * @param {Node} refNode The child node the node was inserted before
2480         */
2481        "insert" : true,
2482        /**
2483         * @event beforeappend
2484         * Fires before a new child is appended, return false to cancel the append.
2485         * @param {Tree} tree The owner tree
2486         * @param {Node} this This node
2487         * @param {Node} node The child node to be appended
2488         */
2489        "beforeappend" : true,
2490        /**
2491         * @event beforeremove
2492         * Fires before a child is removed, return false to cancel the remove.
2493         * @param {Tree} tree The owner tree
2494         * @param {Node} this This node
2495         * @param {Node} node The child node to be removed
2496         */
2497        "beforeremove" : true,
2498        /**
2499         * @event beforemove
2500         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2501         * @param {Tree} tree The owner tree
2502         * @param {Node} this This node
2503         * @param {Node} oldParent The parent of this node
2504         * @param {Node} newParent The new parent this node is moving to
2505         * @param {Number} index The index it is being moved to
2506         */
2507        "beforemove" : true,
2508        /**
2509         * @event beforeinsert
2510         * Fires before a new child is inserted, return false to cancel the insert.
2511         * @param {Tree} tree The owner tree
2512         * @param {Node} this This node
2513         * @param {Node} node The child node to be inserted
2514         * @param {Node} refNode The child node the node is being inserted before
2515         */
2516        "beforeinsert" : true
2517    });
2518     this.listeners = this.attributes.listeners;
2519     Roo.data.Node.superclass.constructor.call(this);
2520 };
2521
2522 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2523     fireEvent : function(evtName){
2524         // first do standard event for this node
2525         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2526             return false;
2527         }
2528         // then bubble it up to the tree if the event wasn't cancelled
2529         var ot = this.getOwnerTree();
2530         if(ot){
2531             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2532                 return false;
2533             }
2534         }
2535         return true;
2536     },
2537
2538     /**
2539      * Returns true if this node is a leaf
2540      * @return {Boolean}
2541      */
2542     isLeaf : function(){
2543         return this.leaf === true;
2544     },
2545
2546     // private
2547     setFirstChild : function(node){
2548         this.firstChild = node;
2549     },
2550
2551     //private
2552     setLastChild : function(node){
2553         this.lastChild = node;
2554     },
2555
2556
2557     /**
2558      * Returns true if this node is the last child of its parent
2559      * @return {Boolean}
2560      */
2561     isLast : function(){
2562        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2563     },
2564
2565     /**
2566      * Returns true if this node is the first child of its parent
2567      * @return {Boolean}
2568      */
2569     isFirst : function(){
2570        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2571     },
2572
2573     hasChildNodes : function(){
2574         return !this.isLeaf() && this.childNodes.length > 0;
2575     },
2576
2577     /**
2578      * Insert node(s) as the last child node of this node.
2579      * @param {Node/Array} node The node or Array of nodes to append
2580      * @return {Node} The appended node if single append, or null if an array was passed
2581      */
2582     appendChild : function(node){
2583         var multi = false;
2584         if(node instanceof Array){
2585             multi = node;
2586         }else if(arguments.length > 1){
2587             multi = arguments;
2588         }
2589         
2590         // if passed an array or multiple args do them one by one
2591         if(multi){
2592             for(var i = 0, len = multi.length; i < len; i++) {
2593                 this.appendChild(multi[i]);
2594             }
2595         }else{
2596             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2597                 return false;
2598             }
2599             var index = this.childNodes.length;
2600             var oldParent = node.parentNode;
2601             // it's a move, make sure we move it cleanly
2602             if(oldParent){
2603                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2604                     return false;
2605                 }
2606                 oldParent.removeChild(node);
2607             }
2608             
2609             index = this.childNodes.length;
2610             if(index == 0){
2611                 this.setFirstChild(node);
2612             }
2613             this.childNodes.push(node);
2614             node.parentNode = this;
2615             var ps = this.childNodes[index-1];
2616             if(ps){
2617                 node.previousSibling = ps;
2618                 ps.nextSibling = node;
2619             }else{
2620                 node.previousSibling = null;
2621             }
2622             node.nextSibling = null;
2623             this.setLastChild(node);
2624             node.setOwnerTree(this.getOwnerTree());
2625             this.fireEvent("append", this.ownerTree, this, node, index);
2626             if(this.ownerTree) {
2627                 this.ownerTree.fireEvent("appendnode", this, node, index);
2628             }
2629             if(oldParent){
2630                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2631             }
2632             return node;
2633         }
2634     },
2635
2636     /**
2637      * Removes a child node from this node.
2638      * @param {Node} node The node to remove
2639      * @return {Node} The removed node
2640      */
2641     removeChild : function(node){
2642         var index = this.childNodes.indexOf(node);
2643         if(index == -1){
2644             return false;
2645         }
2646         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2647             return false;
2648         }
2649
2650         // remove it from childNodes collection
2651         this.childNodes.splice(index, 1);
2652
2653         // update siblings
2654         if(node.previousSibling){
2655             node.previousSibling.nextSibling = node.nextSibling;
2656         }
2657         if(node.nextSibling){
2658             node.nextSibling.previousSibling = node.previousSibling;
2659         }
2660
2661         // update child refs
2662         if(this.firstChild == node){
2663             this.setFirstChild(node.nextSibling);
2664         }
2665         if(this.lastChild == node){
2666             this.setLastChild(node.previousSibling);
2667         }
2668
2669         node.setOwnerTree(null);
2670         // clear any references from the node
2671         node.parentNode = null;
2672         node.previousSibling = null;
2673         node.nextSibling = null;
2674         this.fireEvent("remove", this.ownerTree, this, node);
2675         return node;
2676     },
2677
2678     /**
2679      * Inserts the first node before the second node in this nodes childNodes collection.
2680      * @param {Node} node The node to insert
2681      * @param {Node} refNode The node to insert before (if null the node is appended)
2682      * @return {Node} The inserted node
2683      */
2684     insertBefore : function(node, refNode){
2685         if(!refNode){ // like standard Dom, refNode can be null for append
2686             return this.appendChild(node);
2687         }
2688         // nothing to do
2689         if(node == refNode){
2690             return false;
2691         }
2692
2693         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2694             return false;
2695         }
2696         var index = this.childNodes.indexOf(refNode);
2697         var oldParent = node.parentNode;
2698         var refIndex = index;
2699
2700         // when moving internally, indexes will change after remove
2701         if(oldParent == this && this.childNodes.indexOf(node) < index){
2702             refIndex--;
2703         }
2704
2705         // it's a move, make sure we move it cleanly
2706         if(oldParent){
2707             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2708                 return false;
2709             }
2710             oldParent.removeChild(node);
2711         }
2712         if(refIndex == 0){
2713             this.setFirstChild(node);
2714         }
2715         this.childNodes.splice(refIndex, 0, node);
2716         node.parentNode = this;
2717         var ps = this.childNodes[refIndex-1];
2718         if(ps){
2719             node.previousSibling = ps;
2720             ps.nextSibling = node;
2721         }else{
2722             node.previousSibling = null;
2723         }
2724         node.nextSibling = refNode;
2725         refNode.previousSibling = node;
2726         node.setOwnerTree(this.getOwnerTree());
2727         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2728         if(oldParent){
2729             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2730         }
2731         return node;
2732     },
2733
2734     /**
2735      * Returns the child node at the specified index.
2736      * @param {Number} index
2737      * @return {Node}
2738      */
2739     item : function(index){
2740         return this.childNodes[index];
2741     },
2742
2743     /**
2744      * Replaces one child node in this node with another.
2745      * @param {Node} newChild The replacement node
2746      * @param {Node} oldChild The node to replace
2747      * @return {Node} The replaced node
2748      */
2749     replaceChild : function(newChild, oldChild){
2750         this.insertBefore(newChild, oldChild);
2751         this.removeChild(oldChild);
2752         return oldChild;
2753     },
2754
2755     /**
2756      * Returns the index of a child node
2757      * @param {Node} node
2758      * @return {Number} The index of the node or -1 if it was not found
2759      */
2760     indexOf : function(child){
2761         return this.childNodes.indexOf(child);
2762     },
2763
2764     /**
2765      * Returns the tree this node is in.
2766      * @return {Tree}
2767      */
2768     getOwnerTree : function(){
2769         // if it doesn't have one, look for one
2770         if(!this.ownerTree){
2771             var p = this;
2772             while(p){
2773                 if(p.ownerTree){
2774                     this.ownerTree = p.ownerTree;
2775                     break;
2776                 }
2777                 p = p.parentNode;
2778             }
2779         }
2780         return this.ownerTree;
2781     },
2782
2783     /**
2784      * Returns depth of this node (the root node has a depth of 0)
2785      * @return {Number}
2786      */
2787     getDepth : function(){
2788         var depth = 0;
2789         var p = this;
2790         while(p.parentNode){
2791             ++depth;
2792             p = p.parentNode;
2793         }
2794         return depth;
2795     },
2796
2797     // private
2798     setOwnerTree : function(tree){
2799         // if it's move, we need to update everyone
2800         if(tree != this.ownerTree){
2801             if(this.ownerTree){
2802                 this.ownerTree.unregisterNode(this);
2803             }
2804             this.ownerTree = tree;
2805             var cs = this.childNodes;
2806             for(var i = 0, len = cs.length; i < len; i++) {
2807                 cs[i].setOwnerTree(tree);
2808             }
2809             if(tree){
2810                 tree.registerNode(this);
2811             }
2812         }
2813     },
2814
2815     /**
2816      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2817      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2818      * @return {String} The path
2819      */
2820     getPath : function(attr){
2821         attr = attr || "id";
2822         var p = this.parentNode;
2823         var b = [this.attributes[attr]];
2824         while(p){
2825             b.unshift(p.attributes[attr]);
2826             p = p.parentNode;
2827         }
2828         var sep = this.getOwnerTree().pathSeparator;
2829         return sep + b.join(sep);
2830     },
2831
2832     /**
2833      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2834      * function call will be the scope provided or the current node. The arguments to the function
2835      * will be the args provided or the current node. If the function returns false at any point,
2836      * the bubble is stopped.
2837      * @param {Function} fn The function to call
2838      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2839      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2840      */
2841     bubble : function(fn, scope, args){
2842         var p = this;
2843         while(p){
2844             if(fn.call(scope || p, args || p) === false){
2845                 break;
2846             }
2847             p = p.parentNode;
2848         }
2849     },
2850
2851     /**
2852      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2853      * function call will be the scope provided or the current node. The arguments to the function
2854      * will be the args provided or the current node. If the function returns false at any point,
2855      * the cascade is stopped on that branch.
2856      * @param {Function} fn The function to call
2857      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2858      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2859      */
2860     cascade : function(fn, scope, args){
2861         if(fn.call(scope || this, args || this) !== false){
2862             var cs = this.childNodes;
2863             for(var i = 0, len = cs.length; i < len; i++) {
2864                 cs[i].cascade(fn, scope, args);
2865             }
2866         }
2867     },
2868
2869     /**
2870      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2871      * function call will be the scope provided or the current node. The arguments to the function
2872      * will be the args provided or the current node. If the function returns false at any point,
2873      * the iteration stops.
2874      * @param {Function} fn The function to call
2875      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2876      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2877      */
2878     eachChild : function(fn, scope, args){
2879         var cs = this.childNodes;
2880         for(var i = 0, len = cs.length; i < len; i++) {
2881                 if(fn.call(scope || this, args || cs[i]) === false){
2882                     break;
2883                 }
2884         }
2885     },
2886
2887     /**
2888      * Finds the first child that has the attribute with the specified value.
2889      * @param {String} attribute The attribute name
2890      * @param {Mixed} value The value to search for
2891      * @return {Node} The found child or null if none was found
2892      */
2893     findChild : function(attribute, value){
2894         var cs = this.childNodes;
2895         for(var i = 0, len = cs.length; i < len; i++) {
2896                 if(cs[i].attributes[attribute] == value){
2897                     return cs[i];
2898                 }
2899         }
2900         return null;
2901     },
2902
2903     /**
2904      * Finds the first child by a custom function. The child matches if the function passed
2905      * returns true.
2906      * @param {Function} fn
2907      * @param {Object} scope (optional)
2908      * @return {Node} The found child or null if none was found
2909      */
2910     findChildBy : function(fn, scope){
2911         var cs = this.childNodes;
2912         for(var i = 0, len = cs.length; i < len; i++) {
2913                 if(fn.call(scope||cs[i], cs[i]) === true){
2914                     return cs[i];
2915                 }
2916         }
2917         return null;
2918     },
2919
2920     /**
2921      * Sorts this nodes children using the supplied sort function
2922      * @param {Function} fn
2923      * @param {Object} scope (optional)
2924      */
2925     sort : function(fn, scope){
2926         var cs = this.childNodes;
2927         var len = cs.length;
2928         if(len > 0){
2929             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2930             cs.sort(sortFn);
2931             for(var i = 0; i < len; i++){
2932                 var n = cs[i];
2933                 n.previousSibling = cs[i-1];
2934                 n.nextSibling = cs[i+1];
2935                 if(i == 0){
2936                     this.setFirstChild(n);
2937                 }
2938                 if(i == len-1){
2939                     this.setLastChild(n);
2940                 }
2941             }
2942         }
2943     },
2944
2945     /**
2946      * Returns true if this node is an ancestor (at any point) of the passed node.
2947      * @param {Node} node
2948      * @return {Boolean}
2949      */
2950     contains : function(node){
2951         return node.isAncestor(this);
2952     },
2953
2954     /**
2955      * Returns true if the passed node is an ancestor (at any point) of this node.
2956      * @param {Node} node
2957      * @return {Boolean}
2958      */
2959     isAncestor : function(node){
2960         var p = this.parentNode;
2961         while(p){
2962             if(p == node){
2963                 return true;
2964             }
2965             p = p.parentNode;
2966         }
2967         return false;
2968     },
2969
2970     toString : function(){
2971         return "[Node"+(this.id?" "+this.id:"")+"]";
2972     }
2973 });/*
2974  * Based on:
2975  * Ext JS Library 1.1.1
2976  * Copyright(c) 2006-2007, Ext JS, LLC.
2977  *
2978  * Originally Released Under LGPL - original licence link has changed is not relivant.
2979  *
2980  * Fork - LGPL
2981  * <script type="text/javascript">
2982  */
2983  (function(){ 
2984 /**
2985  * @class Roo.Layer
2986  * @extends Roo.Element
2987  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2988  * automatic maintaining of shadow/shim positions.
2989  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2990  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2991  * you can pass a string with a CSS class name. False turns off the shadow.
2992  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2993  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2994  * @cfg {String} cls CSS class to add to the element
2995  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2996  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2997  * @constructor
2998  * @param {Object} config An object with config options.
2999  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
3000  */
3001
3002 Roo.Layer = function(config, existingEl){
3003     config = config || {};
3004     var dh = Roo.DomHelper;
3005     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
3006     if(existingEl){
3007         this.dom = Roo.getDom(existingEl);
3008     }
3009     if(!this.dom){
3010         var o = config.dh || {tag: "div", cls: "x-layer"};
3011         this.dom = dh.append(pel, o);
3012     }
3013     if(config.cls){
3014         this.addClass(config.cls);
3015     }
3016     this.constrain = config.constrain !== false;
3017     this.visibilityMode = Roo.Element.VISIBILITY;
3018     if(config.id){
3019         this.id = this.dom.id = config.id;
3020     }else{
3021         this.id = Roo.id(this.dom);
3022     }
3023     this.zindex = config.zindex || this.getZIndex();
3024     this.position("absolute", this.zindex);
3025     if(config.shadow){
3026         this.shadowOffset = config.shadowOffset || 4;
3027         this.shadow = new Roo.Shadow({
3028             offset : this.shadowOffset,
3029             mode : config.shadow
3030         });
3031     }else{
3032         this.shadowOffset = 0;
3033     }
3034     this.useShim = config.shim !== false && Roo.useShims;
3035     this.useDisplay = config.useDisplay;
3036     this.hide();
3037 };
3038
3039 var supr = Roo.Element.prototype;
3040
3041 // shims are shared among layer to keep from having 100 iframes
3042 var shims = [];
3043
3044 Roo.extend(Roo.Layer, Roo.Element, {
3045
3046     getZIndex : function(){
3047         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3048     },
3049
3050     getShim : function(){
3051         if(!this.useShim){
3052             return null;
3053         }
3054         if(this.shim){
3055             return this.shim;
3056         }
3057         var shim = shims.shift();
3058         if(!shim){
3059             shim = this.createShim();
3060             shim.enableDisplayMode('block');
3061             shim.dom.style.display = 'none';
3062             shim.dom.style.visibility = 'visible';
3063         }
3064         var pn = this.dom.parentNode;
3065         if(shim.dom.parentNode != pn){
3066             pn.insertBefore(shim.dom, this.dom);
3067         }
3068         shim.setStyle('z-index', this.getZIndex()-2);
3069         this.shim = shim;
3070         return shim;
3071     },
3072
3073     hideShim : function(){
3074         if(this.shim){
3075             this.shim.setDisplayed(false);
3076             shims.push(this.shim);
3077             delete this.shim;
3078         }
3079     },
3080
3081     disableShadow : function(){
3082         if(this.shadow){
3083             this.shadowDisabled = true;
3084             this.shadow.hide();
3085             this.lastShadowOffset = this.shadowOffset;
3086             this.shadowOffset = 0;
3087         }
3088     },
3089
3090     enableShadow : function(show){
3091         if(this.shadow){
3092             this.shadowDisabled = false;
3093             this.shadowOffset = this.lastShadowOffset;
3094             delete this.lastShadowOffset;
3095             if(show){
3096                 this.sync(true);
3097             }
3098         }
3099     },
3100
3101     // private
3102     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3103     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3104     sync : function(doShow){
3105         var sw = this.shadow;
3106         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3107             var sh = this.getShim();
3108
3109             var w = this.getWidth(),
3110                 h = this.getHeight();
3111
3112             var l = this.getLeft(true),
3113                 t = this.getTop(true);
3114
3115             if(sw && !this.shadowDisabled){
3116                 if(doShow && !sw.isVisible()){
3117                     sw.show(this);
3118                 }else{
3119                     sw.realign(l, t, w, h);
3120                 }
3121                 if(sh){
3122                     if(doShow){
3123                        sh.show();
3124                     }
3125                     // fit the shim behind the shadow, so it is shimmed too
3126                     var a = sw.adjusts, s = sh.dom.style;
3127                     s.left = (Math.min(l, l+a.l))+"px";
3128                     s.top = (Math.min(t, t+a.t))+"px";
3129                     s.width = (w+a.w)+"px";
3130                     s.height = (h+a.h)+"px";
3131                 }
3132             }else if(sh){
3133                 if(doShow){
3134                    sh.show();
3135                 }
3136                 sh.setSize(w, h);
3137                 sh.setLeftTop(l, t);
3138             }
3139             
3140         }
3141     },
3142
3143     // private
3144     destroy : function(){
3145         this.hideShim();
3146         if(this.shadow){
3147             this.shadow.hide();
3148         }
3149         this.removeAllListeners();
3150         var pn = this.dom.parentNode;
3151         if(pn){
3152             pn.removeChild(this.dom);
3153         }
3154         Roo.Element.uncache(this.id);
3155     },
3156
3157     remove : function(){
3158         this.destroy();
3159     },
3160
3161     // private
3162     beginUpdate : function(){
3163         this.updating = true;
3164     },
3165
3166     // private
3167     endUpdate : function(){
3168         this.updating = false;
3169         this.sync(true);
3170     },
3171
3172     // private
3173     hideUnders : function(negOffset){
3174         if(this.shadow){
3175             this.shadow.hide();
3176         }
3177         this.hideShim();
3178     },
3179
3180     // private
3181     constrainXY : function(){
3182         if(this.constrain){
3183             var vw = Roo.lib.Dom.getViewWidth(),
3184                 vh = Roo.lib.Dom.getViewHeight();
3185             var s = Roo.get(document).getScroll();
3186
3187             var xy = this.getXY();
3188             var x = xy[0], y = xy[1];   
3189             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3190             // only move it if it needs it
3191             var moved = false;
3192             // first validate right/bottom
3193             if((x + w) > vw+s.left){
3194                 x = vw - w - this.shadowOffset;
3195                 moved = true;
3196             }
3197             if((y + h) > vh+s.top){
3198                 y = vh - h - this.shadowOffset;
3199                 moved = true;
3200             }
3201             // then make sure top/left isn't negative
3202             if(x < s.left){
3203                 x = s.left;
3204                 moved = true;
3205             }
3206             if(y < s.top){
3207                 y = s.top;
3208                 moved = true;
3209             }
3210             if(moved){
3211                 if(this.avoidY){
3212                     var ay = this.avoidY;
3213                     if(y <= ay && (y+h) >= ay){
3214                         y = ay-h-5;   
3215                     }
3216                 }
3217                 xy = [x, y];
3218                 this.storeXY(xy);
3219                 supr.setXY.call(this, xy);
3220                 this.sync();
3221             }
3222         }
3223     },
3224
3225     isVisible : function(){
3226         return this.visible;    
3227     },
3228
3229     // private
3230     showAction : function(){
3231         this.visible = true; // track visibility to prevent getStyle calls
3232         if(this.useDisplay === true){
3233             this.setDisplayed("");
3234         }else if(this.lastXY){
3235             supr.setXY.call(this, this.lastXY);
3236         }else if(this.lastLT){
3237             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3238         }
3239     },
3240
3241     // private
3242     hideAction : function(){
3243         this.visible = false;
3244         if(this.useDisplay === true){
3245             this.setDisplayed(false);
3246         }else{
3247             this.setLeftTop(-10000,-10000);
3248         }
3249     },
3250
3251     // overridden Element method
3252     setVisible : function(v, a, d, c, e){
3253         if(v){
3254             this.showAction();
3255         }
3256         if(a && v){
3257             var cb = function(){
3258                 this.sync(true);
3259                 if(c){
3260                     c();
3261                 }
3262             }.createDelegate(this);
3263             supr.setVisible.call(this, true, true, d, cb, e);
3264         }else{
3265             if(!v){
3266                 this.hideUnders(true);
3267             }
3268             var cb = c;
3269             if(a){
3270                 cb = function(){
3271                     this.hideAction();
3272                     if(c){
3273                         c();
3274                     }
3275                 }.createDelegate(this);
3276             }
3277             supr.setVisible.call(this, v, a, d, cb, e);
3278             if(v){
3279                 this.sync(true);
3280             }else if(!a){
3281                 this.hideAction();
3282             }
3283         }
3284     },
3285
3286     storeXY : function(xy){
3287         delete this.lastLT;
3288         this.lastXY = xy;
3289     },
3290
3291     storeLeftTop : function(left, top){
3292         delete this.lastXY;
3293         this.lastLT = [left, top];
3294     },
3295
3296     // private
3297     beforeFx : function(){
3298         this.beforeAction();
3299         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3300     },
3301
3302     // private
3303     afterFx : function(){
3304         Roo.Layer.superclass.afterFx.apply(this, arguments);
3305         this.sync(this.isVisible());
3306     },
3307
3308     // private
3309     beforeAction : function(){
3310         if(!this.updating && this.shadow){
3311             this.shadow.hide();
3312         }
3313     },
3314
3315     // overridden Element method
3316     setLeft : function(left){
3317         this.storeLeftTop(left, this.getTop(true));
3318         supr.setLeft.apply(this, arguments);
3319         this.sync();
3320     },
3321
3322     setTop : function(top){
3323         this.storeLeftTop(this.getLeft(true), top);
3324         supr.setTop.apply(this, arguments);
3325         this.sync();
3326     },
3327
3328     setLeftTop : function(left, top){
3329         this.storeLeftTop(left, top);
3330         supr.setLeftTop.apply(this, arguments);
3331         this.sync();
3332     },
3333
3334     setXY : function(xy, a, d, c, e){
3335         this.fixDisplay();
3336         this.beforeAction();
3337         this.storeXY(xy);
3338         var cb = this.createCB(c);
3339         supr.setXY.call(this, xy, a, d, cb, e);
3340         if(!a){
3341             cb();
3342         }
3343     },
3344
3345     // private
3346     createCB : function(c){
3347         var el = this;
3348         return function(){
3349             el.constrainXY();
3350             el.sync(true);
3351             if(c){
3352                 c();
3353             }
3354         };
3355     },
3356
3357     // overridden Element method
3358     setX : function(x, a, d, c, e){
3359         this.setXY([x, this.getY()], a, d, c, e);
3360     },
3361
3362     // overridden Element method
3363     setY : function(y, a, d, c, e){
3364         this.setXY([this.getX(), y], a, d, c, e);
3365     },
3366
3367     // overridden Element method
3368     setSize : function(w, h, a, d, c, e){
3369         this.beforeAction();
3370         var cb = this.createCB(c);
3371         supr.setSize.call(this, w, h, a, d, cb, e);
3372         if(!a){
3373             cb();
3374         }
3375     },
3376
3377     // overridden Element method
3378     setWidth : function(w, a, d, c, e){
3379         this.beforeAction();
3380         var cb = this.createCB(c);
3381         supr.setWidth.call(this, w, a, d, cb, e);
3382         if(!a){
3383             cb();
3384         }
3385     },
3386
3387     // overridden Element method
3388     setHeight : function(h, a, d, c, e){
3389         this.beforeAction();
3390         var cb = this.createCB(c);
3391         supr.setHeight.call(this, h, a, d, cb, e);
3392         if(!a){
3393             cb();
3394         }
3395     },
3396
3397     // overridden Element method
3398     setBounds : function(x, y, w, h, a, d, c, e){
3399         this.beforeAction();
3400         var cb = this.createCB(c);
3401         if(!a){
3402             this.storeXY([x, y]);
3403             supr.setXY.call(this, [x, y]);
3404             supr.setSize.call(this, w, h, a, d, cb, e);
3405             cb();
3406         }else{
3407             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3408         }
3409         return this;
3410     },
3411     
3412     /**
3413      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3414      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3415      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3416      * @param {Number} zindex The new z-index to set
3417      * @return {this} The Layer
3418      */
3419     setZIndex : function(zindex){
3420         this.zindex = zindex;
3421         this.setStyle("z-index", zindex + 2);
3422         if(this.shadow){
3423             this.shadow.setZIndex(zindex + 1);
3424         }
3425         if(this.shim){
3426             this.shim.setStyle("z-index", zindex);
3427         }
3428     }
3429 });
3430 })();/*
3431  * Based on:
3432  * Ext JS Library 1.1.1
3433  * Copyright(c) 2006-2007, Ext JS, LLC.
3434  *
3435  * Originally Released Under LGPL - original licence link has changed is not relivant.
3436  *
3437  * Fork - LGPL
3438  * <script type="text/javascript">
3439  */
3440
3441
3442 /**
3443  * @class Roo.Shadow
3444  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3445  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3446  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3447  * @constructor
3448  * Create a new Shadow
3449  * @param {Object} config The config object
3450  */
3451 Roo.Shadow = function(config){
3452     Roo.apply(this, config);
3453     if(typeof this.mode != "string"){
3454         this.mode = this.defaultMode;
3455     }
3456     var o = this.offset, a = {h: 0};
3457     var rad = Math.floor(this.offset/2);
3458     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3459         case "drop":
3460             a.w = 0;
3461             a.l = a.t = o;
3462             a.t -= 1;
3463             if(Roo.isIE){
3464                 a.l -= this.offset + rad;
3465                 a.t -= this.offset + rad;
3466                 a.w -= rad;
3467                 a.h -= rad;
3468                 a.t += 1;
3469             }
3470         break;
3471         case "sides":
3472             a.w = (o*2);
3473             a.l = -o;
3474             a.t = o-1;
3475             if(Roo.isIE){
3476                 a.l -= (this.offset - rad);
3477                 a.t -= this.offset + rad;
3478                 a.l += 1;
3479                 a.w -= (this.offset - rad)*2;
3480                 a.w -= rad + 1;
3481                 a.h -= 1;
3482             }
3483         break;
3484         case "frame":
3485             a.w = a.h = (o*2);
3486             a.l = a.t = -o;
3487             a.t += 1;
3488             a.h -= 2;
3489             if(Roo.isIE){
3490                 a.l -= (this.offset - rad);
3491                 a.t -= (this.offset - rad);
3492                 a.l += 1;
3493                 a.w -= (this.offset + rad + 1);
3494                 a.h -= (this.offset + rad);
3495                 a.h += 1;
3496             }
3497         break;
3498     };
3499
3500     this.adjusts = a;
3501 };
3502
3503 Roo.Shadow.prototype = {
3504     /**
3505      * @cfg {String} mode
3506      * The shadow display mode.  Supports the following options:<br />
3507      * sides: Shadow displays on both sides and bottom only<br />
3508      * frame: Shadow displays equally on all four sides<br />
3509      * drop: Traditional bottom-right drop shadow (default)
3510      */
3511     /**
3512      * @cfg {String} offset
3513      * The number of pixels to offset the shadow from the element (defaults to 4)
3514      */
3515     offset: 4,
3516
3517     // private
3518     defaultMode: "drop",
3519
3520     /**
3521      * Displays the shadow under the target element
3522      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3523      */
3524     show : function(target){
3525         target = Roo.get(target);
3526         if(!this.el){
3527             this.el = Roo.Shadow.Pool.pull();
3528             if(this.el.dom.nextSibling != target.dom){
3529                 this.el.insertBefore(target);
3530             }
3531         }
3532         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3533         if(Roo.isIE){
3534             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3535         }
3536         this.realign(
3537             target.getLeft(true),
3538             target.getTop(true),
3539             target.getWidth(),
3540             target.getHeight()
3541         );
3542         this.el.dom.style.display = "block";
3543     },
3544
3545     /**
3546      * Returns true if the shadow is visible, else false
3547      */
3548     isVisible : function(){
3549         return this.el ? true : false;  
3550     },
3551
3552     /**
3553      * Direct alignment when values are already available. Show must be called at least once before
3554      * calling this method to ensure it is initialized.
3555      * @param {Number} left The target element left position
3556      * @param {Number} top The target element top position
3557      * @param {Number} width The target element width
3558      * @param {Number} height The target element height
3559      */
3560     realign : function(l, t, w, h){
3561         if(!this.el){
3562             return;
3563         }
3564         var a = this.adjusts, d = this.el.dom, s = d.style;
3565         var iea = 0;
3566         s.left = (l+a.l)+"px";
3567         s.top = (t+a.t)+"px";
3568         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3569  
3570         if(s.width != sws || s.height != shs){
3571             s.width = sws;
3572             s.height = shs;
3573             if(!Roo.isIE){
3574                 var cn = d.childNodes;
3575                 var sww = Math.max(0, (sw-12))+"px";
3576                 cn[0].childNodes[1].style.width = sww;
3577                 cn[1].childNodes[1].style.width = sww;
3578                 cn[2].childNodes[1].style.width = sww;
3579                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3580             }
3581         }
3582     },
3583
3584     /**
3585      * Hides this shadow
3586      */
3587     hide : function(){
3588         if(this.el){
3589             this.el.dom.style.display = "none";
3590             Roo.Shadow.Pool.push(this.el);
3591             delete this.el;
3592         }
3593     },
3594
3595     /**
3596      * Adjust the z-index of this shadow
3597      * @param {Number} zindex The new z-index
3598      */
3599     setZIndex : function(z){
3600         this.zIndex = z;
3601         if(this.el){
3602             this.el.setStyle("z-index", z);
3603         }
3604     }
3605 };
3606
3607 // Private utility class that manages the internal Shadow cache
3608 Roo.Shadow.Pool = function(){
3609     var p = [];
3610     var markup = Roo.isIE ?
3611                  '<div class="x-ie-shadow"></div>' :
3612                  '<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>';
3613     return {
3614         pull : function(){
3615             var sh = p.shift();
3616             if(!sh){
3617                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3618                 sh.autoBoxAdjust = false;
3619             }
3620             return sh;
3621         },
3622
3623         push : function(sh){
3624             p.push(sh);
3625         }
3626     };
3627 }();/*
3628  * Based on:
3629  * Ext JS Library 1.1.1
3630  * Copyright(c) 2006-2007, Ext JS, LLC.
3631  *
3632  * Originally Released Under LGPL - original licence link has changed is not relivant.
3633  *
3634  * Fork - LGPL
3635  * <script type="text/javascript">
3636  */
3637
3638
3639 /**
3640  * @class Roo.SplitBar
3641  * @extends Roo.util.Observable
3642  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3643  * <br><br>
3644  * Usage:
3645  * <pre><code>
3646 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3647                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3648 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3649 split.minSize = 100;
3650 split.maxSize = 600;
3651 split.animate = true;
3652 split.on('moved', splitterMoved);
3653 </code></pre>
3654  * @constructor
3655  * Create a new SplitBar
3656  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3657  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3658  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3659  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3660                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3661                         position of the SplitBar).
3662  */
3663 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3664     
3665     /** @private */
3666     this.el = Roo.get(dragElement, true);
3667     this.el.dom.unselectable = "on";
3668     /** @private */
3669     this.resizingEl = Roo.get(resizingElement, true);
3670
3671     /**
3672      * @private
3673      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3674      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3675      * @type Number
3676      */
3677     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3678     
3679     /**
3680      * The minimum size of the resizing element. (Defaults to 0)
3681      * @type Number
3682      */
3683     this.minSize = 0;
3684     
3685     /**
3686      * The maximum size of the resizing element. (Defaults to 2000)
3687      * @type Number
3688      */
3689     this.maxSize = 2000;
3690     
3691     /**
3692      * Whether to animate the transition to the new size
3693      * @type Boolean
3694      */
3695     this.animate = false;
3696     
3697     /**
3698      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3699      * @type Boolean
3700      */
3701     this.useShim = false;
3702     
3703     /** @private */
3704     this.shim = null;
3705     
3706     if(!existingProxy){
3707         /** @private */
3708         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3709     }else{
3710         this.proxy = Roo.get(existingProxy).dom;
3711     }
3712     /** @private */
3713     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3714     
3715     /** @private */
3716     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3717     
3718     /** @private */
3719     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3720     
3721     /** @private */
3722     this.dragSpecs = {};
3723     
3724     /**
3725      * @private The adapter to use to positon and resize elements
3726      */
3727     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3728     this.adapter.init(this);
3729     
3730     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3731         /** @private */
3732         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3733         this.el.addClass("x-splitbar-h");
3734     }else{
3735         /** @private */
3736         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3737         this.el.addClass("x-splitbar-v");
3738     }
3739     
3740     this.addEvents({
3741         /**
3742          * @event resize
3743          * Fires when the splitter is moved (alias for {@link #event-moved})
3744          * @param {Roo.SplitBar} this
3745          * @param {Number} newSize the new width or height
3746          */
3747         "resize" : true,
3748         /**
3749          * @event moved
3750          * Fires when the splitter is moved
3751          * @param {Roo.SplitBar} this
3752          * @param {Number} newSize the new width or height
3753          */
3754         "moved" : true,
3755         /**
3756          * @event beforeresize
3757          * Fires before the splitter is dragged
3758          * @param {Roo.SplitBar} this
3759          */
3760         "beforeresize" : true,
3761
3762         "beforeapply" : true
3763     });
3764
3765     Roo.util.Observable.call(this);
3766 };
3767
3768 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3769     onStartProxyDrag : function(x, y){
3770         this.fireEvent("beforeresize", this);
3771         if(!this.overlay){
3772             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3773             o.unselectable();
3774             o.enableDisplayMode("block");
3775             // all splitbars share the same overlay
3776             Roo.SplitBar.prototype.overlay = o;
3777         }
3778         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3779         this.overlay.show();
3780         Roo.get(this.proxy).setDisplayed("block");
3781         var size = this.adapter.getElementSize(this);
3782         this.activeMinSize = this.getMinimumSize();;
3783         this.activeMaxSize = this.getMaximumSize();;
3784         var c1 = size - this.activeMinSize;
3785         var c2 = Math.max(this.activeMaxSize - size, 0);
3786         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3787             this.dd.resetConstraints();
3788             this.dd.setXConstraint(
3789                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3790                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3791             );
3792             this.dd.setYConstraint(0, 0);
3793         }else{
3794             this.dd.resetConstraints();
3795             this.dd.setXConstraint(0, 0);
3796             this.dd.setYConstraint(
3797                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3798                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3799             );
3800          }
3801         this.dragSpecs.startSize = size;
3802         this.dragSpecs.startPoint = [x, y];
3803         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3804     },
3805     
3806     /** 
3807      * @private Called after the drag operation by the DDProxy
3808      */
3809     onEndProxyDrag : function(e){
3810         Roo.get(this.proxy).setDisplayed(false);
3811         var endPoint = Roo.lib.Event.getXY(e);
3812         if(this.overlay){
3813             this.overlay.hide();
3814         }
3815         var newSize;
3816         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3817             newSize = this.dragSpecs.startSize + 
3818                 (this.placement == Roo.SplitBar.LEFT ?
3819                     endPoint[0] - this.dragSpecs.startPoint[0] :
3820                     this.dragSpecs.startPoint[0] - endPoint[0]
3821                 );
3822         }else{
3823             newSize = this.dragSpecs.startSize + 
3824                 (this.placement == Roo.SplitBar.TOP ?
3825                     endPoint[1] - this.dragSpecs.startPoint[1] :
3826                     this.dragSpecs.startPoint[1] - endPoint[1]
3827                 );
3828         }
3829         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3830         if(newSize != this.dragSpecs.startSize){
3831             if(this.fireEvent('beforeapply', this, newSize) !== false){
3832                 this.adapter.setElementSize(this, newSize);
3833                 this.fireEvent("moved", this, newSize);
3834                 this.fireEvent("resize", this, newSize);
3835             }
3836         }
3837     },
3838     
3839     /**
3840      * Get the adapter this SplitBar uses
3841      * @return The adapter object
3842      */
3843     getAdapter : function(){
3844         return this.adapter;
3845     },
3846     
3847     /**
3848      * Set the adapter this SplitBar uses
3849      * @param {Object} adapter A SplitBar adapter object
3850      */
3851     setAdapter : function(adapter){
3852         this.adapter = adapter;
3853         this.adapter.init(this);
3854     },
3855     
3856     /**
3857      * Gets the minimum size for the resizing element
3858      * @return {Number} The minimum size
3859      */
3860     getMinimumSize : function(){
3861         return this.minSize;
3862     },
3863     
3864     /**
3865      * Sets the minimum size for the resizing element
3866      * @param {Number} minSize The minimum size
3867      */
3868     setMinimumSize : function(minSize){
3869         this.minSize = minSize;
3870     },
3871     
3872     /**
3873      * Gets the maximum size for the resizing element
3874      * @return {Number} The maximum size
3875      */
3876     getMaximumSize : function(){
3877         return this.maxSize;
3878     },
3879     
3880     /**
3881      * Sets the maximum size for the resizing element
3882      * @param {Number} maxSize The maximum size
3883      */
3884     setMaximumSize : function(maxSize){
3885         this.maxSize = maxSize;
3886     },
3887     
3888     /**
3889      * Sets the initialize size for the resizing element
3890      * @param {Number} size The initial size
3891      */
3892     setCurrentSize : function(size){
3893         var oldAnimate = this.animate;
3894         this.animate = false;
3895         this.adapter.setElementSize(this, size);
3896         this.animate = oldAnimate;
3897     },
3898     
3899     /**
3900      * Destroy this splitbar. 
3901      * @param {Boolean} removeEl True to remove the element
3902      */
3903     destroy : function(removeEl){
3904         if(this.shim){
3905             this.shim.remove();
3906         }
3907         this.dd.unreg();
3908         this.proxy.parentNode.removeChild(this.proxy);
3909         if(removeEl){
3910             this.el.remove();
3911         }
3912     }
3913 });
3914
3915 /**
3916  * @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.
3917  */
3918 Roo.SplitBar.createProxy = function(dir){
3919     var proxy = new Roo.Element(document.createElement("div"));
3920     proxy.unselectable();
3921     var cls = 'x-splitbar-proxy';
3922     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3923     document.body.appendChild(proxy.dom);
3924     return proxy.dom;
3925 };
3926
3927 /** 
3928  * @class Roo.SplitBar.BasicLayoutAdapter
3929  * Default Adapter. It assumes the splitter and resizing element are not positioned
3930  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3931  */
3932 Roo.SplitBar.BasicLayoutAdapter = function(){
3933 };
3934
3935 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3936     // do nothing for now
3937     init : function(s){
3938     
3939     },
3940     /**
3941      * Called before drag operations to get the current size of the resizing element. 
3942      * @param {Roo.SplitBar} s The SplitBar using this adapter
3943      */
3944      getElementSize : function(s){
3945         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3946             return s.resizingEl.getWidth();
3947         }else{
3948             return s.resizingEl.getHeight();
3949         }
3950     },
3951     
3952     /**
3953      * Called after drag operations to set the size of the resizing element.
3954      * @param {Roo.SplitBar} s The SplitBar using this adapter
3955      * @param {Number} newSize The new size to set
3956      * @param {Function} onComplete A function to be invoked when resizing is complete
3957      */
3958     setElementSize : function(s, newSize, onComplete){
3959         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3960             if(!s.animate){
3961                 s.resizingEl.setWidth(newSize);
3962                 if(onComplete){
3963                     onComplete(s, newSize);
3964                 }
3965             }else{
3966                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3967             }
3968         }else{
3969             
3970             if(!s.animate){
3971                 s.resizingEl.setHeight(newSize);
3972                 if(onComplete){
3973                     onComplete(s, newSize);
3974                 }
3975             }else{
3976                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3977             }
3978         }
3979     }
3980 };
3981
3982 /** 
3983  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3984  * @extends Roo.SplitBar.BasicLayoutAdapter
3985  * Adapter that  moves the splitter element to align with the resized sizing element. 
3986  * Used with an absolute positioned SplitBar.
3987  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3988  * document.body, make sure you assign an id to the body element.
3989  */
3990 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3991     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3992     this.container = Roo.get(container);
3993 };
3994
3995 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3996     init : function(s){
3997         this.basic.init(s);
3998     },
3999     
4000     getElementSize : function(s){
4001         return this.basic.getElementSize(s);
4002     },
4003     
4004     setElementSize : function(s, newSize, onComplete){
4005         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
4006     },
4007     
4008     moveSplitter : function(s){
4009         var yes = Roo.SplitBar;
4010         switch(s.placement){
4011             case yes.LEFT:
4012                 s.el.setX(s.resizingEl.getRight());
4013                 break;
4014             case yes.RIGHT:
4015                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
4016                 break;
4017             case yes.TOP:
4018                 s.el.setY(s.resizingEl.getBottom());
4019                 break;
4020             case yes.BOTTOM:
4021                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4022                 break;
4023         }
4024     }
4025 };
4026
4027 /**
4028  * Orientation constant - Create a vertical SplitBar
4029  * @static
4030  * @type Number
4031  */
4032 Roo.SplitBar.VERTICAL = 1;
4033
4034 /**
4035  * Orientation constant - Create a horizontal SplitBar
4036  * @static
4037  * @type Number
4038  */
4039 Roo.SplitBar.HORIZONTAL = 2;
4040
4041 /**
4042  * Placement constant - The resizing element is to the left of the splitter element
4043  * @static
4044  * @type Number
4045  */
4046 Roo.SplitBar.LEFT = 1;
4047
4048 /**
4049  * Placement constant - The resizing element is to the right of the splitter element
4050  * @static
4051  * @type Number
4052  */
4053 Roo.SplitBar.RIGHT = 2;
4054
4055 /**
4056  * Placement constant - The resizing element is positioned above the splitter element
4057  * @static
4058  * @type Number
4059  */
4060 Roo.SplitBar.TOP = 3;
4061
4062 /**
4063  * Placement constant - The resizing element is positioned under splitter element
4064  * @static
4065  * @type Number
4066  */
4067 Roo.SplitBar.BOTTOM = 4;
4068 /*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079 /**
4080  * @class Roo.View
4081  * @extends Roo.util.Observable
4082  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4083  * This class also supports single and multi selection modes. <br>
4084  * Create a data model bound view:
4085  <pre><code>
4086  var store = new Roo.data.Store(...);
4087
4088  var view = new Roo.View({
4089     el : "my-element",
4090     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4091  
4092     singleSelect: true,
4093     selectedClass: "ydataview-selected",
4094     store: store
4095  });
4096
4097  // listen for node click?
4098  view.on("click", function(vw, index, node, e){
4099  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4100  });
4101
4102  // load XML data
4103  dataModel.load("foobar.xml");
4104  </code></pre>
4105  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4106  * <br><br>
4107  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4108  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4109  * 
4110  * Note: old style constructor is still suported (container, template, config)
4111  * 
4112  * @constructor
4113  * Create a new View
4114  * @param {Object} config The config object
4115  * 
4116  */
4117 Roo.View = function(config, depreciated_tpl, depreciated_config){
4118     
4119     this.parent = false;
4120     
4121     if (typeof(depreciated_tpl) == 'undefined') {
4122         // new way.. - universal constructor.
4123         Roo.apply(this, config);
4124         this.el  = Roo.get(this.el);
4125     } else {
4126         // old format..
4127         this.el  = Roo.get(config);
4128         this.tpl = depreciated_tpl;
4129         Roo.apply(this, depreciated_config);
4130     }
4131     this.wrapEl  = this.el.wrap().wrap();
4132     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4133     
4134     
4135     if(typeof(this.tpl) == "string"){
4136         this.tpl = new Roo.Template(this.tpl);
4137     } else {
4138         // support xtype ctors..
4139         this.tpl = new Roo.factory(this.tpl, Roo);
4140     }
4141     
4142     
4143     this.tpl.compile();
4144     
4145     /** @private */
4146     this.addEvents({
4147         /**
4148          * @event beforeclick
4149          * Fires before a click is processed. Returns false to cancel the default action.
4150          * @param {Roo.View} this
4151          * @param {Number} index The index of the target node
4152          * @param {HTMLElement} node The target node
4153          * @param {Roo.EventObject} e The raw event object
4154          */
4155             "beforeclick" : true,
4156         /**
4157          * @event click
4158          * Fires when a template node is clicked.
4159          * @param {Roo.View} this
4160          * @param {Number} index The index of the target node
4161          * @param {HTMLElement} node The target node
4162          * @param {Roo.EventObject} e The raw event object
4163          */
4164             "click" : true,
4165         /**
4166          * @event dblclick
4167          * Fires when a template node is double clicked.
4168          * @param {Roo.View} this
4169          * @param {Number} index The index of the target node
4170          * @param {HTMLElement} node The target node
4171          * @param {Roo.EventObject} e The raw event object
4172          */
4173             "dblclick" : true,
4174         /**
4175          * @event contextmenu
4176          * Fires when a template node is right clicked.
4177          * @param {Roo.View} this
4178          * @param {Number} index The index of the target node
4179          * @param {HTMLElement} node The target node
4180          * @param {Roo.EventObject} e The raw event object
4181          */
4182             "contextmenu" : true,
4183         /**
4184          * @event selectionchange
4185          * Fires when the selected nodes change.
4186          * @param {Roo.View} this
4187          * @param {Array} selections Array of the selected nodes
4188          */
4189             "selectionchange" : true,
4190     
4191         /**
4192          * @event beforeselect
4193          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4194          * @param {Roo.View} this
4195          * @param {HTMLElement} node The node to be selected
4196          * @param {Array} selections Array of currently selected nodes
4197          */
4198             "beforeselect" : true,
4199         /**
4200          * @event preparedata
4201          * Fires on every row to render, to allow you to change the data.
4202          * @param {Roo.View} this
4203          * @param {Object} data to be rendered (change this)
4204          */
4205           "preparedata" : true
4206           
4207           
4208         });
4209
4210
4211
4212     this.el.on({
4213         "click": this.onClick,
4214         "dblclick": this.onDblClick,
4215         "contextmenu": this.onContextMenu,
4216         scope:this
4217     });
4218
4219     this.selections = [];
4220     this.nodes = [];
4221     this.cmp = new Roo.CompositeElementLite([]);
4222     if(this.store){
4223         this.store = Roo.factory(this.store, Roo.data);
4224         this.setStore(this.store, true);
4225     }
4226     
4227     if ( this.footer && this.footer.xtype) {
4228            
4229          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4230         
4231         this.footer.dataSource = this.store;
4232         this.footer.container = fctr;
4233         this.footer = Roo.factory(this.footer, Roo);
4234         fctr.insertFirst(this.el);
4235         
4236         // this is a bit insane - as the paging toolbar seems to detach the el..
4237 //        dom.parentNode.parentNode.parentNode
4238          // they get detached?
4239     }
4240     
4241     
4242     Roo.View.superclass.constructor.call(this);
4243     
4244     
4245 };
4246
4247 Roo.extend(Roo.View, Roo.util.Observable, {
4248     
4249      /**
4250      * @cfg {Roo.data.Store} store Data store to load data from.
4251      */
4252     store : false,
4253     
4254     /**
4255      * @cfg {String|Roo.Element} el The container element.
4256      */
4257     el : '',
4258     
4259     /**
4260      * @cfg {String|Roo.Template} tpl The template used by this View 
4261      */
4262     tpl : false,
4263     /**
4264      * @cfg {String} dataName the named area of the template to use as the data area
4265      *                          Works with domtemplates roo-name="name"
4266      */
4267     dataName: false,
4268     /**
4269      * @cfg {String} selectedClass The css class to add to selected nodes
4270      */
4271     selectedClass : "x-view-selected",
4272      /**
4273      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4274      */
4275     emptyText : "",
4276     
4277     /**
4278      * @cfg {String} text to display on mask (default Loading)
4279      */
4280     mask : false,
4281     /**
4282      * @cfg {Boolean} multiSelect Allow multiple selection
4283      */
4284     multiSelect : false,
4285     /**
4286      * @cfg {Boolean} singleSelect Allow single selection
4287      */
4288     singleSelect:  false,
4289     
4290     /**
4291      * @cfg {Boolean} toggleSelect - selecting 
4292      */
4293     toggleSelect : false,
4294     
4295     /**
4296      * @cfg {Boolean} tickable - selecting 
4297      */
4298     tickable : false,
4299     
4300     /**
4301      * Returns the element this view is bound to.
4302      * @return {Roo.Element}
4303      */
4304     getEl : function(){
4305         return this.wrapEl;
4306     },
4307     
4308     
4309
4310     /**
4311      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4312      */
4313     refresh : function(){
4314         //Roo.log('refresh');
4315         var t = this.tpl;
4316         
4317         // if we are using something like 'domtemplate', then
4318         // the what gets used is:
4319         // t.applySubtemplate(NAME, data, wrapping data..)
4320         // the outer template then get' applied with
4321         //     the store 'extra data'
4322         // and the body get's added to the
4323         //      roo-name="data" node?
4324         //      <span class='roo-tpl-{name}'></span> ?????
4325         
4326         
4327         
4328         this.clearSelections();
4329         this.el.update("");
4330         var html = [];
4331         var records = this.store.getRange();
4332         if(records.length < 1) {
4333             
4334             // is this valid??  = should it render a template??
4335             
4336             this.el.update(this.emptyText);
4337             return;
4338         }
4339         var el = this.el;
4340         if (this.dataName) {
4341             this.el.update(t.apply(this.store.meta)); //????
4342             el = this.el.child('.roo-tpl-' + this.dataName);
4343         }
4344         
4345         for(var i = 0, len = records.length; i < len; i++){
4346             var data = this.prepareData(records[i].data, i, records[i]);
4347             this.fireEvent("preparedata", this, data, i, records[i]);
4348             
4349             var d = Roo.apply({}, data);
4350             
4351             if(this.tickable){
4352                 Roo.apply(d, {'roo-id' : Roo.id()});
4353                 
4354                 var _this = this;
4355             
4356                 Roo.each(this.parent.item, function(item){
4357                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4358                         return;
4359                     }
4360                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4361                 });
4362             }
4363             
4364             html[html.length] = Roo.util.Format.trim(
4365                 this.dataName ?
4366                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4367                     t.apply(d)
4368             );
4369         }
4370         
4371         
4372         
4373         el.update(html.join(""));
4374         this.nodes = el.dom.childNodes;
4375         this.updateIndexes(0);
4376     },
4377     
4378
4379     /**
4380      * Function to override to reformat the data that is sent to
4381      * the template for each node.
4382      * DEPRICATED - use the preparedata event handler.
4383      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4384      * a JSON object for an UpdateManager bound view).
4385      */
4386     prepareData : function(data, index, record)
4387     {
4388         this.fireEvent("preparedata", this, data, index, record);
4389         return data;
4390     },
4391
4392     onUpdate : function(ds, record){
4393         // Roo.log('on update');   
4394         this.clearSelections();
4395         var index = this.store.indexOf(record);
4396         var n = this.nodes[index];
4397         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4398         n.parentNode.removeChild(n);
4399         this.updateIndexes(index, index);
4400     },
4401
4402     
4403     
4404 // --------- FIXME     
4405     onAdd : function(ds, records, index)
4406     {
4407         //Roo.log(['on Add', ds, records, index] );        
4408         this.clearSelections();
4409         if(this.nodes.length == 0){
4410             this.refresh();
4411             return;
4412         }
4413         var n = this.nodes[index];
4414         for(var i = 0, len = records.length; i < len; i++){
4415             var d = this.prepareData(records[i].data, i, records[i]);
4416             if(n){
4417                 this.tpl.insertBefore(n, d);
4418             }else{
4419                 
4420                 this.tpl.append(this.el, d);
4421             }
4422         }
4423         this.updateIndexes(index);
4424     },
4425
4426     onRemove : function(ds, record, index){
4427        // Roo.log('onRemove');
4428         this.clearSelections();
4429         var el = this.dataName  ?
4430             this.el.child('.roo-tpl-' + this.dataName) :
4431             this.el; 
4432         
4433         el.dom.removeChild(this.nodes[index]);
4434         this.updateIndexes(index);
4435     },
4436
4437     /**
4438      * Refresh an individual node.
4439      * @param {Number} index
4440      */
4441     refreshNode : function(index){
4442         this.onUpdate(this.store, this.store.getAt(index));
4443     },
4444
4445     updateIndexes : function(startIndex, endIndex){
4446         var ns = this.nodes;
4447         startIndex = startIndex || 0;
4448         endIndex = endIndex || ns.length - 1;
4449         for(var i = startIndex; i <= endIndex; i++){
4450             ns[i].nodeIndex = i;
4451         }
4452     },
4453
4454     /**
4455      * Changes the data store this view uses and refresh the view.
4456      * @param {Store} store
4457      */
4458     setStore : function(store, initial){
4459         if(!initial && this.store){
4460             this.store.un("datachanged", this.refresh);
4461             this.store.un("add", this.onAdd);
4462             this.store.un("remove", this.onRemove);
4463             this.store.un("update", this.onUpdate);
4464             this.store.un("clear", this.refresh);
4465             this.store.un("beforeload", this.onBeforeLoad);
4466             this.store.un("load", this.onLoad);
4467             this.store.un("loadexception", this.onLoad);
4468         }
4469         if(store){
4470           
4471             store.on("datachanged", this.refresh, this);
4472             store.on("add", this.onAdd, this);
4473             store.on("remove", this.onRemove, this);
4474             store.on("update", this.onUpdate, this);
4475             store.on("clear", this.refresh, this);
4476             store.on("beforeload", this.onBeforeLoad, this);
4477             store.on("load", this.onLoad, this);
4478             store.on("loadexception", this.onLoad, this);
4479         }
4480         
4481         if(store){
4482             this.refresh();
4483         }
4484     },
4485     /**
4486      * onbeforeLoad - masks the loading area.
4487      *
4488      */
4489     onBeforeLoad : function(store,opts)
4490     {
4491          //Roo.log('onBeforeLoad');   
4492         if (!opts.add) {
4493             this.el.update("");
4494         }
4495         this.el.mask(this.mask ? this.mask : "Loading" ); 
4496     },
4497     onLoad : function ()
4498     {
4499         this.el.unmask();
4500     },
4501     
4502
4503     /**
4504      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4505      * @param {HTMLElement} node
4506      * @return {HTMLElement} The template node
4507      */
4508     findItemFromChild : function(node){
4509         var el = this.dataName  ?
4510             this.el.child('.roo-tpl-' + this.dataName,true) :
4511             this.el.dom; 
4512         
4513         if(!node || node.parentNode == el){
4514                     return node;
4515             }
4516             var p = node.parentNode;
4517             while(p && p != el){
4518             if(p.parentNode == el){
4519                 return p;
4520             }
4521             p = p.parentNode;
4522         }
4523             return null;
4524     },
4525
4526     /** @ignore */
4527     onClick : function(e){
4528         var item = this.findItemFromChild(e.getTarget());
4529         if(item){
4530             var index = this.indexOf(item);
4531             if(this.onItemClick(item, index, e) !== false){
4532                 this.fireEvent("click", this, index, item, e);
4533             }
4534         }else{
4535             this.clearSelections();
4536         }
4537     },
4538
4539     /** @ignore */
4540     onContextMenu : function(e){
4541         var item = this.findItemFromChild(e.getTarget());
4542         if(item){
4543             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4544         }
4545     },
4546
4547     /** @ignore */
4548     onDblClick : function(e){
4549         var item = this.findItemFromChild(e.getTarget());
4550         if(item){
4551             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4552         }
4553     },
4554
4555     onItemClick : function(item, index, e)
4556     {
4557         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4558             return false;
4559         }
4560         if (this.toggleSelect) {
4561             var m = this.isSelected(item) ? 'unselect' : 'select';
4562             //Roo.log(m);
4563             var _t = this;
4564             _t[m](item, true, false);
4565             return true;
4566         }
4567         if(this.multiSelect || this.singleSelect){
4568             if(this.multiSelect && e.shiftKey && this.lastSelection){
4569                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4570             }else{
4571                 this.select(item, this.multiSelect && e.ctrlKey);
4572                 this.lastSelection = item;
4573             }
4574             
4575             if(!this.tickable){
4576                 e.preventDefault();
4577             }
4578             
4579         }
4580         return true;
4581     },
4582
4583     /**
4584      * Get the number of selected nodes.
4585      * @return {Number}
4586      */
4587     getSelectionCount : function(){
4588         return this.selections.length;
4589     },
4590
4591     /**
4592      * Get the currently selected nodes.
4593      * @return {Array} An array of HTMLElements
4594      */
4595     getSelectedNodes : function(){
4596         return this.selections;
4597     },
4598
4599     /**
4600      * Get the indexes of the selected nodes.
4601      * @return {Array}
4602      */
4603     getSelectedIndexes : function(){
4604         var indexes = [], s = this.selections;
4605         for(var i = 0, len = s.length; i < len; i++){
4606             indexes.push(s[i].nodeIndex);
4607         }
4608         return indexes;
4609     },
4610
4611     /**
4612      * Clear all selections
4613      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4614      */
4615     clearSelections : function(suppressEvent){
4616         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4617             this.cmp.elements = this.selections;
4618             this.cmp.removeClass(this.selectedClass);
4619             this.selections = [];
4620             if(!suppressEvent){
4621                 this.fireEvent("selectionchange", this, this.selections);
4622             }
4623         }
4624     },
4625
4626     /**
4627      * Returns true if the passed node is selected
4628      * @param {HTMLElement/Number} node The node or node index
4629      * @return {Boolean}
4630      */
4631     isSelected : function(node){
4632         var s = this.selections;
4633         if(s.length < 1){
4634             return false;
4635         }
4636         node = this.getNode(node);
4637         return s.indexOf(node) !== -1;
4638     },
4639
4640     /**
4641      * Selects nodes.
4642      * @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
4643      * @param {Boolean} keepExisting (optional) true to keep existing selections
4644      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4645      */
4646     select : function(nodeInfo, keepExisting, suppressEvent){
4647         if(nodeInfo instanceof Array){
4648             if(!keepExisting){
4649                 this.clearSelections(true);
4650             }
4651             for(var i = 0, len = nodeInfo.length; i < len; i++){
4652                 this.select(nodeInfo[i], true, true);
4653             }
4654             return;
4655         } 
4656         var node = this.getNode(nodeInfo);
4657         if(!node || this.isSelected(node)){
4658             return; // already selected.
4659         }
4660         if(!keepExisting){
4661             this.clearSelections(true);
4662         }
4663         
4664         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4665             Roo.fly(node).addClass(this.selectedClass);
4666             this.selections.push(node);
4667             if(!suppressEvent){
4668                 this.fireEvent("selectionchange", this, this.selections);
4669             }
4670         }
4671         
4672         
4673     },
4674       /**
4675      * Unselects nodes.
4676      * @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
4677      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4678      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4679      */
4680     unselect : function(nodeInfo, keepExisting, suppressEvent)
4681     {
4682         if(nodeInfo instanceof Array){
4683             Roo.each(this.selections, function(s) {
4684                 this.unselect(s, nodeInfo);
4685             }, this);
4686             return;
4687         }
4688         var node = this.getNode(nodeInfo);
4689         if(!node || !this.isSelected(node)){
4690             //Roo.log("not selected");
4691             return; // not selected.
4692         }
4693         // fireevent???
4694         var ns = [];
4695         Roo.each(this.selections, function(s) {
4696             if (s == node ) {
4697                 Roo.fly(node).removeClass(this.selectedClass);
4698
4699                 return;
4700             }
4701             ns.push(s);
4702         },this);
4703         
4704         this.selections= ns;
4705         this.fireEvent("selectionchange", this, this.selections);
4706     },
4707
4708     /**
4709      * Gets a template node.
4710      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4711      * @return {HTMLElement} The node or null if it wasn't found
4712      */
4713     getNode : function(nodeInfo){
4714         if(typeof nodeInfo == "string"){
4715             return document.getElementById(nodeInfo);
4716         }else if(typeof nodeInfo == "number"){
4717             return this.nodes[nodeInfo];
4718         }
4719         return nodeInfo;
4720     },
4721
4722     /**
4723      * Gets a range template nodes.
4724      * @param {Number} startIndex
4725      * @param {Number} endIndex
4726      * @return {Array} An array of nodes
4727      */
4728     getNodes : function(start, end){
4729         var ns = this.nodes;
4730         start = start || 0;
4731         end = typeof end == "undefined" ? ns.length - 1 : end;
4732         var nodes = [];
4733         if(start <= end){
4734             for(var i = start; i <= end; i++){
4735                 nodes.push(ns[i]);
4736             }
4737         } else{
4738             for(var i = start; i >= end; i--){
4739                 nodes.push(ns[i]);
4740             }
4741         }
4742         return nodes;
4743     },
4744
4745     /**
4746      * Finds the index of the passed node
4747      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4748      * @return {Number} The index of the node or -1
4749      */
4750     indexOf : function(node){
4751         node = this.getNode(node);
4752         if(typeof node.nodeIndex == "number"){
4753             return node.nodeIndex;
4754         }
4755         var ns = this.nodes;
4756         for(var i = 0, len = ns.length; i < len; i++){
4757             if(ns[i] == node){
4758                 return i;
4759             }
4760         }
4761         return -1;
4762     }
4763 });
4764 /*
4765  * Based on:
4766  * Ext JS Library 1.1.1
4767  * Copyright(c) 2006-2007, Ext JS, LLC.
4768  *
4769  * Originally Released Under LGPL - original licence link has changed is not relivant.
4770  *
4771  * Fork - LGPL
4772  * <script type="text/javascript">
4773  */
4774
4775 /**
4776  * @class Roo.JsonView
4777  * @extends Roo.View
4778  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4779 <pre><code>
4780 var view = new Roo.JsonView({
4781     container: "my-element",
4782     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4783     multiSelect: true, 
4784     jsonRoot: "data" 
4785 });
4786
4787 // listen for node click?
4788 view.on("click", function(vw, index, node, e){
4789     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4790 });
4791
4792 // direct load of JSON data
4793 view.load("foobar.php");
4794
4795 // Example from my blog list
4796 var tpl = new Roo.Template(
4797     '&lt;div class="entry"&gt;' +
4798     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4799     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4800     "&lt;/div&gt;&lt;hr /&gt;"
4801 );
4802
4803 var moreView = new Roo.JsonView({
4804     container :  "entry-list", 
4805     template : tpl,
4806     jsonRoot: "posts"
4807 });
4808 moreView.on("beforerender", this.sortEntries, this);
4809 moreView.load({
4810     url: "/blog/get-posts.php",
4811     params: "allposts=true",
4812     text: "Loading Blog Entries..."
4813 });
4814 </code></pre>
4815
4816 * Note: old code is supported with arguments : (container, template, config)
4817
4818
4819  * @constructor
4820  * Create a new JsonView
4821  * 
4822  * @param {Object} config The config object
4823  * 
4824  */
4825 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4826     
4827     
4828     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4829
4830     var um = this.el.getUpdateManager();
4831     um.setRenderer(this);
4832     um.on("update", this.onLoad, this);
4833     um.on("failure", this.onLoadException, this);
4834
4835     /**
4836      * @event beforerender
4837      * Fires before rendering of the downloaded JSON data.
4838      * @param {Roo.JsonView} this
4839      * @param {Object} data The JSON data loaded
4840      */
4841     /**
4842      * @event load
4843      * Fires when data is loaded.
4844      * @param {Roo.JsonView} this
4845      * @param {Object} data The JSON data loaded
4846      * @param {Object} response The raw Connect response object
4847      */
4848     /**
4849      * @event loadexception
4850      * Fires when loading fails.
4851      * @param {Roo.JsonView} this
4852      * @param {Object} response The raw Connect response object
4853      */
4854     this.addEvents({
4855         'beforerender' : true,
4856         'load' : true,
4857         'loadexception' : true
4858     });
4859 };
4860 Roo.extend(Roo.JsonView, Roo.View, {
4861     /**
4862      * @type {String} The root property in the loaded JSON object that contains the data
4863      */
4864     jsonRoot : "",
4865
4866     /**
4867      * Refreshes the view.
4868      */
4869     refresh : function(){
4870         this.clearSelections();
4871         this.el.update("");
4872         var html = [];
4873         var o = this.jsonData;
4874         if(o && o.length > 0){
4875             for(var i = 0, len = o.length; i < len; i++){
4876                 var data = this.prepareData(o[i], i, o);
4877                 html[html.length] = this.tpl.apply(data);
4878             }
4879         }else{
4880             html.push(this.emptyText);
4881         }
4882         this.el.update(html.join(""));
4883         this.nodes = this.el.dom.childNodes;
4884         this.updateIndexes(0);
4885     },
4886
4887     /**
4888      * 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.
4889      * @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:
4890      <pre><code>
4891      view.load({
4892          url: "your-url.php",
4893          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4894          callback: yourFunction,
4895          scope: yourObject, //(optional scope)
4896          discardUrl: false,
4897          nocache: false,
4898          text: "Loading...",
4899          timeout: 30,
4900          scripts: false
4901      });
4902      </code></pre>
4903      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4904      * 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.
4905      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4906      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4907      * @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.
4908      */
4909     load : function(){
4910         var um = this.el.getUpdateManager();
4911         um.update.apply(um, arguments);
4912     },
4913
4914     // note - render is a standard framework call...
4915     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4916     render : function(el, response){
4917         
4918         this.clearSelections();
4919         this.el.update("");
4920         var o;
4921         try{
4922             if (response != '') {
4923                 o = Roo.util.JSON.decode(response.responseText);
4924                 if(this.jsonRoot){
4925                     
4926                     o = o[this.jsonRoot];
4927                 }
4928             }
4929         } catch(e){
4930         }
4931         /**
4932          * The current JSON data or null
4933          */
4934         this.jsonData = o;
4935         this.beforeRender();
4936         this.refresh();
4937     },
4938
4939 /**
4940  * Get the number of records in the current JSON dataset
4941  * @return {Number}
4942  */
4943     getCount : function(){
4944         return this.jsonData ? this.jsonData.length : 0;
4945     },
4946
4947 /**
4948  * Returns the JSON object for the specified node(s)
4949  * @param {HTMLElement/Array} node The node or an array of nodes
4950  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4951  * you get the JSON object for the node
4952  */
4953     getNodeData : function(node){
4954         if(node instanceof Array){
4955             var data = [];
4956             for(var i = 0, len = node.length; i < len; i++){
4957                 data.push(this.getNodeData(node[i]));
4958             }
4959             return data;
4960         }
4961         return this.jsonData[this.indexOf(node)] || null;
4962     },
4963
4964     beforeRender : function(){
4965         this.snapshot = this.jsonData;
4966         if(this.sortInfo){
4967             this.sort.apply(this, this.sortInfo);
4968         }
4969         this.fireEvent("beforerender", this, this.jsonData);
4970     },
4971
4972     onLoad : function(el, o){
4973         this.fireEvent("load", this, this.jsonData, o);
4974     },
4975
4976     onLoadException : function(el, o){
4977         this.fireEvent("loadexception", this, o);
4978     },
4979
4980 /**
4981  * Filter the data by a specific property.
4982  * @param {String} property A property on your JSON objects
4983  * @param {String/RegExp} value Either string that the property values
4984  * should start with, or a RegExp to test against the property
4985  */
4986     filter : function(property, value){
4987         if(this.jsonData){
4988             var data = [];
4989             var ss = this.snapshot;
4990             if(typeof value == "string"){
4991                 var vlen = value.length;
4992                 if(vlen == 0){
4993                     this.clearFilter();
4994                     return;
4995                 }
4996                 value = value.toLowerCase();
4997                 for(var i = 0, len = ss.length; i < len; i++){
4998                     var o = ss[i];
4999                     if(o[property].substr(0, vlen).toLowerCase() == value){
5000                         data.push(o);
5001                     }
5002                 }
5003             } else if(value.exec){ // regex?
5004                 for(var i = 0, len = ss.length; i < len; i++){
5005                     var o = ss[i];
5006                     if(value.test(o[property])){
5007                         data.push(o);
5008                     }
5009                 }
5010             } else{
5011                 return;
5012             }
5013             this.jsonData = data;
5014             this.refresh();
5015         }
5016     },
5017
5018 /**
5019  * Filter by a function. The passed function will be called with each
5020  * object in the current dataset. If the function returns true the value is kept,
5021  * otherwise it is filtered.
5022  * @param {Function} fn
5023  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5024  */
5025     filterBy : function(fn, scope){
5026         if(this.jsonData){
5027             var data = [];
5028             var ss = this.snapshot;
5029             for(var i = 0, len = ss.length; i < len; i++){
5030                 var o = ss[i];
5031                 if(fn.call(scope || this, o)){
5032                     data.push(o);
5033                 }
5034             }
5035             this.jsonData = data;
5036             this.refresh();
5037         }
5038     },
5039
5040 /**
5041  * Clears the current filter.
5042  */
5043     clearFilter : function(){
5044         if(this.snapshot && this.jsonData != this.snapshot){
5045             this.jsonData = this.snapshot;
5046             this.refresh();
5047         }
5048     },
5049
5050
5051 /**
5052  * Sorts the data for this view and refreshes it.
5053  * @param {String} property A property on your JSON objects to sort on
5054  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5055  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5056  */
5057     sort : function(property, dir, sortType){
5058         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5059         if(this.jsonData){
5060             var p = property;
5061             var dsc = dir && dir.toLowerCase() == "desc";
5062             var f = function(o1, o2){
5063                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5064                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5065                 ;
5066                 if(v1 < v2){
5067                     return dsc ? +1 : -1;
5068                 } else if(v1 > v2){
5069                     return dsc ? -1 : +1;
5070                 } else{
5071                     return 0;
5072                 }
5073             };
5074             this.jsonData.sort(f);
5075             this.refresh();
5076             if(this.jsonData != this.snapshot){
5077                 this.snapshot.sort(f);
5078             }
5079         }
5080     }
5081 });/*
5082  * Based on:
5083  * Ext JS Library 1.1.1
5084  * Copyright(c) 2006-2007, Ext JS, LLC.
5085  *
5086  * Originally Released Under LGPL - original licence link has changed is not relivant.
5087  *
5088  * Fork - LGPL
5089  * <script type="text/javascript">
5090  */
5091  
5092
5093 /**
5094  * @class Roo.ColorPalette
5095  * @extends Roo.Component
5096  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5097  * Here's an example of typical usage:
5098  * <pre><code>
5099 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5100 cp.render('my-div');
5101
5102 cp.on('select', function(palette, selColor){
5103     // do something with selColor
5104 });
5105 </code></pre>
5106  * @constructor
5107  * Create a new ColorPalette
5108  * @param {Object} config The config object
5109  */
5110 Roo.ColorPalette = function(config){
5111     Roo.ColorPalette.superclass.constructor.call(this, config);
5112     this.addEvents({
5113         /**
5114              * @event select
5115              * Fires when a color is selected
5116              * @param {ColorPalette} this
5117              * @param {String} color The 6-digit color hex code (without the # symbol)
5118              */
5119         select: true
5120     });
5121
5122     if(this.handler){
5123         this.on("select", this.handler, this.scope, true);
5124     }
5125 };
5126 Roo.extend(Roo.ColorPalette, Roo.Component, {
5127     /**
5128      * @cfg {String} itemCls
5129      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5130      */
5131     itemCls : "x-color-palette",
5132     /**
5133      * @cfg {String} value
5134      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5135      * the hex codes are case-sensitive.
5136      */
5137     value : null,
5138     clickEvent:'click',
5139     // private
5140     ctype: "Roo.ColorPalette",
5141
5142     /**
5143      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5144      */
5145     allowReselect : false,
5146
5147     /**
5148      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5149      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5150      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5151      * of colors with the width setting until the box is symmetrical.</p>
5152      * <p>You can override individual colors if needed:</p>
5153      * <pre><code>
5154 var cp = new Roo.ColorPalette();
5155 cp.colors[0] = "FF0000";  // change the first box to red
5156 </code></pre>
5157
5158 Or you can provide a custom array of your own for complete control:
5159 <pre><code>
5160 var cp = new Roo.ColorPalette();
5161 cp.colors = ["000000", "993300", "333300"];
5162 </code></pre>
5163      * @type Array
5164      */
5165     colors : [
5166         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5167         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5168         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5169         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5170         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5171     ],
5172
5173     // private
5174     onRender : function(container, position){
5175         var t = new Roo.MasterTemplate(
5176             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5177         );
5178         var c = this.colors;
5179         for(var i = 0, len = c.length; i < len; i++){
5180             t.add([c[i]]);
5181         }
5182         var el = document.createElement("div");
5183         el.className = this.itemCls;
5184         t.overwrite(el);
5185         container.dom.insertBefore(el, position);
5186         this.el = Roo.get(el);
5187         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5188         if(this.clickEvent != 'click'){
5189             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5190         }
5191     },
5192
5193     // private
5194     afterRender : function(){
5195         Roo.ColorPalette.superclass.afterRender.call(this);
5196         if(this.value){
5197             var s = this.value;
5198             this.value = null;
5199             this.select(s);
5200         }
5201     },
5202
5203     // private
5204     handleClick : function(e, t){
5205         e.preventDefault();
5206         if(!this.disabled){
5207             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5208             this.select(c.toUpperCase());
5209         }
5210     },
5211
5212     /**
5213      * Selects the specified color in the palette (fires the select event)
5214      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5215      */
5216     select : function(color){
5217         color = color.replace("#", "");
5218         if(color != this.value || this.allowReselect){
5219             var el = this.el;
5220             if(this.value){
5221                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5222             }
5223             el.child("a.color-"+color).addClass("x-color-palette-sel");
5224             this.value = color;
5225             this.fireEvent("select", this, color);
5226         }
5227     }
5228 });/*
5229  * Based on:
5230  * Ext JS Library 1.1.1
5231  * Copyright(c) 2006-2007, Ext JS, LLC.
5232  *
5233  * Originally Released Under LGPL - original licence link has changed is not relivant.
5234  *
5235  * Fork - LGPL
5236  * <script type="text/javascript">
5237  */
5238  
5239 /**
5240  * @class Roo.DatePicker
5241  * @extends Roo.Component
5242  * Simple date picker class.
5243  * @constructor
5244  * Create a new DatePicker
5245  * @param {Object} config The config object
5246  */
5247 Roo.DatePicker = function(config){
5248     Roo.DatePicker.superclass.constructor.call(this, config);
5249
5250     this.value = config && config.value ?
5251                  config.value.clearTime() : new Date().clearTime();
5252
5253     this.addEvents({
5254         /**
5255              * @event select
5256              * Fires when a date is selected
5257              * @param {DatePicker} this
5258              * @param {Date} date The selected date
5259              */
5260         'select': true,
5261         /**
5262              * @event monthchange
5263              * Fires when the displayed month changes 
5264              * @param {DatePicker} this
5265              * @param {Date} date The selected month
5266              */
5267         'monthchange': true
5268     });
5269
5270     if(this.handler){
5271         this.on("select", this.handler,  this.scope || this);
5272     }
5273     // build the disabledDatesRE
5274     if(!this.disabledDatesRE && this.disabledDates){
5275         var dd = this.disabledDates;
5276         var re = "(?:";
5277         for(var i = 0; i < dd.length; i++){
5278             re += dd[i];
5279             if(i != dd.length-1) {
5280                 re += "|";
5281             }
5282         }
5283         this.disabledDatesRE = new RegExp(re + ")");
5284     }
5285 };
5286
5287 Roo.extend(Roo.DatePicker, Roo.Component, {
5288     /**
5289      * @cfg {String} todayText
5290      * The text to display on the button that selects the current date (defaults to "Today")
5291      */
5292     todayText : "Today",
5293     /**
5294      * @cfg {String} okText
5295      * The text to display on the ok button
5296      */
5297     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5298     /**
5299      * @cfg {String} cancelText
5300      * The text to display on the cancel button
5301      */
5302     cancelText : "Cancel",
5303     /**
5304      * @cfg {String} todayTip
5305      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5306      */
5307     todayTip : "{0} (Spacebar)",
5308     /**
5309      * @cfg {Date} minDate
5310      * Minimum allowable date (JavaScript date object, defaults to null)
5311      */
5312     minDate : null,
5313     /**
5314      * @cfg {Date} maxDate
5315      * Maximum allowable date (JavaScript date object, defaults to null)
5316      */
5317     maxDate : null,
5318     /**
5319      * @cfg {String} minText
5320      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5321      */
5322     minText : "This date is before the minimum date",
5323     /**
5324      * @cfg {String} maxText
5325      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5326      */
5327     maxText : "This date is after the maximum date",
5328     /**
5329      * @cfg {String} format
5330      * The default date format string which can be overriden for localization support.  The format must be
5331      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5332      */
5333     format : "m/d/y",
5334     /**
5335      * @cfg {Array} disabledDays
5336      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5337      */
5338     disabledDays : null,
5339     /**
5340      * @cfg {String} disabledDaysText
5341      * The tooltip to display when the date falls on a disabled day (defaults to "")
5342      */
5343     disabledDaysText : "",
5344     /**
5345      * @cfg {RegExp} disabledDatesRE
5346      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5347      */
5348     disabledDatesRE : null,
5349     /**
5350      * @cfg {String} disabledDatesText
5351      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5352      */
5353     disabledDatesText : "",
5354     /**
5355      * @cfg {Boolean} constrainToViewport
5356      * True to constrain the date picker to the viewport (defaults to true)
5357      */
5358     constrainToViewport : true,
5359     /**
5360      * @cfg {Array} monthNames
5361      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5362      */
5363     monthNames : Date.monthNames,
5364     /**
5365      * @cfg {Array} dayNames
5366      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5367      */
5368     dayNames : Date.dayNames,
5369     /**
5370      * @cfg {String} nextText
5371      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5372      */
5373     nextText: 'Next Month (Control+Right)',
5374     /**
5375      * @cfg {String} prevText
5376      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5377      */
5378     prevText: 'Previous Month (Control+Left)',
5379     /**
5380      * @cfg {String} monthYearText
5381      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5382      */
5383     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5384     /**
5385      * @cfg {Number} startDay
5386      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5387      */
5388     startDay : 0,
5389     /**
5390      * @cfg {Bool} showClear
5391      * Show a clear button (usefull for date form elements that can be blank.)
5392      */
5393     
5394     showClear: false,
5395     
5396     /**
5397      * Sets the value of the date field
5398      * @param {Date} value The date to set
5399      */
5400     setValue : function(value){
5401         var old = this.value;
5402         
5403         if (typeof(value) == 'string') {
5404          
5405             value = Date.parseDate(value, this.format);
5406         }
5407         if (!value) {
5408             value = new Date();
5409         }
5410         
5411         this.value = value.clearTime(true);
5412         if(this.el){
5413             this.update(this.value);
5414         }
5415     },
5416
5417     /**
5418      * Gets the current selected value of the date field
5419      * @return {Date} The selected date
5420      */
5421     getValue : function(){
5422         return this.value;
5423     },
5424
5425     // private
5426     focus : function(){
5427         if(this.el){
5428             this.update(this.activeDate);
5429         }
5430     },
5431
5432     // privateval
5433     onRender : function(container, position){
5434         
5435         var m = [
5436              '<table cellspacing="0">',
5437                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5438                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5439         var dn = this.dayNames;
5440         for(var i = 0; i < 7; i++){
5441             var d = this.startDay+i;
5442             if(d > 6){
5443                 d = d-7;
5444             }
5445             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5446         }
5447         m[m.length] = "</tr></thead><tbody><tr>";
5448         for(var i = 0; i < 42; i++) {
5449             if(i % 7 == 0 && i != 0){
5450                 m[m.length] = "</tr><tr>";
5451             }
5452             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5453         }
5454         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5455             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5456
5457         var el = document.createElement("div");
5458         el.className = "x-date-picker";
5459         el.innerHTML = m.join("");
5460
5461         container.dom.insertBefore(el, position);
5462
5463         this.el = Roo.get(el);
5464         this.eventEl = Roo.get(el.firstChild);
5465
5466         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5467             handler: this.showPrevMonth,
5468             scope: this,
5469             preventDefault:true,
5470             stopDefault:true
5471         });
5472
5473         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5474             handler: this.showNextMonth,
5475             scope: this,
5476             preventDefault:true,
5477             stopDefault:true
5478         });
5479
5480         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5481
5482         this.monthPicker = this.el.down('div.x-date-mp');
5483         this.monthPicker.enableDisplayMode('block');
5484         
5485         var kn = new Roo.KeyNav(this.eventEl, {
5486             "left" : function(e){
5487                 e.ctrlKey ?
5488                     this.showPrevMonth() :
5489                     this.update(this.activeDate.add("d", -1));
5490             },
5491
5492             "right" : function(e){
5493                 e.ctrlKey ?
5494                     this.showNextMonth() :
5495                     this.update(this.activeDate.add("d", 1));
5496             },
5497
5498             "up" : function(e){
5499                 e.ctrlKey ?
5500                     this.showNextYear() :
5501                     this.update(this.activeDate.add("d", -7));
5502             },
5503
5504             "down" : function(e){
5505                 e.ctrlKey ?
5506                     this.showPrevYear() :
5507                     this.update(this.activeDate.add("d", 7));
5508             },
5509
5510             "pageUp" : function(e){
5511                 this.showNextMonth();
5512             },
5513
5514             "pageDown" : function(e){
5515                 this.showPrevMonth();
5516             },
5517
5518             "enter" : function(e){
5519                 e.stopPropagation();
5520                 return true;
5521             },
5522
5523             scope : this
5524         });
5525
5526         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5527
5528         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5529
5530         this.el.unselectable();
5531         
5532         this.cells = this.el.select("table.x-date-inner tbody td");
5533         this.textNodes = this.el.query("table.x-date-inner tbody span");
5534
5535         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5536             text: "&#160;",
5537             tooltip: this.monthYearText
5538         });
5539
5540         this.mbtn.on('click', this.showMonthPicker, this);
5541         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5542
5543
5544         var today = (new Date()).dateFormat(this.format);
5545         
5546         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5547         if (this.showClear) {
5548             baseTb.add( new Roo.Toolbar.Fill());
5549         }
5550         baseTb.add({
5551             text: String.format(this.todayText, today),
5552             tooltip: String.format(this.todayTip, today),
5553             handler: this.selectToday,
5554             scope: this
5555         });
5556         
5557         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5558             
5559         //});
5560         if (this.showClear) {
5561             
5562             baseTb.add( new Roo.Toolbar.Fill());
5563             baseTb.add({
5564                 text: '&#160;',
5565                 cls: 'x-btn-icon x-btn-clear',
5566                 handler: function() {
5567                     //this.value = '';
5568                     this.fireEvent("select", this, '');
5569                 },
5570                 scope: this
5571             });
5572         }
5573         
5574         
5575         if(Roo.isIE){
5576             this.el.repaint();
5577         }
5578         this.update(this.value);
5579     },
5580
5581     createMonthPicker : function(){
5582         if(!this.monthPicker.dom.firstChild){
5583             var buf = ['<table border="0" cellspacing="0">'];
5584             for(var i = 0; i < 6; i++){
5585                 buf.push(
5586                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5587                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5588                     i == 0 ?
5589                     '<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>' :
5590                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5591                 );
5592             }
5593             buf.push(
5594                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5595                     this.okText,
5596                     '</button><button type="button" class="x-date-mp-cancel">',
5597                     this.cancelText,
5598                     '</button></td></tr>',
5599                 '</table>'
5600             );
5601             this.monthPicker.update(buf.join(''));
5602             this.monthPicker.on('click', this.onMonthClick, this);
5603             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5604
5605             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5606             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5607
5608             this.mpMonths.each(function(m, a, i){
5609                 i += 1;
5610                 if((i%2) == 0){
5611                     m.dom.xmonth = 5 + Math.round(i * .5);
5612                 }else{
5613                     m.dom.xmonth = Math.round((i-1) * .5);
5614                 }
5615             });
5616         }
5617     },
5618
5619     showMonthPicker : function(){
5620         this.createMonthPicker();
5621         var size = this.el.getSize();
5622         this.monthPicker.setSize(size);
5623         this.monthPicker.child('table').setSize(size);
5624
5625         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5626         this.updateMPMonth(this.mpSelMonth);
5627         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5628         this.updateMPYear(this.mpSelYear);
5629
5630         this.monthPicker.slideIn('t', {duration:.2});
5631     },
5632
5633     updateMPYear : function(y){
5634         this.mpyear = y;
5635         var ys = this.mpYears.elements;
5636         for(var i = 1; i <= 10; i++){
5637             var td = ys[i-1], y2;
5638             if((i%2) == 0){
5639                 y2 = y + Math.round(i * .5);
5640                 td.firstChild.innerHTML = y2;
5641                 td.xyear = y2;
5642             }else{
5643                 y2 = y - (5-Math.round(i * .5));
5644                 td.firstChild.innerHTML = y2;
5645                 td.xyear = y2;
5646             }
5647             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5648         }
5649     },
5650
5651     updateMPMonth : function(sm){
5652         this.mpMonths.each(function(m, a, i){
5653             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5654         });
5655     },
5656
5657     selectMPMonth: function(m){
5658         
5659     },
5660
5661     onMonthClick : function(e, t){
5662         e.stopEvent();
5663         var el = new Roo.Element(t), pn;
5664         if(el.is('button.x-date-mp-cancel')){
5665             this.hideMonthPicker();
5666         }
5667         else if(el.is('button.x-date-mp-ok')){
5668             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5669             this.hideMonthPicker();
5670         }
5671         else if(pn = el.up('td.x-date-mp-month', 2)){
5672             this.mpMonths.removeClass('x-date-mp-sel');
5673             pn.addClass('x-date-mp-sel');
5674             this.mpSelMonth = pn.dom.xmonth;
5675         }
5676         else if(pn = el.up('td.x-date-mp-year', 2)){
5677             this.mpYears.removeClass('x-date-mp-sel');
5678             pn.addClass('x-date-mp-sel');
5679             this.mpSelYear = pn.dom.xyear;
5680         }
5681         else if(el.is('a.x-date-mp-prev')){
5682             this.updateMPYear(this.mpyear-10);
5683         }
5684         else if(el.is('a.x-date-mp-next')){
5685             this.updateMPYear(this.mpyear+10);
5686         }
5687     },
5688
5689     onMonthDblClick : function(e, t){
5690         e.stopEvent();
5691         var el = new Roo.Element(t), pn;
5692         if(pn = el.up('td.x-date-mp-month', 2)){
5693             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5694             this.hideMonthPicker();
5695         }
5696         else if(pn = el.up('td.x-date-mp-year', 2)){
5697             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5698             this.hideMonthPicker();
5699         }
5700     },
5701
5702     hideMonthPicker : function(disableAnim){
5703         if(this.monthPicker){
5704             if(disableAnim === true){
5705                 this.monthPicker.hide();
5706             }else{
5707                 this.monthPicker.slideOut('t', {duration:.2});
5708             }
5709         }
5710     },
5711
5712     // private
5713     showPrevMonth : function(e){
5714         this.update(this.activeDate.add("mo", -1));
5715     },
5716
5717     // private
5718     showNextMonth : function(e){
5719         this.update(this.activeDate.add("mo", 1));
5720     },
5721
5722     // private
5723     showPrevYear : function(){
5724         this.update(this.activeDate.add("y", -1));
5725     },
5726
5727     // private
5728     showNextYear : function(){
5729         this.update(this.activeDate.add("y", 1));
5730     },
5731
5732     // private
5733     handleMouseWheel : function(e){
5734         var delta = e.getWheelDelta();
5735         if(delta > 0){
5736             this.showPrevMonth();
5737             e.stopEvent();
5738         } else if(delta < 0){
5739             this.showNextMonth();
5740             e.stopEvent();
5741         }
5742     },
5743
5744     // private
5745     handleDateClick : function(e, t){
5746         e.stopEvent();
5747         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5748             this.setValue(new Date(t.dateValue));
5749             this.fireEvent("select", this, this.value);
5750         }
5751     },
5752
5753     // private
5754     selectToday : function(){
5755         this.setValue(new Date().clearTime());
5756         this.fireEvent("select", this, this.value);
5757     },
5758
5759     // private
5760     update : function(date)
5761     {
5762         var vd = this.activeDate;
5763         this.activeDate = date;
5764         if(vd && this.el){
5765             var t = date.getTime();
5766             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5767                 this.cells.removeClass("x-date-selected");
5768                 this.cells.each(function(c){
5769                    if(c.dom.firstChild.dateValue == t){
5770                        c.addClass("x-date-selected");
5771                        setTimeout(function(){
5772                             try{c.dom.firstChild.focus();}catch(e){}
5773                        }, 50);
5774                        return false;
5775                    }
5776                 });
5777                 return;
5778             }
5779         }
5780         
5781         var days = date.getDaysInMonth();
5782         var firstOfMonth = date.getFirstDateOfMonth();
5783         var startingPos = firstOfMonth.getDay()-this.startDay;
5784
5785         if(startingPos <= this.startDay){
5786             startingPos += 7;
5787         }
5788
5789         var pm = date.add("mo", -1);
5790         var prevStart = pm.getDaysInMonth()-startingPos;
5791
5792         var cells = this.cells.elements;
5793         var textEls = this.textNodes;
5794         days += startingPos;
5795
5796         // convert everything to numbers so it's fast
5797         var day = 86400000;
5798         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5799         var today = new Date().clearTime().getTime();
5800         var sel = date.clearTime().getTime();
5801         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5802         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5803         var ddMatch = this.disabledDatesRE;
5804         var ddText = this.disabledDatesText;
5805         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5806         var ddaysText = this.disabledDaysText;
5807         var format = this.format;
5808
5809         var setCellClass = function(cal, cell){
5810             cell.title = "";
5811             var t = d.getTime();
5812             cell.firstChild.dateValue = t;
5813             if(t == today){
5814                 cell.className += " x-date-today";
5815                 cell.title = cal.todayText;
5816             }
5817             if(t == sel){
5818                 cell.className += " x-date-selected";
5819                 setTimeout(function(){
5820                     try{cell.firstChild.focus();}catch(e){}
5821                 }, 50);
5822             }
5823             // disabling
5824             if(t < min) {
5825                 cell.className = " x-date-disabled";
5826                 cell.title = cal.minText;
5827                 return;
5828             }
5829             if(t > max) {
5830                 cell.className = " x-date-disabled";
5831                 cell.title = cal.maxText;
5832                 return;
5833             }
5834             if(ddays){
5835                 if(ddays.indexOf(d.getDay()) != -1){
5836                     cell.title = ddaysText;
5837                     cell.className = " x-date-disabled";
5838                 }
5839             }
5840             if(ddMatch && format){
5841                 var fvalue = d.dateFormat(format);
5842                 if(ddMatch.test(fvalue)){
5843                     cell.title = ddText.replace("%0", fvalue);
5844                     cell.className = " x-date-disabled";
5845                 }
5846             }
5847         };
5848
5849         var i = 0;
5850         for(; i < startingPos; i++) {
5851             textEls[i].innerHTML = (++prevStart);
5852             d.setDate(d.getDate()+1);
5853             cells[i].className = "x-date-prevday";
5854             setCellClass(this, cells[i]);
5855         }
5856         for(; i < days; i++){
5857             intDay = i - startingPos + 1;
5858             textEls[i].innerHTML = (intDay);
5859             d.setDate(d.getDate()+1);
5860             cells[i].className = "x-date-active";
5861             setCellClass(this, cells[i]);
5862         }
5863         var extraDays = 0;
5864         for(; i < 42; i++) {
5865              textEls[i].innerHTML = (++extraDays);
5866              d.setDate(d.getDate()+1);
5867              cells[i].className = "x-date-nextday";
5868              setCellClass(this, cells[i]);
5869         }
5870
5871         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5872         this.fireEvent('monthchange', this, date);
5873         
5874         if(!this.internalRender){
5875             var main = this.el.dom.firstChild;
5876             var w = main.offsetWidth;
5877             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5878             Roo.fly(main).setWidth(w);
5879             this.internalRender = true;
5880             // opera does not respect the auto grow header center column
5881             // then, after it gets a width opera refuses to recalculate
5882             // without a second pass
5883             if(Roo.isOpera && !this.secondPass){
5884                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5885                 this.secondPass = true;
5886                 this.update.defer(10, this, [date]);
5887             }
5888         }
5889         
5890         
5891     }
5892 });        /*
5893  * Based on:
5894  * Ext JS Library 1.1.1
5895  * Copyright(c) 2006-2007, Ext JS, LLC.
5896  *
5897  * Originally Released Under LGPL - original licence link has changed is not relivant.
5898  *
5899  * Fork - LGPL
5900  * <script type="text/javascript">
5901  */
5902 /**
5903  * @class Roo.TabPanel
5904  * @extends Roo.util.Observable
5905  * A lightweight tab container.
5906  * <br><br>
5907  * Usage:
5908  * <pre><code>
5909 // basic tabs 1, built from existing content
5910 var tabs = new Roo.TabPanel("tabs1");
5911 tabs.addTab("script", "View Script");
5912 tabs.addTab("markup", "View Markup");
5913 tabs.activate("script");
5914
5915 // more advanced tabs, built from javascript
5916 var jtabs = new Roo.TabPanel("jtabs");
5917 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5918
5919 // set up the UpdateManager
5920 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5921 var updater = tab2.getUpdateManager();
5922 updater.setDefaultUrl("ajax1.htm");
5923 tab2.on('activate', updater.refresh, updater, true);
5924
5925 // Use setUrl for Ajax loading
5926 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5927 tab3.setUrl("ajax2.htm", null, true);
5928
5929 // Disabled tab
5930 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5931 tab4.disable();
5932
5933 jtabs.activate("jtabs-1");
5934  * </code></pre>
5935  * @constructor
5936  * Create a new TabPanel.
5937  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5938  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5939  */
5940 Roo.TabPanel = function(container, config){
5941     /**
5942     * The container element for this TabPanel.
5943     * @type Roo.Element
5944     */
5945     this.el = Roo.get(container, true);
5946     if(config){
5947         if(typeof config == "boolean"){
5948             this.tabPosition = config ? "bottom" : "top";
5949         }else{
5950             Roo.apply(this, config);
5951         }
5952     }
5953     if(this.tabPosition == "bottom"){
5954         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5955         this.el.addClass("x-tabs-bottom");
5956     }
5957     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5958     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5959     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5960     if(Roo.isIE){
5961         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5962     }
5963     if(this.tabPosition != "bottom"){
5964         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5965          * @type Roo.Element
5966          */
5967         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5968         this.el.addClass("x-tabs-top");
5969     }
5970     this.items = [];
5971
5972     this.bodyEl.setStyle("position", "relative");
5973
5974     this.active = null;
5975     this.activateDelegate = this.activate.createDelegate(this);
5976
5977     this.addEvents({
5978         /**
5979          * @event tabchange
5980          * Fires when the active tab changes
5981          * @param {Roo.TabPanel} this
5982          * @param {Roo.TabPanelItem} activePanel The new active tab
5983          */
5984         "tabchange": true,
5985         /**
5986          * @event beforetabchange
5987          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5988          * @param {Roo.TabPanel} this
5989          * @param {Object} e Set cancel to true on this object to cancel the tab change
5990          * @param {Roo.TabPanelItem} tab The tab being changed to
5991          */
5992         "beforetabchange" : true
5993     });
5994
5995     Roo.EventManager.onWindowResize(this.onResize, this);
5996     this.cpad = this.el.getPadding("lr");
5997     this.hiddenCount = 0;
5998
5999
6000     // toolbar on the tabbar support...
6001     if (this.toolbar) {
6002         var tcfg = this.toolbar;
6003         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
6004         this.toolbar = new Roo.Toolbar(tcfg);
6005         if (Roo.isSafari) {
6006             var tbl = tcfg.container.child('table', true);
6007             tbl.setAttribute('width', '100%');
6008         }
6009         
6010     }
6011    
6012
6013
6014     Roo.TabPanel.superclass.constructor.call(this);
6015 };
6016
6017 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
6018     /*
6019      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
6020      */
6021     tabPosition : "top",
6022     /*
6023      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6024      */
6025     currentTabWidth : 0,
6026     /*
6027      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6028      */
6029     minTabWidth : 40,
6030     /*
6031      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6032      */
6033     maxTabWidth : 250,
6034     /*
6035      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6036      */
6037     preferredTabWidth : 175,
6038     /*
6039      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6040      */
6041     resizeTabs : false,
6042     /*
6043      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6044      */
6045     monitorResize : true,
6046     /*
6047      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6048      */
6049     toolbar : false,
6050
6051     /**
6052      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6053      * @param {String} id The id of the div to use <b>or create</b>
6054      * @param {String} text The text for the tab
6055      * @param {String} content (optional) Content to put in the TabPanelItem body
6056      * @param {Boolean} closable (optional) True to create a close icon on the tab
6057      * @return {Roo.TabPanelItem} The created TabPanelItem
6058      */
6059     addTab : function(id, text, content, closable){
6060         var item = new Roo.TabPanelItem(this, id, text, closable);
6061         this.addTabItem(item);
6062         if(content){
6063             item.setContent(content);
6064         }
6065         return item;
6066     },
6067
6068     /**
6069      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6070      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6071      * @return {Roo.TabPanelItem}
6072      */
6073     getTab : function(id){
6074         return this.items[id];
6075     },
6076
6077     /**
6078      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6079      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6080      */
6081     hideTab : function(id){
6082         var t = this.items[id];
6083         if(!t.isHidden()){
6084            t.setHidden(true);
6085            this.hiddenCount++;
6086            this.autoSizeTabs();
6087         }
6088     },
6089
6090     /**
6091      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6092      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6093      */
6094     unhideTab : function(id){
6095         var t = this.items[id];
6096         if(t.isHidden()){
6097            t.setHidden(false);
6098            this.hiddenCount--;
6099            this.autoSizeTabs();
6100         }
6101     },
6102
6103     /**
6104      * Adds an existing {@link Roo.TabPanelItem}.
6105      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6106      */
6107     addTabItem : function(item){
6108         this.items[item.id] = item;
6109         this.items.push(item);
6110         if(this.resizeTabs){
6111            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6112            this.autoSizeTabs();
6113         }else{
6114             item.autoSize();
6115         }
6116     },
6117
6118     /**
6119      * Removes a {@link Roo.TabPanelItem}.
6120      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6121      */
6122     removeTab : function(id){
6123         var items = this.items;
6124         var tab = items[id];
6125         if(!tab) { return; }
6126         var index = items.indexOf(tab);
6127         if(this.active == tab && items.length > 1){
6128             var newTab = this.getNextAvailable(index);
6129             if(newTab) {
6130                 newTab.activate();
6131             }
6132         }
6133         this.stripEl.dom.removeChild(tab.pnode.dom);
6134         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6135             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6136         }
6137         items.splice(index, 1);
6138         delete this.items[tab.id];
6139         tab.fireEvent("close", tab);
6140         tab.purgeListeners();
6141         this.autoSizeTabs();
6142     },
6143
6144     getNextAvailable : function(start){
6145         var items = this.items;
6146         var index = start;
6147         // look for a next tab that will slide over to
6148         // replace the one being removed
6149         while(index < items.length){
6150             var item = items[++index];
6151             if(item && !item.isHidden()){
6152                 return item;
6153             }
6154         }
6155         // if one isn't found select the previous tab (on the left)
6156         index = start;
6157         while(index >= 0){
6158             var item = items[--index];
6159             if(item && !item.isHidden()){
6160                 return item;
6161             }
6162         }
6163         return null;
6164     },
6165
6166     /**
6167      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6168      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6169      */
6170     disableTab : function(id){
6171         var tab = this.items[id];
6172         if(tab && this.active != tab){
6173             tab.disable();
6174         }
6175     },
6176
6177     /**
6178      * Enables a {@link Roo.TabPanelItem} that is disabled.
6179      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6180      */
6181     enableTab : function(id){
6182         var tab = this.items[id];
6183         tab.enable();
6184     },
6185
6186     /**
6187      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6188      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6189      * @return {Roo.TabPanelItem} The TabPanelItem.
6190      */
6191     activate : function(id){
6192         var tab = this.items[id];
6193         if(!tab){
6194             return null;
6195         }
6196         if(tab == this.active || tab.disabled){
6197             return tab;
6198         }
6199         var e = {};
6200         this.fireEvent("beforetabchange", this, e, tab);
6201         if(e.cancel !== true && !tab.disabled){
6202             if(this.active){
6203                 this.active.hide();
6204             }
6205             this.active = this.items[id];
6206             this.active.show();
6207             this.fireEvent("tabchange", this, this.active);
6208         }
6209         return tab;
6210     },
6211
6212     /**
6213      * Gets the active {@link Roo.TabPanelItem}.
6214      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6215      */
6216     getActiveTab : function(){
6217         return this.active;
6218     },
6219
6220     /**
6221      * Updates the tab body element to fit the height of the container element
6222      * for overflow scrolling
6223      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6224      */
6225     syncHeight : function(targetHeight){
6226         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6227         var bm = this.bodyEl.getMargins();
6228         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6229         this.bodyEl.setHeight(newHeight);
6230         return newHeight;
6231     },
6232
6233     onResize : function(){
6234         if(this.monitorResize){
6235             this.autoSizeTabs();
6236         }
6237     },
6238
6239     /**
6240      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6241      */
6242     beginUpdate : function(){
6243         this.updating = true;
6244     },
6245
6246     /**
6247      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6248      */
6249     endUpdate : function(){
6250         this.updating = false;
6251         this.autoSizeTabs();
6252     },
6253
6254     /**
6255      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6256      */
6257     autoSizeTabs : function(){
6258         var count = this.items.length;
6259         var vcount = count - this.hiddenCount;
6260         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6261             return;
6262         }
6263         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6264         var availWidth = Math.floor(w / vcount);
6265         var b = this.stripBody;
6266         if(b.getWidth() > w){
6267             var tabs = this.items;
6268             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6269             if(availWidth < this.minTabWidth){
6270                 /*if(!this.sleft){    // incomplete scrolling code
6271                     this.createScrollButtons();
6272                 }
6273                 this.showScroll();
6274                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6275             }
6276         }else{
6277             if(this.currentTabWidth < this.preferredTabWidth){
6278                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6279             }
6280         }
6281     },
6282
6283     /**
6284      * Returns the number of tabs in this TabPanel.
6285      * @return {Number}
6286      */
6287      getCount : function(){
6288          return this.items.length;
6289      },
6290
6291     /**
6292      * Resizes all the tabs to the passed width
6293      * @param {Number} The new width
6294      */
6295     setTabWidth : function(width){
6296         this.currentTabWidth = width;
6297         for(var i = 0, len = this.items.length; i < len; i++) {
6298                 if(!this.items[i].isHidden()) {
6299                 this.items[i].setWidth(width);
6300             }
6301         }
6302     },
6303
6304     /**
6305      * Destroys this TabPanel
6306      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6307      */
6308     destroy : function(removeEl){
6309         Roo.EventManager.removeResizeListener(this.onResize, this);
6310         for(var i = 0, len = this.items.length; i < len; i++){
6311             this.items[i].purgeListeners();
6312         }
6313         if(removeEl === true){
6314             this.el.update("");
6315             this.el.remove();
6316         }
6317     }
6318 });
6319
6320 /**
6321  * @class Roo.TabPanelItem
6322  * @extends Roo.util.Observable
6323  * Represents an individual item (tab plus body) in a TabPanel.
6324  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6325  * @param {String} id The id of this TabPanelItem
6326  * @param {String} text The text for the tab of this TabPanelItem
6327  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6328  */
6329 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6330     /**
6331      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6332      * @type Roo.TabPanel
6333      */
6334     this.tabPanel = tabPanel;
6335     /**
6336      * The id for this TabPanelItem
6337      * @type String
6338      */
6339     this.id = id;
6340     /** @private */
6341     this.disabled = false;
6342     /** @private */
6343     this.text = text;
6344     /** @private */
6345     this.loaded = false;
6346     this.closable = closable;
6347
6348     /**
6349      * The body element for this TabPanelItem.
6350      * @type Roo.Element
6351      */
6352     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6353     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6354     this.bodyEl.setStyle("display", "block");
6355     this.bodyEl.setStyle("zoom", "1");
6356     this.hideAction();
6357
6358     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6359     /** @private */
6360     this.el = Roo.get(els.el, true);
6361     this.inner = Roo.get(els.inner, true);
6362     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6363     this.pnode = Roo.get(els.el.parentNode, true);
6364     this.el.on("mousedown", this.onTabMouseDown, this);
6365     this.el.on("click", this.onTabClick, this);
6366     /** @private */
6367     if(closable){
6368         var c = Roo.get(els.close, true);
6369         c.dom.title = this.closeText;
6370         c.addClassOnOver("close-over");
6371         c.on("click", this.closeClick, this);
6372      }
6373
6374     this.addEvents({
6375          /**
6376          * @event activate
6377          * Fires when this tab becomes the active tab.
6378          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6379          * @param {Roo.TabPanelItem} this
6380          */
6381         "activate": true,
6382         /**
6383          * @event beforeclose
6384          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6385          * @param {Roo.TabPanelItem} this
6386          * @param {Object} e Set cancel to true on this object to cancel the close.
6387          */
6388         "beforeclose": true,
6389         /**
6390          * @event close
6391          * Fires when this tab is closed.
6392          * @param {Roo.TabPanelItem} this
6393          */
6394          "close": true,
6395         /**
6396          * @event deactivate
6397          * Fires when this tab is no longer the active tab.
6398          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6399          * @param {Roo.TabPanelItem} this
6400          */
6401          "deactivate" : true
6402     });
6403     this.hidden = false;
6404
6405     Roo.TabPanelItem.superclass.constructor.call(this);
6406 };
6407
6408 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6409     purgeListeners : function(){
6410        Roo.util.Observable.prototype.purgeListeners.call(this);
6411        this.el.removeAllListeners();
6412     },
6413     /**
6414      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6415      */
6416     show : function(){
6417         this.pnode.addClass("on");
6418         this.showAction();
6419         if(Roo.isOpera){
6420             this.tabPanel.stripWrap.repaint();
6421         }
6422         this.fireEvent("activate", this.tabPanel, this);
6423     },
6424
6425     /**
6426      * Returns true if this tab is the active tab.
6427      * @return {Boolean}
6428      */
6429     isActive : function(){
6430         return this.tabPanel.getActiveTab() == this;
6431     },
6432
6433     /**
6434      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6435      */
6436     hide : function(){
6437         this.pnode.removeClass("on");
6438         this.hideAction();
6439         this.fireEvent("deactivate", this.tabPanel, this);
6440     },
6441
6442     hideAction : function(){
6443         this.bodyEl.hide();
6444         this.bodyEl.setStyle("position", "absolute");
6445         this.bodyEl.setLeft("-20000px");
6446         this.bodyEl.setTop("-20000px");
6447     },
6448
6449     showAction : function(){
6450         this.bodyEl.setStyle("position", "relative");
6451         this.bodyEl.setTop("");
6452         this.bodyEl.setLeft("");
6453         this.bodyEl.show();
6454     },
6455
6456     /**
6457      * Set the tooltip for the tab.
6458      * @param {String} tooltip The tab's tooltip
6459      */
6460     setTooltip : function(text){
6461         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6462             this.textEl.dom.qtip = text;
6463             this.textEl.dom.removeAttribute('title');
6464         }else{
6465             this.textEl.dom.title = text;
6466         }
6467     },
6468
6469     onTabClick : function(e){
6470         e.preventDefault();
6471         this.tabPanel.activate(this.id);
6472     },
6473
6474     onTabMouseDown : function(e){
6475         e.preventDefault();
6476         this.tabPanel.activate(this.id);
6477     },
6478
6479     getWidth : function(){
6480         return this.inner.getWidth();
6481     },
6482
6483     setWidth : function(width){
6484         var iwidth = width - this.pnode.getPadding("lr");
6485         this.inner.setWidth(iwidth);
6486         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6487         this.pnode.setWidth(width);
6488     },
6489
6490     /**
6491      * Show or hide the tab
6492      * @param {Boolean} hidden True to hide or false to show.
6493      */
6494     setHidden : function(hidden){
6495         this.hidden = hidden;
6496         this.pnode.setStyle("display", hidden ? "none" : "");
6497     },
6498
6499     /**
6500      * Returns true if this tab is "hidden"
6501      * @return {Boolean}
6502      */
6503     isHidden : function(){
6504         return this.hidden;
6505     },
6506
6507     /**
6508      * Returns the text for this tab
6509      * @return {String}
6510      */
6511     getText : function(){
6512         return this.text;
6513     },
6514
6515     autoSize : function(){
6516         //this.el.beginMeasure();
6517         this.textEl.setWidth(1);
6518         /*
6519          *  #2804 [new] Tabs in Roojs
6520          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6521          */
6522         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6523         //this.el.endMeasure();
6524     },
6525
6526     /**
6527      * Sets the text for the tab (Note: this also sets the tooltip text)
6528      * @param {String} text The tab's text and tooltip
6529      */
6530     setText : function(text){
6531         this.text = text;
6532         this.textEl.update(text);
6533         this.setTooltip(text);
6534         if(!this.tabPanel.resizeTabs){
6535             this.autoSize();
6536         }
6537     },
6538     /**
6539      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6540      */
6541     activate : function(){
6542         this.tabPanel.activate(this.id);
6543     },
6544
6545     /**
6546      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6547      */
6548     disable : function(){
6549         if(this.tabPanel.active != this){
6550             this.disabled = true;
6551             this.pnode.addClass("disabled");
6552         }
6553     },
6554
6555     /**
6556      * Enables this TabPanelItem if it was previously disabled.
6557      */
6558     enable : function(){
6559         this.disabled = false;
6560         this.pnode.removeClass("disabled");
6561     },
6562
6563     /**
6564      * Sets the content for this TabPanelItem.
6565      * @param {String} content The content
6566      * @param {Boolean} loadScripts true to look for and load scripts
6567      */
6568     setContent : function(content, loadScripts){
6569         this.bodyEl.update(content, loadScripts);
6570     },
6571
6572     /**
6573      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6574      * @return {Roo.UpdateManager} The UpdateManager
6575      */
6576     getUpdateManager : function(){
6577         return this.bodyEl.getUpdateManager();
6578     },
6579
6580     /**
6581      * Set a URL to be used to load the content for this TabPanelItem.
6582      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6583      * @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)
6584      * @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)
6585      * @return {Roo.UpdateManager} The UpdateManager
6586      */
6587     setUrl : function(url, params, loadOnce){
6588         if(this.refreshDelegate){
6589             this.un('activate', this.refreshDelegate);
6590         }
6591         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6592         this.on("activate", this.refreshDelegate);
6593         return this.bodyEl.getUpdateManager();
6594     },
6595
6596     /** @private */
6597     _handleRefresh : function(url, params, loadOnce){
6598         if(!loadOnce || !this.loaded){
6599             var updater = this.bodyEl.getUpdateManager();
6600             updater.update(url, params, this._setLoaded.createDelegate(this));
6601         }
6602     },
6603
6604     /**
6605      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6606      *   Will fail silently if the setUrl method has not been called.
6607      *   This does not activate the panel, just updates its content.
6608      */
6609     refresh : function(){
6610         if(this.refreshDelegate){
6611            this.loaded = false;
6612            this.refreshDelegate();
6613         }
6614     },
6615
6616     /** @private */
6617     _setLoaded : function(){
6618         this.loaded = true;
6619     },
6620
6621     /** @private */
6622     closeClick : function(e){
6623         var o = {};
6624         e.stopEvent();
6625         this.fireEvent("beforeclose", this, o);
6626         if(o.cancel !== true){
6627             this.tabPanel.removeTab(this.id);
6628         }
6629     },
6630     /**
6631      * The text displayed in the tooltip for the close icon.
6632      * @type String
6633      */
6634     closeText : "Close this tab"
6635 });
6636
6637 /** @private */
6638 Roo.TabPanel.prototype.createStrip = function(container){
6639     var strip = document.createElement("div");
6640     strip.className = "x-tabs-wrap";
6641     container.appendChild(strip);
6642     return strip;
6643 };
6644 /** @private */
6645 Roo.TabPanel.prototype.createStripList = function(strip){
6646     // div wrapper for retard IE
6647     // returns the "tr" element.
6648     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6649         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6650         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6651     return strip.firstChild.firstChild.firstChild.firstChild;
6652 };
6653 /** @private */
6654 Roo.TabPanel.prototype.createBody = function(container){
6655     var body = document.createElement("div");
6656     Roo.id(body, "tab-body");
6657     Roo.fly(body).addClass("x-tabs-body");
6658     container.appendChild(body);
6659     return body;
6660 };
6661 /** @private */
6662 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6663     var body = Roo.getDom(id);
6664     if(!body){
6665         body = document.createElement("div");
6666         body.id = id;
6667     }
6668     Roo.fly(body).addClass("x-tabs-item-body");
6669     bodyEl.insertBefore(body, bodyEl.firstChild);
6670     return body;
6671 };
6672 /** @private */
6673 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6674     var td = document.createElement("td");
6675     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6676     //stripEl.appendChild(td);
6677     if(closable){
6678         td.className = "x-tabs-closable";
6679         if(!this.closeTpl){
6680             this.closeTpl = new Roo.Template(
6681                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6682                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6683                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6684             );
6685         }
6686         var el = this.closeTpl.overwrite(td, {"text": text});
6687         var close = el.getElementsByTagName("div")[0];
6688         var inner = el.getElementsByTagName("em")[0];
6689         return {"el": el, "close": close, "inner": inner};
6690     } else {
6691         if(!this.tabTpl){
6692             this.tabTpl = new Roo.Template(
6693                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6694                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6695             );
6696         }
6697         var el = this.tabTpl.overwrite(td, {"text": text});
6698         var inner = el.getElementsByTagName("em")[0];
6699         return {"el": el, "inner": inner};
6700     }
6701 };/*
6702  * Based on:
6703  * Ext JS Library 1.1.1
6704  * Copyright(c) 2006-2007, Ext JS, LLC.
6705  *
6706  * Originally Released Under LGPL - original licence link has changed is not relivant.
6707  *
6708  * Fork - LGPL
6709  * <script type="text/javascript">
6710  */
6711
6712 /**
6713  * @class Roo.Button
6714  * @extends Roo.util.Observable
6715  * Simple Button class
6716  * @cfg {String} text The button text
6717  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6718  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6719  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6720  * @cfg {Object} scope The scope of the handler
6721  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6722  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6723  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6724  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6725  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6726  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6727    applies if enableToggle = true)
6728  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6729  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6730   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6731  * @constructor
6732  * Create a new button
6733  * @param {Object} config The config object
6734  */
6735 Roo.Button = function(renderTo, config)
6736 {
6737     if (!config) {
6738         config = renderTo;
6739         renderTo = config.renderTo || false;
6740     }
6741     
6742     Roo.apply(this, config);
6743     this.addEvents({
6744         /**
6745              * @event click
6746              * Fires when this button is clicked
6747              * @param {Button} this
6748              * @param {EventObject} e The click event
6749              */
6750             "click" : true,
6751         /**
6752              * @event toggle
6753              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6754              * @param {Button} this
6755              * @param {Boolean} pressed
6756              */
6757             "toggle" : true,
6758         /**
6759              * @event mouseover
6760              * Fires when the mouse hovers over the button
6761              * @param {Button} this
6762              * @param {Event} e The event object
6763              */
6764         'mouseover' : true,
6765         /**
6766              * @event mouseout
6767              * Fires when the mouse exits the button
6768              * @param {Button} this
6769              * @param {Event} e The event object
6770              */
6771         'mouseout': true,
6772          /**
6773              * @event render
6774              * Fires when the button is rendered
6775              * @param {Button} this
6776              */
6777         'render': true
6778     });
6779     if(this.menu){
6780         this.menu = Roo.menu.MenuMgr.get(this.menu);
6781     }
6782     // register listeners first!!  - so render can be captured..
6783     Roo.util.Observable.call(this);
6784     if(renderTo){
6785         this.render(renderTo);
6786     }
6787     
6788   
6789 };
6790
6791 Roo.extend(Roo.Button, Roo.util.Observable, {
6792     /**
6793      * 
6794      */
6795     
6796     /**
6797      * Read-only. True if this button is hidden
6798      * @type Boolean
6799      */
6800     hidden : false,
6801     /**
6802      * Read-only. True if this button is disabled
6803      * @type Boolean
6804      */
6805     disabled : false,
6806     /**
6807      * Read-only. True if this button is pressed (only if enableToggle = true)
6808      * @type Boolean
6809      */
6810     pressed : false,
6811
6812     /**
6813      * @cfg {Number} tabIndex 
6814      * The DOM tabIndex for this button (defaults to undefined)
6815      */
6816     tabIndex : undefined,
6817
6818     /**
6819      * @cfg {Boolean} enableToggle
6820      * True to enable pressed/not pressed toggling (defaults to false)
6821      */
6822     enableToggle: false,
6823     /**
6824      * @cfg {Mixed} menu
6825      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6826      */
6827     menu : undefined,
6828     /**
6829      * @cfg {String} menuAlign
6830      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6831      */
6832     menuAlign : "tl-bl?",
6833
6834     /**
6835      * @cfg {String} iconCls
6836      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6837      */
6838     iconCls : undefined,
6839     /**
6840      * @cfg {String} type
6841      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6842      */
6843     type : 'button',
6844
6845     // private
6846     menuClassTarget: 'tr',
6847
6848     /**
6849      * @cfg {String} clickEvent
6850      * The type of event to map to the button's event handler (defaults to 'click')
6851      */
6852     clickEvent : 'click',
6853
6854     /**
6855      * @cfg {Boolean} handleMouseEvents
6856      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6857      */
6858     handleMouseEvents : true,
6859
6860     /**
6861      * @cfg {String} tooltipType
6862      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6863      */
6864     tooltipType : 'qtip',
6865
6866     /**
6867      * @cfg {String} cls
6868      * A CSS class to apply to the button's main element.
6869      */
6870     
6871     /**
6872      * @cfg {Roo.Template} template (Optional)
6873      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6874      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6875      * require code modifications if required elements (e.g. a button) aren't present.
6876      */
6877
6878     // private
6879     render : function(renderTo){
6880         var btn;
6881         if(this.hideParent){
6882             this.parentEl = Roo.get(renderTo);
6883         }
6884         if(!this.dhconfig){
6885             if(!this.template){
6886                 if(!Roo.Button.buttonTemplate){
6887                     // hideous table template
6888                     Roo.Button.buttonTemplate = new Roo.Template(
6889                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6890                         '<td class="x-btn-left"><i>&#160;</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>&#160;</i></td>',
6891                         "</tr></tbody></table>");
6892                 }
6893                 this.template = Roo.Button.buttonTemplate;
6894             }
6895             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6896             var btnEl = btn.child("button:first");
6897             btnEl.on('focus', this.onFocus, this);
6898             btnEl.on('blur', this.onBlur, this);
6899             if(this.cls){
6900                 btn.addClass(this.cls);
6901             }
6902             if(this.icon){
6903                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6904             }
6905             if(this.iconCls){
6906                 btnEl.addClass(this.iconCls);
6907                 if(!this.cls){
6908                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6909                 }
6910             }
6911             if(this.tabIndex !== undefined){
6912                 btnEl.dom.tabIndex = this.tabIndex;
6913             }
6914             if(this.tooltip){
6915                 if(typeof this.tooltip == 'object'){
6916                     Roo.QuickTips.tips(Roo.apply({
6917                           target: btnEl.id
6918                     }, this.tooltip));
6919                 } else {
6920                     btnEl.dom[this.tooltipType] = this.tooltip;
6921                 }
6922             }
6923         }else{
6924             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6925         }
6926         this.el = btn;
6927         if(this.id){
6928             this.el.dom.id = this.el.id = this.id;
6929         }
6930         if(this.menu){
6931             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6932             this.menu.on("show", this.onMenuShow, this);
6933             this.menu.on("hide", this.onMenuHide, this);
6934         }
6935         btn.addClass("x-btn");
6936         if(Roo.isIE && !Roo.isIE7){
6937             this.autoWidth.defer(1, this);
6938         }else{
6939             this.autoWidth();
6940         }
6941         if(this.handleMouseEvents){
6942             btn.on("mouseover", this.onMouseOver, this);
6943             btn.on("mouseout", this.onMouseOut, this);
6944             btn.on("mousedown", this.onMouseDown, this);
6945         }
6946         btn.on(this.clickEvent, this.onClick, this);
6947         //btn.on("mouseup", this.onMouseUp, this);
6948         if(this.hidden){
6949             this.hide();
6950         }
6951         if(this.disabled){
6952             this.disable();
6953         }
6954         Roo.ButtonToggleMgr.register(this);
6955         if(this.pressed){
6956             this.el.addClass("x-btn-pressed");
6957         }
6958         if(this.repeat){
6959             var repeater = new Roo.util.ClickRepeater(btn,
6960                 typeof this.repeat == "object" ? this.repeat : {}
6961             );
6962             repeater.on("click", this.onClick,  this);
6963         }
6964         
6965         this.fireEvent('render', this);
6966         
6967     },
6968     /**
6969      * Returns the button's underlying element
6970      * @return {Roo.Element} The element
6971      */
6972     getEl : function(){
6973         return this.el;  
6974     },
6975     
6976     /**
6977      * Destroys this Button and removes any listeners.
6978      */
6979     destroy : function(){
6980         Roo.ButtonToggleMgr.unregister(this);
6981         this.el.removeAllListeners();
6982         this.purgeListeners();
6983         this.el.remove();
6984     },
6985
6986     // private
6987     autoWidth : function(){
6988         if(this.el){
6989             this.el.setWidth("auto");
6990             if(Roo.isIE7 && Roo.isStrict){
6991                 var ib = this.el.child('button');
6992                 if(ib && ib.getWidth() > 20){
6993                     ib.clip();
6994                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6995                 }
6996             }
6997             if(this.minWidth){
6998                 if(this.hidden){
6999                     this.el.beginMeasure();
7000                 }
7001                 if(this.el.getWidth() < this.minWidth){
7002                     this.el.setWidth(this.minWidth);
7003                 }
7004                 if(this.hidden){
7005                     this.el.endMeasure();
7006                 }
7007             }
7008         }
7009     },
7010
7011     /**
7012      * Assigns this button's click handler
7013      * @param {Function} handler The function to call when the button is clicked
7014      * @param {Object} scope (optional) Scope for the function passed in
7015      */
7016     setHandler : function(handler, scope){
7017         this.handler = handler;
7018         this.scope = scope;  
7019     },
7020     
7021     /**
7022      * Sets this button's text
7023      * @param {String} text The button text
7024      */
7025     setText : function(text){
7026         this.text = text;
7027         if(this.el){
7028             this.el.child("td.x-btn-center button.x-btn-text").update(text);
7029         }
7030         this.autoWidth();
7031     },
7032     
7033     /**
7034      * Gets the text for this button
7035      * @return {String} The button text
7036      */
7037     getText : function(){
7038         return this.text;  
7039     },
7040     
7041     /**
7042      * Show this button
7043      */
7044     show: function(){
7045         this.hidden = false;
7046         if(this.el){
7047             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7048         }
7049     },
7050     
7051     /**
7052      * Hide this button
7053      */
7054     hide: function(){
7055         this.hidden = true;
7056         if(this.el){
7057             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7058         }
7059     },
7060     
7061     /**
7062      * Convenience function for boolean show/hide
7063      * @param {Boolean} visible True to show, false to hide
7064      */
7065     setVisible: function(visible){
7066         if(visible) {
7067             this.show();
7068         }else{
7069             this.hide();
7070         }
7071     },
7072     
7073     /**
7074      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7075      * @param {Boolean} state (optional) Force a particular state
7076      */
7077     toggle : function(state){
7078         state = state === undefined ? !this.pressed : state;
7079         if(state != this.pressed){
7080             if(state){
7081                 this.el.addClass("x-btn-pressed");
7082                 this.pressed = true;
7083                 this.fireEvent("toggle", this, true);
7084             }else{
7085                 this.el.removeClass("x-btn-pressed");
7086                 this.pressed = false;
7087                 this.fireEvent("toggle", this, false);
7088             }
7089             if(this.toggleHandler){
7090                 this.toggleHandler.call(this.scope || this, this, state);
7091             }
7092         }
7093     },
7094     
7095     /**
7096      * Focus the button
7097      */
7098     focus : function(){
7099         this.el.child('button:first').focus();
7100     },
7101     
7102     /**
7103      * Disable this button
7104      */
7105     disable : function(){
7106         if(this.el){
7107             this.el.addClass("x-btn-disabled");
7108         }
7109         this.disabled = true;
7110     },
7111     
7112     /**
7113      * Enable this button
7114      */
7115     enable : function(){
7116         if(this.el){
7117             this.el.removeClass("x-btn-disabled");
7118         }
7119         this.disabled = false;
7120     },
7121
7122     /**
7123      * Convenience function for boolean enable/disable
7124      * @param {Boolean} enabled True to enable, false to disable
7125      */
7126     setDisabled : function(v){
7127         this[v !== true ? "enable" : "disable"]();
7128     },
7129
7130     // private
7131     onClick : function(e)
7132     {
7133         if(e){
7134             e.preventDefault();
7135         }
7136         if(e.button != 0){
7137             return;
7138         }
7139         if(!this.disabled){
7140             if(this.enableToggle){
7141                 this.toggle();
7142             }
7143             if(this.menu && !this.menu.isVisible()){
7144                 this.menu.show(this.el, this.menuAlign);
7145             }
7146             this.fireEvent("click", this, e);
7147             if(this.handler){
7148                 this.el.removeClass("x-btn-over");
7149                 this.handler.call(this.scope || this, this, e);
7150             }
7151         }
7152     },
7153     // private
7154     onMouseOver : function(e){
7155         if(!this.disabled){
7156             this.el.addClass("x-btn-over");
7157             this.fireEvent('mouseover', this, e);
7158         }
7159     },
7160     // private
7161     onMouseOut : function(e){
7162         if(!e.within(this.el,  true)){
7163             this.el.removeClass("x-btn-over");
7164             this.fireEvent('mouseout', this, e);
7165         }
7166     },
7167     // private
7168     onFocus : function(e){
7169         if(!this.disabled){
7170             this.el.addClass("x-btn-focus");
7171         }
7172     },
7173     // private
7174     onBlur : function(e){
7175         this.el.removeClass("x-btn-focus");
7176     },
7177     // private
7178     onMouseDown : function(e){
7179         if(!this.disabled && e.button == 0){
7180             this.el.addClass("x-btn-click");
7181             Roo.get(document).on('mouseup', this.onMouseUp, this);
7182         }
7183     },
7184     // private
7185     onMouseUp : function(e){
7186         if(e.button == 0){
7187             this.el.removeClass("x-btn-click");
7188             Roo.get(document).un('mouseup', this.onMouseUp, this);
7189         }
7190     },
7191     // private
7192     onMenuShow : function(e){
7193         this.el.addClass("x-btn-menu-active");
7194     },
7195     // private
7196     onMenuHide : function(e){
7197         this.el.removeClass("x-btn-menu-active");
7198     }   
7199 });
7200
7201 // Private utility class used by Button
7202 Roo.ButtonToggleMgr = function(){
7203    var groups = {};
7204    
7205    function toggleGroup(btn, state){
7206        if(state){
7207            var g = groups[btn.toggleGroup];
7208            for(var i = 0, l = g.length; i < l; i++){
7209                if(g[i] != btn){
7210                    g[i].toggle(false);
7211                }
7212            }
7213        }
7214    }
7215    
7216    return {
7217        register : function(btn){
7218            if(!btn.toggleGroup){
7219                return;
7220            }
7221            var g = groups[btn.toggleGroup];
7222            if(!g){
7223                g = groups[btn.toggleGroup] = [];
7224            }
7225            g.push(btn);
7226            btn.on("toggle", toggleGroup);
7227        },
7228        
7229        unregister : function(btn){
7230            if(!btn.toggleGroup){
7231                return;
7232            }
7233            var g = groups[btn.toggleGroup];
7234            if(g){
7235                g.remove(btn);
7236                btn.un("toggle", toggleGroup);
7237            }
7238        }
7239    };
7240 }();/*
7241  * Based on:
7242  * Ext JS Library 1.1.1
7243  * Copyright(c) 2006-2007, Ext JS, LLC.
7244  *
7245  * Originally Released Under LGPL - original licence link has changed is not relivant.
7246  *
7247  * Fork - LGPL
7248  * <script type="text/javascript">
7249  */
7250  
7251 /**
7252  * @class Roo.SplitButton
7253  * @extends Roo.Button
7254  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7255  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7256  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7257  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7258  * @cfg {String} arrowTooltip The title attribute of the arrow
7259  * @constructor
7260  * Create a new menu button
7261  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7262  * @param {Object} config The config object
7263  */
7264 Roo.SplitButton = function(renderTo, config){
7265     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7266     /**
7267      * @event arrowclick
7268      * Fires when this button's arrow is clicked
7269      * @param {SplitButton} this
7270      * @param {EventObject} e The click event
7271      */
7272     this.addEvents({"arrowclick":true});
7273 };
7274
7275 Roo.extend(Roo.SplitButton, Roo.Button, {
7276     render : function(renderTo){
7277         // this is one sweet looking template!
7278         var tpl = new Roo.Template(
7279             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7280             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7281             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
7282             "</tbody></table></td><td>",
7283             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7284             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
7285             "</tbody></table></td></tr></table>"
7286         );
7287         var btn = tpl.append(renderTo, [this.text, this.type], true);
7288         var btnEl = btn.child("button");
7289         if(this.cls){
7290             btn.addClass(this.cls);
7291         }
7292         if(this.icon){
7293             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7294         }
7295         if(this.iconCls){
7296             btnEl.addClass(this.iconCls);
7297             if(!this.cls){
7298                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7299             }
7300         }
7301         this.el = btn;
7302         if(this.handleMouseEvents){
7303             btn.on("mouseover", this.onMouseOver, this);
7304             btn.on("mouseout", this.onMouseOut, this);
7305             btn.on("mousedown", this.onMouseDown, this);
7306             btn.on("mouseup", this.onMouseUp, this);
7307         }
7308         btn.on(this.clickEvent, this.onClick, this);
7309         if(this.tooltip){
7310             if(typeof this.tooltip == 'object'){
7311                 Roo.QuickTips.tips(Roo.apply({
7312                       target: btnEl.id
7313                 }, this.tooltip));
7314             } else {
7315                 btnEl.dom[this.tooltipType] = this.tooltip;
7316             }
7317         }
7318         if(this.arrowTooltip){
7319             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7320         }
7321         if(this.hidden){
7322             this.hide();
7323         }
7324         if(this.disabled){
7325             this.disable();
7326         }
7327         if(this.pressed){
7328             this.el.addClass("x-btn-pressed");
7329         }
7330         if(Roo.isIE && !Roo.isIE7){
7331             this.autoWidth.defer(1, this);
7332         }else{
7333             this.autoWidth();
7334         }
7335         if(this.menu){
7336             this.menu.on("show", this.onMenuShow, this);
7337             this.menu.on("hide", this.onMenuHide, this);
7338         }
7339         this.fireEvent('render', this);
7340     },
7341
7342     // private
7343     autoWidth : function(){
7344         if(this.el){
7345             var tbl = this.el.child("table:first");
7346             var tbl2 = this.el.child("table:last");
7347             this.el.setWidth("auto");
7348             tbl.setWidth("auto");
7349             if(Roo.isIE7 && Roo.isStrict){
7350                 var ib = this.el.child('button:first');
7351                 if(ib && ib.getWidth() > 20){
7352                     ib.clip();
7353                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7354                 }
7355             }
7356             if(this.minWidth){
7357                 if(this.hidden){
7358                     this.el.beginMeasure();
7359                 }
7360                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7361                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7362                 }
7363                 if(this.hidden){
7364                     this.el.endMeasure();
7365                 }
7366             }
7367             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7368         } 
7369     },
7370     /**
7371      * Sets this button's click handler
7372      * @param {Function} handler The function to call when the button is clicked
7373      * @param {Object} scope (optional) Scope for the function passed above
7374      */
7375     setHandler : function(handler, scope){
7376         this.handler = handler;
7377         this.scope = scope;  
7378     },
7379     
7380     /**
7381      * Sets this button's arrow click handler
7382      * @param {Function} handler The function to call when the arrow is clicked
7383      * @param {Object} scope (optional) Scope for the function passed above
7384      */
7385     setArrowHandler : function(handler, scope){
7386         this.arrowHandler = handler;
7387         this.scope = scope;  
7388     },
7389     
7390     /**
7391      * Focus the button
7392      */
7393     focus : function(){
7394         if(this.el){
7395             this.el.child("button:first").focus();
7396         }
7397     },
7398
7399     // private
7400     onClick : function(e){
7401         e.preventDefault();
7402         if(!this.disabled){
7403             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7404                 if(this.menu && !this.menu.isVisible()){
7405                     this.menu.show(this.el, this.menuAlign);
7406                 }
7407                 this.fireEvent("arrowclick", this, e);
7408                 if(this.arrowHandler){
7409                     this.arrowHandler.call(this.scope || this, this, e);
7410                 }
7411             }else{
7412                 this.fireEvent("click", this, e);
7413                 if(this.handler){
7414                     this.handler.call(this.scope || this, this, e);
7415                 }
7416             }
7417         }
7418     },
7419     // private
7420     onMouseDown : function(e){
7421         if(!this.disabled){
7422             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7423         }
7424     },
7425     // private
7426     onMouseUp : function(e){
7427         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7428     }   
7429 });
7430
7431
7432 // backwards compat
7433 Roo.MenuButton = Roo.SplitButton;/*
7434  * Based on:
7435  * Ext JS Library 1.1.1
7436  * Copyright(c) 2006-2007, Ext JS, LLC.
7437  *
7438  * Originally Released Under LGPL - original licence link has changed is not relivant.
7439  *
7440  * Fork - LGPL
7441  * <script type="text/javascript">
7442  */
7443
7444 /**
7445  * @class Roo.Toolbar
7446  * Basic Toolbar class.
7447  * @constructor
7448  * Creates a new Toolbar
7449  * @param {Object} container The config object
7450  */ 
7451 Roo.Toolbar = function(container, buttons, config)
7452 {
7453     /// old consturctor format still supported..
7454     if(container instanceof Array){ // omit the container for later rendering
7455         buttons = container;
7456         config = buttons;
7457         container = null;
7458     }
7459     if (typeof(container) == 'object' && container.xtype) {
7460         config = container;
7461         container = config.container;
7462         buttons = config.buttons || []; // not really - use items!!
7463     }
7464     var xitems = [];
7465     if (config && config.items) {
7466         xitems = config.items;
7467         delete config.items;
7468     }
7469     Roo.apply(this, config);
7470     this.buttons = buttons;
7471     
7472     if(container){
7473         this.render(container);
7474     }
7475     this.xitems = xitems;
7476     Roo.each(xitems, function(b) {
7477         this.add(b);
7478     }, this);
7479     
7480 };
7481
7482 Roo.Toolbar.prototype = {
7483     /**
7484      * @cfg {Array} items
7485      * array of button configs or elements to add (will be converted to a MixedCollection)
7486      */
7487     
7488     /**
7489      * @cfg {String/HTMLElement/Element} container
7490      * The id or element that will contain the toolbar
7491      */
7492     // private
7493     render : function(ct){
7494         this.el = Roo.get(ct);
7495         if(this.cls){
7496             this.el.addClass(this.cls);
7497         }
7498         // using a table allows for vertical alignment
7499         // 100% width is needed by Safari...
7500         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7501         this.tr = this.el.child("tr", true);
7502         var autoId = 0;
7503         this.items = new Roo.util.MixedCollection(false, function(o){
7504             return o.id || ("item" + (++autoId));
7505         });
7506         if(this.buttons){
7507             this.add.apply(this, this.buttons);
7508             delete this.buttons;
7509         }
7510     },
7511
7512     /**
7513      * Adds element(s) to the toolbar -- this function takes a variable number of 
7514      * arguments of mixed type and adds them to the toolbar.
7515      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7516      * <ul>
7517      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7518      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7519      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7520      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7521      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7522      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7523      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7524      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7525      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7526      * </ul>
7527      * @param {Mixed} arg2
7528      * @param {Mixed} etc.
7529      */
7530     add : function(){
7531         var a = arguments, l = a.length;
7532         for(var i = 0; i < l; i++){
7533             this._add(a[i]);
7534         }
7535     },
7536     // private..
7537     _add : function(el) {
7538         
7539         if (el.xtype) {
7540             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7541         }
7542         
7543         if (el.applyTo){ // some kind of form field
7544             return this.addField(el);
7545         } 
7546         if (el.render){ // some kind of Toolbar.Item
7547             return this.addItem(el);
7548         }
7549         if (typeof el == "string"){ // string
7550             if(el == "separator" || el == "-"){
7551                 return this.addSeparator();
7552             }
7553             if (el == " "){
7554                 return this.addSpacer();
7555             }
7556             if(el == "->"){
7557                 return this.addFill();
7558             }
7559             return this.addText(el);
7560             
7561         }
7562         if(el.tagName){ // element
7563             return this.addElement(el);
7564         }
7565         if(typeof el == "object"){ // must be button config?
7566             return this.addButton(el);
7567         }
7568         // and now what?!?!
7569         return false;
7570         
7571     },
7572     
7573     /**
7574      * Add an Xtype element
7575      * @param {Object} xtype Xtype Object
7576      * @return {Object} created Object
7577      */
7578     addxtype : function(e){
7579         return this.add(e);  
7580     },
7581     
7582     /**
7583      * Returns the Element for this toolbar.
7584      * @return {Roo.Element}
7585      */
7586     getEl : function(){
7587         return this.el;  
7588     },
7589     
7590     /**
7591      * Adds a separator
7592      * @return {Roo.Toolbar.Item} The separator item
7593      */
7594     addSeparator : function(){
7595         return this.addItem(new Roo.Toolbar.Separator());
7596     },
7597
7598     /**
7599      * Adds a spacer element
7600      * @return {Roo.Toolbar.Spacer} The spacer item
7601      */
7602     addSpacer : function(){
7603         return this.addItem(new Roo.Toolbar.Spacer());
7604     },
7605
7606     /**
7607      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7608      * @return {Roo.Toolbar.Fill} The fill item
7609      */
7610     addFill : function(){
7611         return this.addItem(new Roo.Toolbar.Fill());
7612     },
7613
7614     /**
7615      * Adds any standard HTML element to the toolbar
7616      * @param {String/HTMLElement/Element} el The element or id of the element to add
7617      * @return {Roo.Toolbar.Item} The element's item
7618      */
7619     addElement : function(el){
7620         return this.addItem(new Roo.Toolbar.Item(el));
7621     },
7622     /**
7623      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7624      * @type Roo.util.MixedCollection  
7625      */
7626     items : false,
7627      
7628     /**
7629      * Adds any Toolbar.Item or subclass
7630      * @param {Roo.Toolbar.Item} item
7631      * @return {Roo.Toolbar.Item} The item
7632      */
7633     addItem : function(item){
7634         var td = this.nextBlock();
7635         item.render(td);
7636         this.items.add(item);
7637         return item;
7638     },
7639     
7640     /**
7641      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7642      * @param {Object/Array} config A button config or array of configs
7643      * @return {Roo.Toolbar.Button/Array}
7644      */
7645     addButton : function(config){
7646         if(config instanceof Array){
7647             var buttons = [];
7648             for(var i = 0, len = config.length; i < len; i++) {
7649                 buttons.push(this.addButton(config[i]));
7650             }
7651             return buttons;
7652         }
7653         var b = config;
7654         if(!(config instanceof Roo.Toolbar.Button)){
7655             b = config.split ?
7656                 new Roo.Toolbar.SplitButton(config) :
7657                 new Roo.Toolbar.Button(config);
7658         }
7659         var td = this.nextBlock();
7660         b.render(td);
7661         this.items.add(b);
7662         return b;
7663     },
7664     
7665     /**
7666      * Adds text to the toolbar
7667      * @param {String} text The text to add
7668      * @return {Roo.Toolbar.Item} The element's item
7669      */
7670     addText : function(text){
7671         return this.addItem(new Roo.Toolbar.TextItem(text));
7672     },
7673     
7674     /**
7675      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7676      * @param {Number} index The index where the item is to be inserted
7677      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7678      * @return {Roo.Toolbar.Button/Item}
7679      */
7680     insertButton : function(index, item){
7681         if(item instanceof Array){
7682             var buttons = [];
7683             for(var i = 0, len = item.length; i < len; i++) {
7684                buttons.push(this.insertButton(index + i, item[i]));
7685             }
7686             return buttons;
7687         }
7688         if (!(item instanceof Roo.Toolbar.Button)){
7689            item = new Roo.Toolbar.Button(item);
7690         }
7691         var td = document.createElement("td");
7692         this.tr.insertBefore(td, this.tr.childNodes[index]);
7693         item.render(td);
7694         this.items.insert(index, item);
7695         return item;
7696     },
7697     
7698     /**
7699      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7700      * @param {Object} config
7701      * @return {Roo.Toolbar.Item} The element's item
7702      */
7703     addDom : function(config, returnEl){
7704         var td = this.nextBlock();
7705         Roo.DomHelper.overwrite(td, config);
7706         var ti = new Roo.Toolbar.Item(td.firstChild);
7707         ti.render(td);
7708         this.items.add(ti);
7709         return ti;
7710     },
7711
7712     /**
7713      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7714      * @type Roo.util.MixedCollection  
7715      */
7716     fields : false,
7717     
7718     /**
7719      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7720      * Note: the field should not have been rendered yet. For a field that has already been
7721      * rendered, use {@link #addElement}.
7722      * @param {Roo.form.Field} field
7723      * @return {Roo.ToolbarItem}
7724      */
7725      
7726       
7727     addField : function(field) {
7728         if (!this.fields) {
7729             var autoId = 0;
7730             this.fields = new Roo.util.MixedCollection(false, function(o){
7731                 return o.id || ("item" + (++autoId));
7732             });
7733
7734         }
7735         
7736         var td = this.nextBlock();
7737         field.render(td);
7738         var ti = new Roo.Toolbar.Item(td.firstChild);
7739         ti.render(td);
7740         this.items.add(ti);
7741         this.fields.add(field);
7742         return ti;
7743     },
7744     /**
7745      * Hide the toolbar
7746      * @method hide
7747      */
7748      
7749       
7750     hide : function()
7751     {
7752         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7753         this.el.child('div').hide();
7754     },
7755     /**
7756      * Show the toolbar
7757      * @method show
7758      */
7759     show : function()
7760     {
7761         this.el.child('div').show();
7762     },
7763       
7764     // private
7765     nextBlock : function(){
7766         var td = document.createElement("td");
7767         this.tr.appendChild(td);
7768         return td;
7769     },
7770
7771     // private
7772     destroy : function(){
7773         if(this.items){ // rendered?
7774             Roo.destroy.apply(Roo, this.items.items);
7775         }
7776         if(this.fields){ // rendered?
7777             Roo.destroy.apply(Roo, this.fields.items);
7778         }
7779         Roo.Element.uncache(this.el, this.tr);
7780     }
7781 };
7782
7783 /**
7784  * @class Roo.Toolbar.Item
7785  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7786  * @constructor
7787  * Creates a new Item
7788  * @param {HTMLElement} el 
7789  */
7790 Roo.Toolbar.Item = function(el){
7791     var cfg = {};
7792     if (typeof (el.xtype) != 'undefined') {
7793         cfg = el;
7794         el = cfg.el;
7795     }
7796     
7797     this.el = Roo.getDom(el);
7798     this.id = Roo.id(this.el);
7799     this.hidden = false;
7800     
7801     this.addEvents({
7802          /**
7803              * @event render
7804              * Fires when the button is rendered
7805              * @param {Button} this
7806              */
7807         'render': true
7808     });
7809     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7810 };
7811 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7812 //Roo.Toolbar.Item.prototype = {
7813     
7814     /**
7815      * Get this item's HTML Element
7816      * @return {HTMLElement}
7817      */
7818     getEl : function(){
7819        return this.el;  
7820     },
7821
7822     // private
7823     render : function(td){
7824         
7825          this.td = td;
7826         td.appendChild(this.el);
7827         
7828         this.fireEvent('render', this);
7829     },
7830     
7831     /**
7832      * Removes and destroys this item.
7833      */
7834     destroy : function(){
7835         this.td.parentNode.removeChild(this.td);
7836     },
7837     
7838     /**
7839      * Shows this item.
7840      */
7841     show: function(){
7842         this.hidden = false;
7843         this.td.style.display = "";
7844     },
7845     
7846     /**
7847      * Hides this item.
7848      */
7849     hide: function(){
7850         this.hidden = true;
7851         this.td.style.display = "none";
7852     },
7853     
7854     /**
7855      * Convenience function for boolean show/hide.
7856      * @param {Boolean} visible true to show/false to hide
7857      */
7858     setVisible: function(visible){
7859         if(visible) {
7860             this.show();
7861         }else{
7862             this.hide();
7863         }
7864     },
7865     
7866     /**
7867      * Try to focus this item.
7868      */
7869     focus : function(){
7870         Roo.fly(this.el).focus();
7871     },
7872     
7873     /**
7874      * Disables this item.
7875      */
7876     disable : function(){
7877         Roo.fly(this.td).addClass("x-item-disabled");
7878         this.disabled = true;
7879         this.el.disabled = true;
7880     },
7881     
7882     /**
7883      * Enables this item.
7884      */
7885     enable : function(){
7886         Roo.fly(this.td).removeClass("x-item-disabled");
7887         this.disabled = false;
7888         this.el.disabled = false;
7889     }
7890 });
7891
7892
7893 /**
7894  * @class Roo.Toolbar.Separator
7895  * @extends Roo.Toolbar.Item
7896  * A simple toolbar separator class
7897  * @constructor
7898  * Creates a new Separator
7899  */
7900 Roo.Toolbar.Separator = function(cfg){
7901     
7902     var s = document.createElement("span");
7903     s.className = "ytb-sep";
7904     if (cfg) {
7905         cfg.el = s;
7906     }
7907     
7908     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7909 };
7910 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7911     enable:Roo.emptyFn,
7912     disable:Roo.emptyFn,
7913     focus:Roo.emptyFn
7914 });
7915
7916 /**
7917  * @class Roo.Toolbar.Spacer
7918  * @extends Roo.Toolbar.Item
7919  * A simple element that adds extra horizontal space to a toolbar.
7920  * @constructor
7921  * Creates a new Spacer
7922  */
7923 Roo.Toolbar.Spacer = function(cfg){
7924     var s = document.createElement("div");
7925     s.className = "ytb-spacer";
7926     if (cfg) {
7927         cfg.el = s;
7928     }
7929     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7930 };
7931 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7932     enable:Roo.emptyFn,
7933     disable:Roo.emptyFn,
7934     focus:Roo.emptyFn
7935 });
7936
7937 /**
7938  * @class Roo.Toolbar.Fill
7939  * @extends Roo.Toolbar.Spacer
7940  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7941  * @constructor
7942  * Creates a new Spacer
7943  */
7944 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7945     // private
7946     render : function(td){
7947         td.style.width = '100%';
7948         Roo.Toolbar.Fill.superclass.render.call(this, td);
7949     }
7950 });
7951
7952 /**
7953  * @class Roo.Toolbar.TextItem
7954  * @extends Roo.Toolbar.Item
7955  * A simple class that renders text directly into a toolbar.
7956  * @constructor
7957  * Creates a new TextItem
7958  * @param {String} text
7959  */
7960 Roo.Toolbar.TextItem = function(cfg){
7961     var  text = cfg || "";
7962     if (typeof(cfg) == 'object') {
7963         text = cfg.text || "";
7964     }  else {
7965         cfg = null;
7966     }
7967     var s = document.createElement("span");
7968     s.className = "ytb-text";
7969     s.innerHTML = text;
7970     if (cfg) {
7971         cfg.el  = s;
7972     }
7973     
7974     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7975 };
7976 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7977     
7978      
7979     enable:Roo.emptyFn,
7980     disable:Roo.emptyFn,
7981     focus:Roo.emptyFn
7982 });
7983
7984 /**
7985  * @class Roo.Toolbar.Button
7986  * @extends Roo.Button
7987  * A button that renders into a toolbar.
7988  * @constructor
7989  * Creates a new Button
7990  * @param {Object} config A standard {@link Roo.Button} config object
7991  */
7992 Roo.Toolbar.Button = function(config){
7993     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7994 };
7995 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7996     render : function(td){
7997         this.td = td;
7998         Roo.Toolbar.Button.superclass.render.call(this, td);
7999     },
8000     
8001     /**
8002      * Removes and destroys this button
8003      */
8004     destroy : function(){
8005         Roo.Toolbar.Button.superclass.destroy.call(this);
8006         this.td.parentNode.removeChild(this.td);
8007     },
8008     
8009     /**
8010      * Shows this button
8011      */
8012     show: function(){
8013         this.hidden = false;
8014         this.td.style.display = "";
8015     },
8016     
8017     /**
8018      * Hides this button
8019      */
8020     hide: function(){
8021         this.hidden = true;
8022         this.td.style.display = "none";
8023     },
8024
8025     /**
8026      * Disables this item
8027      */
8028     disable : function(){
8029         Roo.fly(this.td).addClass("x-item-disabled");
8030         this.disabled = true;
8031     },
8032
8033     /**
8034      * Enables this item
8035      */
8036     enable : function(){
8037         Roo.fly(this.td).removeClass("x-item-disabled");
8038         this.disabled = false;
8039     }
8040 });
8041 // backwards compat
8042 Roo.ToolbarButton = Roo.Toolbar.Button;
8043
8044 /**
8045  * @class Roo.Toolbar.SplitButton
8046  * @extends Roo.SplitButton
8047  * A menu button that renders into a toolbar.
8048  * @constructor
8049  * Creates a new SplitButton
8050  * @param {Object} config A standard {@link Roo.SplitButton} config object
8051  */
8052 Roo.Toolbar.SplitButton = function(config){
8053     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8054 };
8055 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8056     render : function(td){
8057         this.td = td;
8058         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8059     },
8060     
8061     /**
8062      * Removes and destroys this button
8063      */
8064     destroy : function(){
8065         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8066         this.td.parentNode.removeChild(this.td);
8067     },
8068     
8069     /**
8070      * Shows this button
8071      */
8072     show: function(){
8073         this.hidden = false;
8074         this.td.style.display = "";
8075     },
8076     
8077     /**
8078      * Hides this button
8079      */
8080     hide: function(){
8081         this.hidden = true;
8082         this.td.style.display = "none";
8083     }
8084 });
8085
8086 // backwards compat
8087 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8088  * Based on:
8089  * Ext JS Library 1.1.1
8090  * Copyright(c) 2006-2007, Ext JS, LLC.
8091  *
8092  * Originally Released Under LGPL - original licence link has changed is not relivant.
8093  *
8094  * Fork - LGPL
8095  * <script type="text/javascript">
8096  */
8097  
8098 /**
8099  * @class Roo.PagingToolbar
8100  * @extends Roo.Toolbar
8101  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8102  * @constructor
8103  * Create a new PagingToolbar
8104  * @param {Object} config The config object
8105  */
8106 Roo.PagingToolbar = function(el, ds, config)
8107 {
8108     // old args format still supported... - xtype is prefered..
8109     if (typeof(el) == 'object' && el.xtype) {
8110         // created from xtype...
8111         config = el;
8112         ds = el.dataSource;
8113         el = config.container;
8114     }
8115     var items = [];
8116     if (config.items) {
8117         items = config.items;
8118         config.items = [];
8119     }
8120     
8121     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8122     this.ds = ds;
8123     this.cursor = 0;
8124     this.renderButtons(this.el);
8125     this.bind(ds);
8126     
8127     // supprot items array.
8128    
8129     Roo.each(items, function(e) {
8130         this.add(Roo.factory(e));
8131     },this);
8132     
8133 };
8134
8135 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8136     /**
8137      * @cfg {Roo.data.Store} dataSource
8138      * The underlying data store providing the paged data
8139      */
8140     /**
8141      * @cfg {String/HTMLElement/Element} container
8142      * container The id or element that will contain the toolbar
8143      */
8144     /**
8145      * @cfg {Boolean} displayInfo
8146      * True to display the displayMsg (defaults to false)
8147      */
8148     /**
8149      * @cfg {Number} pageSize
8150      * The number of records to display per page (defaults to 20)
8151      */
8152     pageSize: 20,
8153     /**
8154      * @cfg {String} displayMsg
8155      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8156      */
8157     displayMsg : 'Displaying {0} - {1} of {2}',
8158     /**
8159      * @cfg {String} emptyMsg
8160      * The message to display when no records are found (defaults to "No data to display")
8161      */
8162     emptyMsg : 'No data to display',
8163     /**
8164      * Customizable piece of the default paging text (defaults to "Page")
8165      * @type String
8166      */
8167     beforePageText : "Page",
8168     /**
8169      * Customizable piece of the default paging text (defaults to "of %0")
8170      * @type String
8171      */
8172     afterPageText : "of {0}",
8173     /**
8174      * Customizable piece of the default paging text (defaults to "First Page")
8175      * @type String
8176      */
8177     firstText : "First Page",
8178     /**
8179      * Customizable piece of the default paging text (defaults to "Previous Page")
8180      * @type String
8181      */
8182     prevText : "Previous Page",
8183     /**
8184      * Customizable piece of the default paging text (defaults to "Next Page")
8185      * @type String
8186      */
8187     nextText : "Next Page",
8188     /**
8189      * Customizable piece of the default paging text (defaults to "Last Page")
8190      * @type String
8191      */
8192     lastText : "Last Page",
8193     /**
8194      * Customizable piece of the default paging text (defaults to "Refresh")
8195      * @type String
8196      */
8197     refreshText : "Refresh",
8198
8199     // private
8200     renderButtons : function(el){
8201         Roo.PagingToolbar.superclass.render.call(this, el);
8202         this.first = this.addButton({
8203             tooltip: this.firstText,
8204             cls: "x-btn-icon x-grid-page-first",
8205             disabled: true,
8206             handler: this.onClick.createDelegate(this, ["first"])
8207         });
8208         this.prev = this.addButton({
8209             tooltip: this.prevText,
8210             cls: "x-btn-icon x-grid-page-prev",
8211             disabled: true,
8212             handler: this.onClick.createDelegate(this, ["prev"])
8213         });
8214         //this.addSeparator();
8215         this.add(this.beforePageText);
8216         this.field = Roo.get(this.addDom({
8217            tag: "input",
8218            type: "text",
8219            size: "3",
8220            value: "1",
8221            cls: "x-grid-page-number"
8222         }).el);
8223         this.field.on("keydown", this.onPagingKeydown, this);
8224         this.field.on("focus", function(){this.dom.select();});
8225         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8226         this.field.setHeight(18);
8227         //this.addSeparator();
8228         this.next = this.addButton({
8229             tooltip: this.nextText,
8230             cls: "x-btn-icon x-grid-page-next",
8231             disabled: true,
8232             handler: this.onClick.createDelegate(this, ["next"])
8233         });
8234         this.last = this.addButton({
8235             tooltip: this.lastText,
8236             cls: "x-btn-icon x-grid-page-last",
8237             disabled: true,
8238             handler: this.onClick.createDelegate(this, ["last"])
8239         });
8240         //this.addSeparator();
8241         this.loading = this.addButton({
8242             tooltip: this.refreshText,
8243             cls: "x-btn-icon x-grid-loading",
8244             handler: this.onClick.createDelegate(this, ["refresh"])
8245         });
8246
8247         if(this.displayInfo){
8248             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8249         }
8250     },
8251
8252     // private
8253     updateInfo : function(){
8254         if(this.displayEl){
8255             var count = this.ds.getCount();
8256             var msg = count == 0 ?
8257                 this.emptyMsg :
8258                 String.format(
8259                     this.displayMsg,
8260                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8261                 );
8262             this.displayEl.update(msg);
8263         }
8264     },
8265
8266     // private
8267     onLoad : function(ds, r, o){
8268        this.cursor = o.params ? o.params.start : 0;
8269        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8270
8271        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8272        this.field.dom.value = ap;
8273        this.first.setDisabled(ap == 1);
8274        this.prev.setDisabled(ap == 1);
8275        this.next.setDisabled(ap == ps);
8276        this.last.setDisabled(ap == ps);
8277        this.loading.enable();
8278        this.updateInfo();
8279     },
8280
8281     // private
8282     getPageData : function(){
8283         var total = this.ds.getTotalCount();
8284         return {
8285             total : total,
8286             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8287             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8288         };
8289     },
8290
8291     // private
8292     onLoadError : function(){
8293         this.loading.enable();
8294     },
8295
8296     // private
8297     onPagingKeydown : function(e){
8298         var k = e.getKey();
8299         var d = this.getPageData();
8300         if(k == e.RETURN){
8301             var v = this.field.dom.value, pageNum;
8302             if(!v || isNaN(pageNum = parseInt(v, 10))){
8303                 this.field.dom.value = d.activePage;
8304                 return;
8305             }
8306             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8307             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8308             e.stopEvent();
8309         }
8310         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))
8311         {
8312           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8313           this.field.dom.value = pageNum;
8314           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8315           e.stopEvent();
8316         }
8317         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8318         {
8319           var v = this.field.dom.value, pageNum; 
8320           var increment = (e.shiftKey) ? 10 : 1;
8321           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8322             increment *= -1;
8323           }
8324           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8325             this.field.dom.value = d.activePage;
8326             return;
8327           }
8328           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8329           {
8330             this.field.dom.value = parseInt(v, 10) + increment;
8331             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8332             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8333           }
8334           e.stopEvent();
8335         }
8336     },
8337
8338     // private
8339     beforeLoad : function(){
8340         if(this.loading){
8341             this.loading.disable();
8342         }
8343     },
8344
8345     // private
8346     onClick : function(which){
8347         var ds = this.ds;
8348         switch(which){
8349             case "first":
8350                 ds.load({params:{start: 0, limit: this.pageSize}});
8351             break;
8352             case "prev":
8353                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8354             break;
8355             case "next":
8356                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8357             break;
8358             case "last":
8359                 var total = ds.getTotalCount();
8360                 var extra = total % this.pageSize;
8361                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8362                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8363             break;
8364             case "refresh":
8365                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8366             break;
8367         }
8368     },
8369
8370     /**
8371      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8372      * @param {Roo.data.Store} store The data store to unbind
8373      */
8374     unbind : function(ds){
8375         ds.un("beforeload", this.beforeLoad, this);
8376         ds.un("load", this.onLoad, this);
8377         ds.un("loadexception", this.onLoadError, this);
8378         ds.un("remove", this.updateInfo, this);
8379         ds.un("add", this.updateInfo, this);
8380         this.ds = undefined;
8381     },
8382
8383     /**
8384      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8385      * @param {Roo.data.Store} store The data store to bind
8386      */
8387     bind : function(ds){
8388         ds.on("beforeload", this.beforeLoad, this);
8389         ds.on("load", this.onLoad, this);
8390         ds.on("loadexception", this.onLoadError, this);
8391         ds.on("remove", this.updateInfo, this);
8392         ds.on("add", this.updateInfo, this);
8393         this.ds = ds;
8394     }
8395 });/*
8396  * Based on:
8397  * Ext JS Library 1.1.1
8398  * Copyright(c) 2006-2007, Ext JS, LLC.
8399  *
8400  * Originally Released Under LGPL - original licence link has changed is not relivant.
8401  *
8402  * Fork - LGPL
8403  * <script type="text/javascript">
8404  */
8405
8406 /**
8407  * @class Roo.Resizable
8408  * @extends Roo.util.Observable
8409  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8410  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8411  * 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
8412  * the element will be wrapped for you automatically.</p>
8413  * <p>Here is the list of valid resize handles:</p>
8414  * <pre>
8415 Value   Description
8416 ------  -------------------
8417  'n'     north
8418  's'     south
8419  'e'     east
8420  'w'     west
8421  'nw'    northwest
8422  'sw'    southwest
8423  'se'    southeast
8424  'ne'    northeast
8425  'hd'    horizontal drag
8426  'all'   all
8427 </pre>
8428  * <p>Here's an example showing the creation of a typical Resizable:</p>
8429  * <pre><code>
8430 var resizer = new Roo.Resizable("element-id", {
8431     handles: 'all',
8432     minWidth: 200,
8433     minHeight: 100,
8434     maxWidth: 500,
8435     maxHeight: 400,
8436     pinned: true
8437 });
8438 resizer.on("resize", myHandler);
8439 </code></pre>
8440  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8441  * resizer.east.setDisplayed(false);</p>
8442  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8443  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8444  * resize operation's new size (defaults to [0, 0])
8445  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8446  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8447  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8448  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8449  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8450  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8451  * @cfg {Number} width The width of the element in pixels (defaults to null)
8452  * @cfg {Number} height The height of the element in pixels (defaults to null)
8453  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8454  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8455  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8456  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8457  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8458  * in favor of the handles config option (defaults to false)
8459  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8460  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8461  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8462  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8463  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8464  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8465  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8466  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8467  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8468  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8469  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8470  * @constructor
8471  * Create a new resizable component
8472  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8473  * @param {Object} config configuration options
8474   */
8475 Roo.Resizable = function(el, config)
8476 {
8477     this.el = Roo.get(el);
8478
8479     if(config && config.wrap){
8480         config.resizeChild = this.el;
8481         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8482         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8483         this.el.setStyle("overflow", "hidden");
8484         this.el.setPositioning(config.resizeChild.getPositioning());
8485         config.resizeChild.clearPositioning();
8486         if(!config.width || !config.height){
8487             var csize = config.resizeChild.getSize();
8488             this.el.setSize(csize.width, csize.height);
8489         }
8490         if(config.pinned && !config.adjustments){
8491             config.adjustments = "auto";
8492         }
8493     }
8494
8495     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8496     this.proxy.unselectable();
8497     this.proxy.enableDisplayMode('block');
8498
8499     Roo.apply(this, config);
8500
8501     if(this.pinned){
8502         this.disableTrackOver = true;
8503         this.el.addClass("x-resizable-pinned");
8504     }
8505     // if the element isn't positioned, make it relative
8506     var position = this.el.getStyle("position");
8507     if(position != "absolute" && position != "fixed"){
8508         this.el.setStyle("position", "relative");
8509     }
8510     if(!this.handles){ // no handles passed, must be legacy style
8511         this.handles = 's,e,se';
8512         if(this.multiDirectional){
8513             this.handles += ',n,w';
8514         }
8515     }
8516     if(this.handles == "all"){
8517         this.handles = "n s e w ne nw se sw";
8518     }
8519     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8520     var ps = Roo.Resizable.positions;
8521     for(var i = 0, len = hs.length; i < len; i++){
8522         if(hs[i] && ps[hs[i]]){
8523             var pos = ps[hs[i]];
8524             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8525         }
8526     }
8527     // legacy
8528     this.corner = this.southeast;
8529     
8530     // updateBox = the box can move..
8531     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8532         this.updateBox = true;
8533     }
8534
8535     this.activeHandle = null;
8536
8537     if(this.resizeChild){
8538         if(typeof this.resizeChild == "boolean"){
8539             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8540         }else{
8541             this.resizeChild = Roo.get(this.resizeChild, true);
8542         }
8543     }
8544     
8545     if(this.adjustments == "auto"){
8546         var rc = this.resizeChild;
8547         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8548         if(rc && (hw || hn)){
8549             rc.position("relative");
8550             rc.setLeft(hw ? hw.el.getWidth() : 0);
8551             rc.setTop(hn ? hn.el.getHeight() : 0);
8552         }
8553         this.adjustments = [
8554             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8555             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8556         ];
8557     }
8558
8559     if(this.draggable){
8560         this.dd = this.dynamic ?
8561             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8562         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8563     }
8564
8565     // public events
8566     this.addEvents({
8567         /**
8568          * @event beforeresize
8569          * Fired before resize is allowed. Set enabled to false to cancel resize.
8570          * @param {Roo.Resizable} this
8571          * @param {Roo.EventObject} e The mousedown event
8572          */
8573         "beforeresize" : true,
8574         /**
8575          * @event resizing
8576          * Fired a resizing.
8577          * @param {Roo.Resizable} this
8578          * @param {Number} x The new x position
8579          * @param {Number} y The new y position
8580          * @param {Number} w The new w width
8581          * @param {Number} h The new h hight
8582          * @param {Roo.EventObject} e The mouseup event
8583          */
8584         "resizing" : true,
8585         /**
8586          * @event resize
8587          * Fired after a resize.
8588          * @param {Roo.Resizable} this
8589          * @param {Number} width The new width
8590          * @param {Number} height The new height
8591          * @param {Roo.EventObject} e The mouseup event
8592          */
8593         "resize" : true
8594     });
8595
8596     if(this.width !== null && this.height !== null){
8597         this.resizeTo(this.width, this.height);
8598     }else{
8599         this.updateChildSize();
8600     }
8601     if(Roo.isIE){
8602         this.el.dom.style.zoom = 1;
8603     }
8604     Roo.Resizable.superclass.constructor.call(this);
8605 };
8606
8607 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8608         resizeChild : false,
8609         adjustments : [0, 0],
8610         minWidth : 5,
8611         minHeight : 5,
8612         maxWidth : 10000,
8613         maxHeight : 10000,
8614         enabled : true,
8615         animate : false,
8616         duration : .35,
8617         dynamic : false,
8618         handles : false,
8619         multiDirectional : false,
8620         disableTrackOver : false,
8621         easing : 'easeOutStrong',
8622         widthIncrement : 0,
8623         heightIncrement : 0,
8624         pinned : false,
8625         width : null,
8626         height : null,
8627         preserveRatio : false,
8628         transparent: false,
8629         minX: 0,
8630         minY: 0,
8631         draggable: false,
8632
8633         /**
8634          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8635          */
8636         constrainTo: undefined,
8637         /**
8638          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8639          */
8640         resizeRegion: undefined,
8641
8642
8643     /**
8644      * Perform a manual resize
8645      * @param {Number} width
8646      * @param {Number} height
8647      */
8648     resizeTo : function(width, height){
8649         this.el.setSize(width, height);
8650         this.updateChildSize();
8651         this.fireEvent("resize", this, width, height, null);
8652     },
8653
8654     // private
8655     startSizing : function(e, handle){
8656         this.fireEvent("beforeresize", this, e);
8657         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8658
8659             if(!this.overlay){
8660                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8661                 this.overlay.unselectable();
8662                 this.overlay.enableDisplayMode("block");
8663                 this.overlay.on("mousemove", this.onMouseMove, this);
8664                 this.overlay.on("mouseup", this.onMouseUp, this);
8665             }
8666             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8667
8668             this.resizing = true;
8669             this.startBox = this.el.getBox();
8670             this.startPoint = e.getXY();
8671             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8672                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8673
8674             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8675             this.overlay.show();
8676
8677             if(this.constrainTo) {
8678                 var ct = Roo.get(this.constrainTo);
8679                 this.resizeRegion = ct.getRegion().adjust(
8680                     ct.getFrameWidth('t'),
8681                     ct.getFrameWidth('l'),
8682                     -ct.getFrameWidth('b'),
8683                     -ct.getFrameWidth('r')
8684                 );
8685             }
8686
8687             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8688             this.proxy.show();
8689             this.proxy.setBox(this.startBox);
8690             if(!this.dynamic){
8691                 this.proxy.setStyle('visibility', 'visible');
8692             }
8693         }
8694     },
8695
8696     // private
8697     onMouseDown : function(handle, e){
8698         if(this.enabled){
8699             e.stopEvent();
8700             this.activeHandle = handle;
8701             this.startSizing(e, handle);
8702         }
8703     },
8704
8705     // private
8706     onMouseUp : function(e){
8707         var size = this.resizeElement();
8708         this.resizing = false;
8709         this.handleOut();
8710         this.overlay.hide();
8711         this.proxy.hide();
8712         this.fireEvent("resize", this, size.width, size.height, e);
8713     },
8714
8715     // private
8716     updateChildSize : function(){
8717         
8718         if(this.resizeChild){
8719             var el = this.el;
8720             var child = this.resizeChild;
8721             var adj = this.adjustments;
8722             if(el.dom.offsetWidth){
8723                 var b = el.getSize(true);
8724                 child.setSize(b.width+adj[0], b.height+adj[1]);
8725             }
8726             // Second call here for IE
8727             // The first call enables instant resizing and
8728             // the second call corrects scroll bars if they
8729             // exist
8730             if(Roo.isIE){
8731                 setTimeout(function(){
8732                     if(el.dom.offsetWidth){
8733                         var b = el.getSize(true);
8734                         child.setSize(b.width+adj[0], b.height+adj[1]);
8735                     }
8736                 }, 10);
8737             }
8738         }
8739     },
8740
8741     // private
8742     snap : function(value, inc, min){
8743         if(!inc || !value) {
8744             return value;
8745         }
8746         var newValue = value;
8747         var m = value % inc;
8748         if(m > 0){
8749             if(m > (inc/2)){
8750                 newValue = value + (inc-m);
8751             }else{
8752                 newValue = value - m;
8753             }
8754         }
8755         return Math.max(min, newValue);
8756     },
8757
8758     // private
8759     resizeElement : function(){
8760         var box = this.proxy.getBox();
8761         if(this.updateBox){
8762             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8763         }else{
8764             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8765         }
8766         this.updateChildSize();
8767         if(!this.dynamic){
8768             this.proxy.hide();
8769         }
8770         return box;
8771     },
8772
8773     // private
8774     constrain : function(v, diff, m, mx){
8775         if(v - diff < m){
8776             diff = v - m;
8777         }else if(v - diff > mx){
8778             diff = mx - v;
8779         }
8780         return diff;
8781     },
8782
8783     // private
8784     onMouseMove : function(e){
8785         
8786         if(this.enabled){
8787             try{// try catch so if something goes wrong the user doesn't get hung
8788
8789             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8790                 return;
8791             }
8792
8793             //var curXY = this.startPoint;
8794             var curSize = this.curSize || this.startBox;
8795             var x = this.startBox.x, y = this.startBox.y;
8796             var ox = x, oy = y;
8797             var w = curSize.width, h = curSize.height;
8798             var ow = w, oh = h;
8799             var mw = this.minWidth, mh = this.minHeight;
8800             var mxw = this.maxWidth, mxh = this.maxHeight;
8801             var wi = this.widthIncrement;
8802             var hi = this.heightIncrement;
8803
8804             var eventXY = e.getXY();
8805             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8806             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8807
8808             var pos = this.activeHandle.position;
8809
8810             switch(pos){
8811                 case "east":
8812                     w += diffX;
8813                     w = Math.min(Math.max(mw, w), mxw);
8814                     break;
8815              
8816                 case "south":
8817                     h += diffY;
8818                     h = Math.min(Math.max(mh, h), mxh);
8819                     break;
8820                 case "southeast":
8821                     w += diffX;
8822                     h += diffY;
8823                     w = Math.min(Math.max(mw, w), mxw);
8824                     h = Math.min(Math.max(mh, h), mxh);
8825                     break;
8826                 case "north":
8827                     diffY = this.constrain(h, diffY, mh, mxh);
8828                     y += diffY;
8829                     h -= diffY;
8830                     break;
8831                 case "hdrag":
8832                     
8833                     if (wi) {
8834                         var adiffX = Math.abs(diffX);
8835                         var sub = (adiffX % wi); // how much 
8836                         if (sub > (wi/2)) { // far enough to snap
8837                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8838                         } else {
8839                             // remove difference.. 
8840                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8841                         }
8842                     }
8843                     x += diffX;
8844                     x = Math.max(this.minX, x);
8845                     break;
8846                 case "west":
8847                     diffX = this.constrain(w, diffX, mw, mxw);
8848                     x += diffX;
8849                     w -= diffX;
8850                     break;
8851                 case "northeast":
8852                     w += diffX;
8853                     w = Math.min(Math.max(mw, w), mxw);
8854                     diffY = this.constrain(h, diffY, mh, mxh);
8855                     y += diffY;
8856                     h -= diffY;
8857                     break;
8858                 case "northwest":
8859                     diffX = this.constrain(w, diffX, mw, mxw);
8860                     diffY = this.constrain(h, diffY, mh, mxh);
8861                     y += diffY;
8862                     h -= diffY;
8863                     x += diffX;
8864                     w -= diffX;
8865                     break;
8866                case "southwest":
8867                     diffX = this.constrain(w, diffX, mw, mxw);
8868                     h += diffY;
8869                     h = Math.min(Math.max(mh, h), mxh);
8870                     x += diffX;
8871                     w -= diffX;
8872                     break;
8873             }
8874
8875             var sw = this.snap(w, wi, mw);
8876             var sh = this.snap(h, hi, mh);
8877             if(sw != w || sh != h){
8878                 switch(pos){
8879                     case "northeast":
8880                         y -= sh - h;
8881                     break;
8882                     case "north":
8883                         y -= sh - h;
8884                         break;
8885                     case "southwest":
8886                         x -= sw - w;
8887                     break;
8888                     case "west":
8889                         x -= sw - w;
8890                         break;
8891                     case "northwest":
8892                         x -= sw - w;
8893                         y -= sh - h;
8894                     break;
8895                 }
8896                 w = sw;
8897                 h = sh;
8898             }
8899
8900             if(this.preserveRatio){
8901                 switch(pos){
8902                     case "southeast":
8903                     case "east":
8904                         h = oh * (w/ow);
8905                         h = Math.min(Math.max(mh, h), mxh);
8906                         w = ow * (h/oh);
8907                        break;
8908                     case "south":
8909                         w = ow * (h/oh);
8910                         w = Math.min(Math.max(mw, w), mxw);
8911                         h = oh * (w/ow);
8912                         break;
8913                     case "northeast":
8914                         w = ow * (h/oh);
8915                         w = Math.min(Math.max(mw, w), mxw);
8916                         h = oh * (w/ow);
8917                     break;
8918                     case "north":
8919                         var tw = w;
8920                         w = ow * (h/oh);
8921                         w = Math.min(Math.max(mw, w), mxw);
8922                         h = oh * (w/ow);
8923                         x += (tw - w) / 2;
8924                         break;
8925                     case "southwest":
8926                         h = oh * (w/ow);
8927                         h = Math.min(Math.max(mh, h), mxh);
8928                         var tw = w;
8929                         w = ow * (h/oh);
8930                         x += tw - w;
8931                         break;
8932                     case "west":
8933                         var th = h;
8934                         h = oh * (w/ow);
8935                         h = Math.min(Math.max(mh, h), mxh);
8936                         y += (th - h) / 2;
8937                         var tw = w;
8938                         w = ow * (h/oh);
8939                         x += tw - w;
8940                        break;
8941                     case "northwest":
8942                         var tw = w;
8943                         var th = h;
8944                         h = oh * (w/ow);
8945                         h = Math.min(Math.max(mh, h), mxh);
8946                         w = ow * (h/oh);
8947                         y += th - h;
8948                         x += tw - w;
8949                        break;
8950
8951                 }
8952             }
8953             if (pos == 'hdrag') {
8954                 w = ow;
8955             }
8956             this.proxy.setBounds(x, y, w, h);
8957             if(this.dynamic){
8958                 this.resizeElement();
8959             }
8960             }catch(e){}
8961         }
8962         this.fireEvent("resizing", this, x, y, w, h, e);
8963     },
8964
8965     // private
8966     handleOver : function(){
8967         if(this.enabled){
8968             this.el.addClass("x-resizable-over");
8969         }
8970     },
8971
8972     // private
8973     handleOut : function(){
8974         if(!this.resizing){
8975             this.el.removeClass("x-resizable-over");
8976         }
8977     },
8978
8979     /**
8980      * Returns the element this component is bound to.
8981      * @return {Roo.Element}
8982      */
8983     getEl : function(){
8984         return this.el;
8985     },
8986
8987     /**
8988      * Returns the resizeChild element (or null).
8989      * @return {Roo.Element}
8990      */
8991     getResizeChild : function(){
8992         return this.resizeChild;
8993     },
8994     groupHandler : function()
8995     {
8996         
8997     },
8998     /**
8999      * Destroys this resizable. If the element was wrapped and
9000      * removeEl is not true then the element remains.
9001      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9002      */
9003     destroy : function(removeEl){
9004         this.proxy.remove();
9005         if(this.overlay){
9006             this.overlay.removeAllListeners();
9007             this.overlay.remove();
9008         }
9009         var ps = Roo.Resizable.positions;
9010         for(var k in ps){
9011             if(typeof ps[k] != "function" && this[ps[k]]){
9012                 var h = this[ps[k]];
9013                 h.el.removeAllListeners();
9014                 h.el.remove();
9015             }
9016         }
9017         if(removeEl){
9018             this.el.update("");
9019             this.el.remove();
9020         }
9021     }
9022 });
9023
9024 // private
9025 // hash to map config positions to true positions
9026 Roo.Resizable.positions = {
9027     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
9028     hd: "hdrag"
9029 };
9030
9031 // private
9032 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9033     if(!this.tpl){
9034         // only initialize the template if resizable is used
9035         var tpl = Roo.DomHelper.createTemplate(
9036             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9037         );
9038         tpl.compile();
9039         Roo.Resizable.Handle.prototype.tpl = tpl;
9040     }
9041     this.position = pos;
9042     this.rz = rz;
9043     // show north drag fro topdra
9044     var handlepos = pos == 'hdrag' ? 'north' : pos;
9045     
9046     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9047     if (pos == 'hdrag') {
9048         this.el.setStyle('cursor', 'pointer');
9049     }
9050     this.el.unselectable();
9051     if(transparent){
9052         this.el.setOpacity(0);
9053     }
9054     this.el.on("mousedown", this.onMouseDown, this);
9055     if(!disableTrackOver){
9056         this.el.on("mouseover", this.onMouseOver, this);
9057         this.el.on("mouseout", this.onMouseOut, this);
9058     }
9059 };
9060
9061 // private
9062 Roo.Resizable.Handle.prototype = {
9063     afterResize : function(rz){
9064         Roo.log('after?');
9065         // do nothing
9066     },
9067     // private
9068     onMouseDown : function(e){
9069         this.rz.onMouseDown(this, e);
9070     },
9071     // private
9072     onMouseOver : function(e){
9073         this.rz.handleOver(this, e);
9074     },
9075     // private
9076     onMouseOut : function(e){
9077         this.rz.handleOut(this, e);
9078     }
9079 };/*
9080  * Based on:
9081  * Ext JS Library 1.1.1
9082  * Copyright(c) 2006-2007, Ext JS, LLC.
9083  *
9084  * Originally Released Under LGPL - original licence link has changed is not relivant.
9085  *
9086  * Fork - LGPL
9087  * <script type="text/javascript">
9088  */
9089
9090 /**
9091  * @class Roo.Editor
9092  * @extends Roo.Component
9093  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9094  * @constructor
9095  * Create a new Editor
9096  * @param {Roo.form.Field} field The Field object (or descendant)
9097  * @param {Object} config The config object
9098  */
9099 Roo.Editor = function(field, config){
9100     Roo.Editor.superclass.constructor.call(this, config);
9101     this.field = field;
9102     this.addEvents({
9103         /**
9104              * @event beforestartedit
9105              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9106              * false from the handler of this event.
9107              * @param {Editor} this
9108              * @param {Roo.Element} boundEl The underlying element bound to this editor
9109              * @param {Mixed} value The field value being set
9110              */
9111         "beforestartedit" : true,
9112         /**
9113              * @event startedit
9114              * Fires when this editor is displayed
9115              * @param {Roo.Element} boundEl The underlying element bound to this editor
9116              * @param {Mixed} value The starting field value
9117              */
9118         "startedit" : true,
9119         /**
9120              * @event beforecomplete
9121              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9122              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9123              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9124              * event will not fire since no edit actually occurred.
9125              * @param {Editor} this
9126              * @param {Mixed} value The current field value
9127              * @param {Mixed} startValue The original field value
9128              */
9129         "beforecomplete" : true,
9130         /**
9131              * @event complete
9132              * Fires after editing is complete and any changed value has been written to the underlying field.
9133              * @param {Editor} this
9134              * @param {Mixed} value The current field value
9135              * @param {Mixed} startValue The original field value
9136              */
9137         "complete" : true,
9138         /**
9139          * @event specialkey
9140          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9141          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9142          * @param {Roo.form.Field} this
9143          * @param {Roo.EventObject} e The event object
9144          */
9145         "specialkey" : true
9146     });
9147 };
9148
9149 Roo.extend(Roo.Editor, Roo.Component, {
9150     /**
9151      * @cfg {Boolean/String} autosize
9152      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9153      * or "height" to adopt the height only (defaults to false)
9154      */
9155     /**
9156      * @cfg {Boolean} revertInvalid
9157      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9158      * validation fails (defaults to true)
9159      */
9160     /**
9161      * @cfg {Boolean} ignoreNoChange
9162      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9163      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9164      * will never be ignored.
9165      */
9166     /**
9167      * @cfg {Boolean} hideEl
9168      * False to keep the bound element visible while the editor is displayed (defaults to true)
9169      */
9170     /**
9171      * @cfg {Mixed} value
9172      * The data value of the underlying field (defaults to "")
9173      */
9174     value : "",
9175     /**
9176      * @cfg {String} alignment
9177      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9178      */
9179     alignment: "c-c?",
9180     /**
9181      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9182      * for bottom-right shadow (defaults to "frame")
9183      */
9184     shadow : "frame",
9185     /**
9186      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9187      */
9188     constrain : false,
9189     /**
9190      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9191      */
9192     completeOnEnter : false,
9193     /**
9194      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9195      */
9196     cancelOnEsc : false,
9197     /**
9198      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9199      */
9200     updateEl : false,
9201
9202     // private
9203     onRender : function(ct, position){
9204         this.el = new Roo.Layer({
9205             shadow: this.shadow,
9206             cls: "x-editor",
9207             parentEl : ct,
9208             shim : this.shim,
9209             shadowOffset:4,
9210             id: this.id,
9211             constrain: this.constrain
9212         });
9213         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9214         if(this.field.msgTarget != 'title'){
9215             this.field.msgTarget = 'qtip';
9216         }
9217         this.field.render(this.el);
9218         if(Roo.isGecko){
9219             this.field.el.dom.setAttribute('autocomplete', 'off');
9220         }
9221         this.field.on("specialkey", this.onSpecialKey, this);
9222         if(this.swallowKeys){
9223             this.field.el.swallowEvent(['keydown','keypress']);
9224         }
9225         this.field.show();
9226         this.field.on("blur", this.onBlur, this);
9227         if(this.field.grow){
9228             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9229         }
9230     },
9231
9232     onSpecialKey : function(field, e)
9233     {
9234         //Roo.log('editor onSpecialKey');
9235         if(this.completeOnEnter && e.getKey() == e.ENTER){
9236             e.stopEvent();
9237             this.completeEdit();
9238             return;
9239         }
9240         // do not fire special key otherwise it might hide close the editor...
9241         if(e.getKey() == e.ENTER){    
9242             return;
9243         }
9244         if(this.cancelOnEsc && e.getKey() == e.ESC){
9245             this.cancelEdit();
9246             return;
9247         } 
9248         this.fireEvent('specialkey', field, e);
9249     
9250     },
9251
9252     /**
9253      * Starts the editing process and shows the editor.
9254      * @param {String/HTMLElement/Element} el The element to edit
9255      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9256       * to the innerHTML of el.
9257      */
9258     startEdit : function(el, value){
9259         if(this.editing){
9260             this.completeEdit();
9261         }
9262         this.boundEl = Roo.get(el);
9263         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9264         if(!this.rendered){
9265             this.render(this.parentEl || document.body);
9266         }
9267         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9268             return;
9269         }
9270         this.startValue = v;
9271         this.field.setValue(v);
9272         if(this.autoSize){
9273             var sz = this.boundEl.getSize();
9274             switch(this.autoSize){
9275                 case "width":
9276                 this.setSize(sz.width,  "");
9277                 break;
9278                 case "height":
9279                 this.setSize("",  sz.height);
9280                 break;
9281                 default:
9282                 this.setSize(sz.width,  sz.height);
9283             }
9284         }
9285         this.el.alignTo(this.boundEl, this.alignment);
9286         this.editing = true;
9287         if(Roo.QuickTips){
9288             Roo.QuickTips.disable();
9289         }
9290         this.show();
9291     },
9292
9293     /**
9294      * Sets the height and width of this editor.
9295      * @param {Number} width The new width
9296      * @param {Number} height The new height
9297      */
9298     setSize : function(w, h){
9299         this.field.setSize(w, h);
9300         if(this.el){
9301             this.el.sync();
9302         }
9303     },
9304
9305     /**
9306      * Realigns the editor to the bound field based on the current alignment config value.
9307      */
9308     realign : function(){
9309         this.el.alignTo(this.boundEl, this.alignment);
9310     },
9311
9312     /**
9313      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9314      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9315      */
9316     completeEdit : function(remainVisible){
9317         if(!this.editing){
9318             return;
9319         }
9320         var v = this.getValue();
9321         if(this.revertInvalid !== false && !this.field.isValid()){
9322             v = this.startValue;
9323             this.cancelEdit(true);
9324         }
9325         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9326             this.editing = false;
9327             this.hide();
9328             return;
9329         }
9330         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9331             this.editing = false;
9332             if(this.updateEl && this.boundEl){
9333                 this.boundEl.update(v);
9334             }
9335             if(remainVisible !== true){
9336                 this.hide();
9337             }
9338             this.fireEvent("complete", this, v, this.startValue);
9339         }
9340     },
9341
9342     // private
9343     onShow : function(){
9344         this.el.show();
9345         if(this.hideEl !== false){
9346             this.boundEl.hide();
9347         }
9348         this.field.show();
9349         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9350             this.fixIEFocus = true;
9351             this.deferredFocus.defer(50, this);
9352         }else{
9353             this.field.focus();
9354         }
9355         this.fireEvent("startedit", this.boundEl, this.startValue);
9356     },
9357
9358     deferredFocus : function(){
9359         if(this.editing){
9360             this.field.focus();
9361         }
9362     },
9363
9364     /**
9365      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9366      * reverted to the original starting value.
9367      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9368      * cancel (defaults to false)
9369      */
9370     cancelEdit : function(remainVisible){
9371         if(this.editing){
9372             this.setValue(this.startValue);
9373             if(remainVisible !== true){
9374                 this.hide();
9375             }
9376         }
9377     },
9378
9379     // private
9380     onBlur : function(){
9381         if(this.allowBlur !== true && this.editing){
9382             this.completeEdit();
9383         }
9384     },
9385
9386     // private
9387     onHide : function(){
9388         if(this.editing){
9389             this.completeEdit();
9390             return;
9391         }
9392         this.field.blur();
9393         if(this.field.collapse){
9394             this.field.collapse();
9395         }
9396         this.el.hide();
9397         if(this.hideEl !== false){
9398             this.boundEl.show();
9399         }
9400         if(Roo.QuickTips){
9401             Roo.QuickTips.enable();
9402         }
9403     },
9404
9405     /**
9406      * Sets the data value of the editor
9407      * @param {Mixed} value Any valid value supported by the underlying field
9408      */
9409     setValue : function(v){
9410         this.field.setValue(v);
9411     },
9412
9413     /**
9414      * Gets the data value of the editor
9415      * @return {Mixed} The data value
9416      */
9417     getValue : function(){
9418         return this.field.getValue();
9419     }
9420 });/*
9421  * Based on:
9422  * Ext JS Library 1.1.1
9423  * Copyright(c) 2006-2007, Ext JS, LLC.
9424  *
9425  * Originally Released Under LGPL - original licence link has changed is not relivant.
9426  *
9427  * Fork - LGPL
9428  * <script type="text/javascript">
9429  */
9430  
9431 /**
9432  * @class Roo.BasicDialog
9433  * @extends Roo.util.Observable
9434  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9435  * <pre><code>
9436 var dlg = new Roo.BasicDialog("my-dlg", {
9437     height: 200,
9438     width: 300,
9439     minHeight: 100,
9440     minWidth: 150,
9441     modal: true,
9442     proxyDrag: true,
9443     shadow: true
9444 });
9445 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9446 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9447 dlg.addButton('Cancel', dlg.hide, dlg);
9448 dlg.show();
9449 </code></pre>
9450   <b>A Dialog should always be a direct child of the body element.</b>
9451  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9452  * @cfg {String} title Default text to display in the title bar (defaults to null)
9453  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9454  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9455  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9456  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9457  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9458  * (defaults to null with no animation)
9459  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9460  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9461  * property for valid values (defaults to 'all')
9462  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9463  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9464  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9465  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9466  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9467  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9468  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9469  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9470  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9471  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9472  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9473  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9474  * draggable = true (defaults to false)
9475  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9476  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9477  * shadow (defaults to false)
9478  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9479  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9480  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9481  * @cfg {Array} buttons Array of buttons
9482  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9483  * @constructor
9484  * Create a new BasicDialog.
9485  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9486  * @param {Object} config Configuration options
9487  */
9488 Roo.BasicDialog = function(el, config){
9489     this.el = Roo.get(el);
9490     var dh = Roo.DomHelper;
9491     if(!this.el && config && config.autoCreate){
9492         if(typeof config.autoCreate == "object"){
9493             if(!config.autoCreate.id){
9494                 config.autoCreate.id = el;
9495             }
9496             this.el = dh.append(document.body,
9497                         config.autoCreate, true);
9498         }else{
9499             this.el = dh.append(document.body,
9500                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9501         }
9502     }
9503     el = this.el;
9504     el.setDisplayed(true);
9505     el.hide = this.hideAction;
9506     this.id = el.id;
9507     el.addClass("x-dlg");
9508
9509     Roo.apply(this, config);
9510
9511     this.proxy = el.createProxy("x-dlg-proxy");
9512     this.proxy.hide = this.hideAction;
9513     this.proxy.setOpacity(.5);
9514     this.proxy.hide();
9515
9516     if(config.width){
9517         el.setWidth(config.width);
9518     }
9519     if(config.height){
9520         el.setHeight(config.height);
9521     }
9522     this.size = el.getSize();
9523     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9524         this.xy = [config.x,config.y];
9525     }else{
9526         this.xy = el.getCenterXY(true);
9527     }
9528     /** The header element @type Roo.Element */
9529     this.header = el.child("> .x-dlg-hd");
9530     /** The body element @type Roo.Element */
9531     this.body = el.child("> .x-dlg-bd");
9532     /** The footer element @type Roo.Element */
9533     this.footer = el.child("> .x-dlg-ft");
9534
9535     if(!this.header){
9536         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9537     }
9538     if(!this.body){
9539         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9540     }
9541
9542     this.header.unselectable();
9543     if(this.title){
9544         this.header.update(this.title);
9545     }
9546     // this element allows the dialog to be focused for keyboard event
9547     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9548     this.focusEl.swallowEvent("click", true);
9549
9550     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9551
9552     // wrap the body and footer for special rendering
9553     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9554     if(this.footer){
9555         this.bwrap.dom.appendChild(this.footer.dom);
9556     }
9557
9558     this.bg = this.el.createChild({
9559         tag: "div", cls:"x-dlg-bg",
9560         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9561     });
9562     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9563
9564
9565     if(this.autoScroll !== false && !this.autoTabs){
9566         this.body.setStyle("overflow", "auto");
9567     }
9568
9569     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9570
9571     if(this.closable !== false){
9572         this.el.addClass("x-dlg-closable");
9573         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9574         this.close.on("click", this.closeClick, this);
9575         this.close.addClassOnOver("x-dlg-close-over");
9576     }
9577     if(this.collapsible !== false){
9578         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9579         this.collapseBtn.on("click", this.collapseClick, this);
9580         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9581         this.header.on("dblclick", this.collapseClick, this);
9582     }
9583     if(this.resizable !== false){
9584         this.el.addClass("x-dlg-resizable");
9585         this.resizer = new Roo.Resizable(el, {
9586             minWidth: this.minWidth || 80,
9587             minHeight:this.minHeight || 80,
9588             handles: this.resizeHandles || "all",
9589             pinned: true
9590         });
9591         this.resizer.on("beforeresize", this.beforeResize, this);
9592         this.resizer.on("resize", this.onResize, this);
9593     }
9594     if(this.draggable !== false){
9595         el.addClass("x-dlg-draggable");
9596         if (!this.proxyDrag) {
9597             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9598         }
9599         else {
9600             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9601         }
9602         dd.setHandleElId(this.header.id);
9603         dd.endDrag = this.endMove.createDelegate(this);
9604         dd.startDrag = this.startMove.createDelegate(this);
9605         dd.onDrag = this.onDrag.createDelegate(this);
9606         dd.scroll = false;
9607         this.dd = dd;
9608     }
9609     if(this.modal){
9610         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9611         this.mask.enableDisplayMode("block");
9612         this.mask.hide();
9613         this.el.addClass("x-dlg-modal");
9614     }
9615     if(this.shadow){
9616         this.shadow = new Roo.Shadow({
9617             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9618             offset : this.shadowOffset
9619         });
9620     }else{
9621         this.shadowOffset = 0;
9622     }
9623     if(Roo.useShims && this.shim !== false){
9624         this.shim = this.el.createShim();
9625         this.shim.hide = this.hideAction;
9626         this.shim.hide();
9627     }else{
9628         this.shim = false;
9629     }
9630     if(this.autoTabs){
9631         this.initTabs();
9632     }
9633     if (this.buttons) { 
9634         var bts= this.buttons;
9635         this.buttons = [];
9636         Roo.each(bts, function(b) {
9637             this.addButton(b);
9638         }, this);
9639     }
9640     
9641     
9642     this.addEvents({
9643         /**
9644          * @event keydown
9645          * Fires when a key is pressed
9646          * @param {Roo.BasicDialog} this
9647          * @param {Roo.EventObject} e
9648          */
9649         "keydown" : true,
9650         /**
9651          * @event move
9652          * Fires when this dialog is moved by the user.
9653          * @param {Roo.BasicDialog} this
9654          * @param {Number} x The new page X
9655          * @param {Number} y The new page Y
9656          */
9657         "move" : true,
9658         /**
9659          * @event resize
9660          * Fires when this dialog is resized by the user.
9661          * @param {Roo.BasicDialog} this
9662          * @param {Number} width The new width
9663          * @param {Number} height The new height
9664          */
9665         "resize" : true,
9666         /**
9667          * @event beforehide
9668          * Fires before this dialog is hidden.
9669          * @param {Roo.BasicDialog} this
9670          */
9671         "beforehide" : true,
9672         /**
9673          * @event hide
9674          * Fires when this dialog is hidden.
9675          * @param {Roo.BasicDialog} this
9676          */
9677         "hide" : true,
9678         /**
9679          * @event beforeshow
9680          * Fires before this dialog is shown.
9681          * @param {Roo.BasicDialog} this
9682          */
9683         "beforeshow" : true,
9684         /**
9685          * @event show
9686          * Fires when this dialog is shown.
9687          * @param {Roo.BasicDialog} this
9688          */
9689         "show" : true
9690     });
9691     el.on("keydown", this.onKeyDown, this);
9692     el.on("mousedown", this.toFront, this);
9693     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9694     this.el.hide();
9695     Roo.DialogManager.register(this);
9696     Roo.BasicDialog.superclass.constructor.call(this);
9697 };
9698
9699 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9700     shadowOffset: Roo.isIE ? 6 : 5,
9701     minHeight: 80,
9702     minWidth: 200,
9703     minButtonWidth: 75,
9704     defaultButton: null,
9705     buttonAlign: "right",
9706     tabTag: 'div',
9707     firstShow: true,
9708
9709     /**
9710      * Sets the dialog title text
9711      * @param {String} text The title text to display
9712      * @return {Roo.BasicDialog} this
9713      */
9714     setTitle : function(text){
9715         this.header.update(text);
9716         return this;
9717     },
9718
9719     // private
9720     closeClick : function(){
9721         this.hide();
9722     },
9723
9724     // private
9725     collapseClick : function(){
9726         this[this.collapsed ? "expand" : "collapse"]();
9727     },
9728
9729     /**
9730      * Collapses the dialog to its minimized state (only the title bar is visible).
9731      * Equivalent to the user clicking the collapse dialog button.
9732      */
9733     collapse : function(){
9734         if(!this.collapsed){
9735             this.collapsed = true;
9736             this.el.addClass("x-dlg-collapsed");
9737             this.restoreHeight = this.el.getHeight();
9738             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9739         }
9740     },
9741
9742     /**
9743      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9744      * clicking the expand dialog button.
9745      */
9746     expand : function(){
9747         if(this.collapsed){
9748             this.collapsed = false;
9749             this.el.removeClass("x-dlg-collapsed");
9750             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9751         }
9752     },
9753
9754     /**
9755      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9756      * @return {Roo.TabPanel} The tabs component
9757      */
9758     initTabs : function(){
9759         var tabs = this.getTabs();
9760         while(tabs.getTab(0)){
9761             tabs.removeTab(0);
9762         }
9763         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9764             var dom = el.dom;
9765             tabs.addTab(Roo.id(dom), dom.title);
9766             dom.title = "";
9767         });
9768         tabs.activate(0);
9769         return tabs;
9770     },
9771
9772     // private
9773     beforeResize : function(){
9774         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9775     },
9776
9777     // private
9778     onResize : function(){
9779         this.refreshSize();
9780         this.syncBodyHeight();
9781         this.adjustAssets();
9782         this.focus();
9783         this.fireEvent("resize", this, this.size.width, this.size.height);
9784     },
9785
9786     // private
9787     onKeyDown : function(e){
9788         if(this.isVisible()){
9789             this.fireEvent("keydown", this, e);
9790         }
9791     },
9792
9793     /**
9794      * Resizes the dialog.
9795      * @param {Number} width
9796      * @param {Number} height
9797      * @return {Roo.BasicDialog} this
9798      */
9799     resizeTo : function(width, height){
9800         this.el.setSize(width, height);
9801         this.size = {width: width, height: height};
9802         this.syncBodyHeight();
9803         if(this.fixedcenter){
9804             this.center();
9805         }
9806         if(this.isVisible()){
9807             this.constrainXY();
9808             this.adjustAssets();
9809         }
9810         this.fireEvent("resize", this, width, height);
9811         return this;
9812     },
9813
9814
9815     /**
9816      * Resizes the dialog to fit the specified content size.
9817      * @param {Number} width
9818      * @param {Number} height
9819      * @return {Roo.BasicDialog} this
9820      */
9821     setContentSize : function(w, h){
9822         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9823         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9824         //if(!this.el.isBorderBox()){
9825             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9826             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9827         //}
9828         if(this.tabs){
9829             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9830             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9831         }
9832         this.resizeTo(w, h);
9833         return this;
9834     },
9835
9836     /**
9837      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9838      * executed in response to a particular key being pressed while the dialog is active.
9839      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9840      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9841      * @param {Function} fn The function to call
9842      * @param {Object} scope (optional) The scope of the function
9843      * @return {Roo.BasicDialog} this
9844      */
9845     addKeyListener : function(key, fn, scope){
9846         var keyCode, shift, ctrl, alt;
9847         if(typeof key == "object" && !(key instanceof Array)){
9848             keyCode = key["key"];
9849             shift = key["shift"];
9850             ctrl = key["ctrl"];
9851             alt = key["alt"];
9852         }else{
9853             keyCode = key;
9854         }
9855         var handler = function(dlg, e){
9856             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9857                 var k = e.getKey();
9858                 if(keyCode instanceof Array){
9859                     for(var i = 0, len = keyCode.length; i < len; i++){
9860                         if(keyCode[i] == k){
9861                           fn.call(scope || window, dlg, k, e);
9862                           return;
9863                         }
9864                     }
9865                 }else{
9866                     if(k == keyCode){
9867                         fn.call(scope || window, dlg, k, e);
9868                     }
9869                 }
9870             }
9871         };
9872         this.on("keydown", handler);
9873         return this;
9874     },
9875
9876     /**
9877      * Returns the TabPanel component (creates it if it doesn't exist).
9878      * Note: If you wish to simply check for the existence of tabs without creating them,
9879      * check for a null 'tabs' property.
9880      * @return {Roo.TabPanel} The tabs component
9881      */
9882     getTabs : function(){
9883         if(!this.tabs){
9884             this.el.addClass("x-dlg-auto-tabs");
9885             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9886             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9887         }
9888         return this.tabs;
9889     },
9890
9891     /**
9892      * Adds a button to the footer section of the dialog.
9893      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9894      * object or a valid Roo.DomHelper element config
9895      * @param {Function} handler The function called when the button is clicked
9896      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9897      * @return {Roo.Button} The new button
9898      */
9899     addButton : function(config, handler, scope){
9900         var dh = Roo.DomHelper;
9901         if(!this.footer){
9902             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9903         }
9904         if(!this.btnContainer){
9905             var tb = this.footer.createChild({
9906
9907                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9908                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9909             }, null, true);
9910             this.btnContainer = tb.firstChild.firstChild.firstChild;
9911         }
9912         var bconfig = {
9913             handler: handler,
9914             scope: scope,
9915             minWidth: this.minButtonWidth,
9916             hideParent:true
9917         };
9918         if(typeof config == "string"){
9919             bconfig.text = config;
9920         }else{
9921             if(config.tag){
9922                 bconfig.dhconfig = config;
9923             }else{
9924                 Roo.apply(bconfig, config);
9925             }
9926         }
9927         var fc = false;
9928         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9929             bconfig.position = Math.max(0, bconfig.position);
9930             fc = this.btnContainer.childNodes[bconfig.position];
9931         }
9932          
9933         var btn = new Roo.Button(
9934             fc ? 
9935                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9936                 : this.btnContainer.appendChild(document.createElement("td")),
9937             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9938             bconfig
9939         );
9940         this.syncBodyHeight();
9941         if(!this.buttons){
9942             /**
9943              * Array of all the buttons that have been added to this dialog via addButton
9944              * @type Array
9945              */
9946             this.buttons = [];
9947         }
9948         this.buttons.push(btn);
9949         return btn;
9950     },
9951
9952     /**
9953      * Sets the default button to be focused when the dialog is displayed.
9954      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9955      * @return {Roo.BasicDialog} this
9956      */
9957     setDefaultButton : function(btn){
9958         this.defaultButton = btn;
9959         return this;
9960     },
9961
9962     // private
9963     getHeaderFooterHeight : function(safe){
9964         var height = 0;
9965         if(this.header){
9966            height += this.header.getHeight();
9967         }
9968         if(this.footer){
9969            var fm = this.footer.getMargins();
9970             height += (this.footer.getHeight()+fm.top+fm.bottom);
9971         }
9972         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9973         height += this.centerBg.getPadding("tb");
9974         return height;
9975     },
9976
9977     // private
9978     syncBodyHeight : function()
9979     {
9980         var bd = this.body, // the text
9981             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9982             bw = this.bwrap;
9983         var height = this.size.height - this.getHeaderFooterHeight(false);
9984         bd.setHeight(height-bd.getMargins("tb"));
9985         var hh = this.header.getHeight();
9986         var h = this.size.height-hh;
9987         cb.setHeight(h);
9988         
9989         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9990         bw.setHeight(h-cb.getPadding("tb"));
9991         
9992         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9993         bd.setWidth(bw.getWidth(true));
9994         if(this.tabs){
9995             this.tabs.syncHeight();
9996             if(Roo.isIE){
9997                 this.tabs.el.repaint();
9998             }
9999         }
10000     },
10001
10002     /**
10003      * Restores the previous state of the dialog if Roo.state is configured.
10004      * @return {Roo.BasicDialog} this
10005      */
10006     restoreState : function(){
10007         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
10008         if(box && box.width){
10009             this.xy = [box.x, box.y];
10010             this.resizeTo(box.width, box.height);
10011         }
10012         return this;
10013     },
10014
10015     // private
10016     beforeShow : function(){
10017         this.expand();
10018         if(this.fixedcenter){
10019             this.xy = this.el.getCenterXY(true);
10020         }
10021         if(this.modal){
10022             Roo.get(document.body).addClass("x-body-masked");
10023             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10024             this.mask.show();
10025         }
10026         this.constrainXY();
10027     },
10028
10029     // private
10030     animShow : function(){
10031         var b = Roo.get(this.animateTarget).getBox();
10032         this.proxy.setSize(b.width, b.height);
10033         this.proxy.setLocation(b.x, b.y);
10034         this.proxy.show();
10035         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10036                     true, .35, this.showEl.createDelegate(this));
10037     },
10038
10039     /**
10040      * Shows the dialog.
10041      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10042      * @return {Roo.BasicDialog} this
10043      */
10044     show : function(animateTarget){
10045         if (this.fireEvent("beforeshow", this) === false){
10046             return;
10047         }
10048         if(this.syncHeightBeforeShow){
10049             this.syncBodyHeight();
10050         }else if(this.firstShow){
10051             this.firstShow = false;
10052             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10053         }
10054         this.animateTarget = animateTarget || this.animateTarget;
10055         if(!this.el.isVisible()){
10056             this.beforeShow();
10057             if(this.animateTarget && Roo.get(this.animateTarget)){
10058                 this.animShow();
10059             }else{
10060                 this.showEl();
10061             }
10062         }
10063         return this;
10064     },
10065
10066     // private
10067     showEl : function(){
10068         this.proxy.hide();
10069         this.el.setXY(this.xy);
10070         this.el.show();
10071         this.adjustAssets(true);
10072         this.toFront();
10073         this.focus();
10074         // IE peekaboo bug - fix found by Dave Fenwick
10075         if(Roo.isIE){
10076             this.el.repaint();
10077         }
10078         this.fireEvent("show", this);
10079     },
10080
10081     /**
10082      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10083      * dialog itself will receive focus.
10084      */
10085     focus : function(){
10086         if(this.defaultButton){
10087             this.defaultButton.focus();
10088         }else{
10089             this.focusEl.focus();
10090         }
10091     },
10092
10093     // private
10094     constrainXY : function(){
10095         if(this.constraintoviewport !== false){
10096             if(!this.viewSize){
10097                 if(this.container){
10098                     var s = this.container.getSize();
10099                     this.viewSize = [s.width, s.height];
10100                 }else{
10101                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10102                 }
10103             }
10104             var s = Roo.get(this.container||document).getScroll();
10105
10106             var x = this.xy[0], y = this.xy[1];
10107             var w = this.size.width, h = this.size.height;
10108             var vw = this.viewSize[0], vh = this.viewSize[1];
10109             // only move it if it needs it
10110             var moved = false;
10111             // first validate right/bottom
10112             if(x + w > vw+s.left){
10113                 x = vw - w;
10114                 moved = true;
10115             }
10116             if(y + h > vh+s.top){
10117                 y = vh - h;
10118                 moved = true;
10119             }
10120             // then make sure top/left isn't negative
10121             if(x < s.left){
10122                 x = s.left;
10123                 moved = true;
10124             }
10125             if(y < s.top){
10126                 y = s.top;
10127                 moved = true;
10128             }
10129             if(moved){
10130                 // cache xy
10131                 this.xy = [x, y];
10132                 if(this.isVisible()){
10133                     this.el.setLocation(x, y);
10134                     this.adjustAssets();
10135                 }
10136             }
10137         }
10138     },
10139
10140     // private
10141     onDrag : function(){
10142         if(!this.proxyDrag){
10143             this.xy = this.el.getXY();
10144             this.adjustAssets();
10145         }
10146     },
10147
10148     // private
10149     adjustAssets : function(doShow){
10150         var x = this.xy[0], y = this.xy[1];
10151         var w = this.size.width, h = this.size.height;
10152         if(doShow === true){
10153             if(this.shadow){
10154                 this.shadow.show(this.el);
10155             }
10156             if(this.shim){
10157                 this.shim.show();
10158             }
10159         }
10160         if(this.shadow && this.shadow.isVisible()){
10161             this.shadow.show(this.el);
10162         }
10163         if(this.shim && this.shim.isVisible()){
10164             this.shim.setBounds(x, y, w, h);
10165         }
10166     },
10167
10168     // private
10169     adjustViewport : function(w, h){
10170         if(!w || !h){
10171             w = Roo.lib.Dom.getViewWidth();
10172             h = Roo.lib.Dom.getViewHeight();
10173         }
10174         // cache the size
10175         this.viewSize = [w, h];
10176         if(this.modal && this.mask.isVisible()){
10177             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10178             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10179         }
10180         if(this.isVisible()){
10181             this.constrainXY();
10182         }
10183     },
10184
10185     /**
10186      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10187      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10188      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10189      */
10190     destroy : function(removeEl){
10191         if(this.isVisible()){
10192             this.animateTarget = null;
10193             this.hide();
10194         }
10195         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10196         if(this.tabs){
10197             this.tabs.destroy(removeEl);
10198         }
10199         Roo.destroy(
10200              this.shim,
10201              this.proxy,
10202              this.resizer,
10203              this.close,
10204              this.mask
10205         );
10206         if(this.dd){
10207             this.dd.unreg();
10208         }
10209         if(this.buttons){
10210            for(var i = 0, len = this.buttons.length; i < len; i++){
10211                this.buttons[i].destroy();
10212            }
10213         }
10214         this.el.removeAllListeners();
10215         if(removeEl === true){
10216             this.el.update("");
10217             this.el.remove();
10218         }
10219         Roo.DialogManager.unregister(this);
10220     },
10221
10222     // private
10223     startMove : function(){
10224         if(this.proxyDrag){
10225             this.proxy.show();
10226         }
10227         if(this.constraintoviewport !== false){
10228             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10229         }
10230     },
10231
10232     // private
10233     endMove : function(){
10234         if(!this.proxyDrag){
10235             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10236         }else{
10237             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10238             this.proxy.hide();
10239         }
10240         this.refreshSize();
10241         this.adjustAssets();
10242         this.focus();
10243         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10244     },
10245
10246     /**
10247      * Brings this dialog to the front of any other visible dialogs
10248      * @return {Roo.BasicDialog} this
10249      */
10250     toFront : function(){
10251         Roo.DialogManager.bringToFront(this);
10252         return this;
10253     },
10254
10255     /**
10256      * Sends this dialog to the back (under) of any other visible dialogs
10257      * @return {Roo.BasicDialog} this
10258      */
10259     toBack : function(){
10260         Roo.DialogManager.sendToBack(this);
10261         return this;
10262     },
10263
10264     /**
10265      * Centers this dialog in the viewport
10266      * @return {Roo.BasicDialog} this
10267      */
10268     center : function(){
10269         var xy = this.el.getCenterXY(true);
10270         this.moveTo(xy[0], xy[1]);
10271         return this;
10272     },
10273
10274     /**
10275      * Moves the dialog's top-left corner to the specified point
10276      * @param {Number} x
10277      * @param {Number} y
10278      * @return {Roo.BasicDialog} this
10279      */
10280     moveTo : function(x, y){
10281         this.xy = [x,y];
10282         if(this.isVisible()){
10283             this.el.setXY(this.xy);
10284             this.adjustAssets();
10285         }
10286         return this;
10287     },
10288
10289     /**
10290      * Aligns the dialog to the specified element
10291      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10292      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10293      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10294      * @return {Roo.BasicDialog} this
10295      */
10296     alignTo : function(element, position, offsets){
10297         this.xy = this.el.getAlignToXY(element, position, offsets);
10298         if(this.isVisible()){
10299             this.el.setXY(this.xy);
10300             this.adjustAssets();
10301         }
10302         return this;
10303     },
10304
10305     /**
10306      * Anchors an element to another element and realigns it when the window is resized.
10307      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10308      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10309      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10310      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10311      * is a number, it is used as the buffer delay (defaults to 50ms).
10312      * @return {Roo.BasicDialog} this
10313      */
10314     anchorTo : function(el, alignment, offsets, monitorScroll){
10315         var action = function(){
10316             this.alignTo(el, alignment, offsets);
10317         };
10318         Roo.EventManager.onWindowResize(action, this);
10319         var tm = typeof monitorScroll;
10320         if(tm != 'undefined'){
10321             Roo.EventManager.on(window, 'scroll', action, this,
10322                 {buffer: tm == 'number' ? monitorScroll : 50});
10323         }
10324         action.call(this);
10325         return this;
10326     },
10327
10328     /**
10329      * Returns true if the dialog is visible
10330      * @return {Boolean}
10331      */
10332     isVisible : function(){
10333         return this.el.isVisible();
10334     },
10335
10336     // private
10337     animHide : function(callback){
10338         var b = Roo.get(this.animateTarget).getBox();
10339         this.proxy.show();
10340         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10341         this.el.hide();
10342         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10343                     this.hideEl.createDelegate(this, [callback]));
10344     },
10345
10346     /**
10347      * Hides the dialog.
10348      * @param {Function} callback (optional) Function to call when the dialog is hidden
10349      * @return {Roo.BasicDialog} this
10350      */
10351     hide : function(callback){
10352         if (this.fireEvent("beforehide", this) === false){
10353             return;
10354         }
10355         if(this.shadow){
10356             this.shadow.hide();
10357         }
10358         if(this.shim) {
10359           this.shim.hide();
10360         }
10361         // sometimes animateTarget seems to get set.. causing problems...
10362         // this just double checks..
10363         if(this.animateTarget && Roo.get(this.animateTarget)) {
10364            this.animHide(callback);
10365         }else{
10366             this.el.hide();
10367             this.hideEl(callback);
10368         }
10369         return this;
10370     },
10371
10372     // private
10373     hideEl : function(callback){
10374         this.proxy.hide();
10375         if(this.modal){
10376             this.mask.hide();
10377             Roo.get(document.body).removeClass("x-body-masked");
10378         }
10379         this.fireEvent("hide", this);
10380         if(typeof callback == "function"){
10381             callback();
10382         }
10383     },
10384
10385     // private
10386     hideAction : function(){
10387         this.setLeft("-10000px");
10388         this.setTop("-10000px");
10389         this.setStyle("visibility", "hidden");
10390     },
10391
10392     // private
10393     refreshSize : function(){
10394         this.size = this.el.getSize();
10395         this.xy = this.el.getXY();
10396         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10397     },
10398
10399     // private
10400     // z-index is managed by the DialogManager and may be overwritten at any time
10401     setZIndex : function(index){
10402         if(this.modal){
10403             this.mask.setStyle("z-index", index);
10404         }
10405         if(this.shim){
10406             this.shim.setStyle("z-index", ++index);
10407         }
10408         if(this.shadow){
10409             this.shadow.setZIndex(++index);
10410         }
10411         this.el.setStyle("z-index", ++index);
10412         if(this.proxy){
10413             this.proxy.setStyle("z-index", ++index);
10414         }
10415         if(this.resizer){
10416             this.resizer.proxy.setStyle("z-index", ++index);
10417         }
10418
10419         this.lastZIndex = index;
10420     },
10421
10422     /**
10423      * Returns the element for this dialog
10424      * @return {Roo.Element} The underlying dialog Element
10425      */
10426     getEl : function(){
10427         return this.el;
10428     }
10429 });
10430
10431 /**
10432  * @class Roo.DialogManager
10433  * Provides global access to BasicDialogs that have been created and
10434  * support for z-indexing (layering) multiple open dialogs.
10435  */
10436 Roo.DialogManager = function(){
10437     var list = {};
10438     var accessList = [];
10439     var front = null;
10440
10441     // private
10442     var sortDialogs = function(d1, d2){
10443         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10444     };
10445
10446     // private
10447     var orderDialogs = function(){
10448         accessList.sort(sortDialogs);
10449         var seed = Roo.DialogManager.zseed;
10450         for(var i = 0, len = accessList.length; i < len; i++){
10451             var dlg = accessList[i];
10452             if(dlg){
10453                 dlg.setZIndex(seed + (i*10));
10454             }
10455         }
10456     };
10457
10458     return {
10459         /**
10460          * The starting z-index for BasicDialogs (defaults to 9000)
10461          * @type Number The z-index value
10462          */
10463         zseed : 9000,
10464
10465         // private
10466         register : function(dlg){
10467             list[dlg.id] = dlg;
10468             accessList.push(dlg);
10469         },
10470
10471         // private
10472         unregister : function(dlg){
10473             delete list[dlg.id];
10474             var i=0;
10475             var len=0;
10476             if(!accessList.indexOf){
10477                 for(  i = 0, len = accessList.length; i < len; i++){
10478                     if(accessList[i] == dlg){
10479                         accessList.splice(i, 1);
10480                         return;
10481                     }
10482                 }
10483             }else{
10484                  i = accessList.indexOf(dlg);
10485                 if(i != -1){
10486                     accessList.splice(i, 1);
10487                 }
10488             }
10489         },
10490
10491         /**
10492          * Gets a registered dialog by id
10493          * @param {String/Object} id The id of the dialog or a dialog
10494          * @return {Roo.BasicDialog} this
10495          */
10496         get : function(id){
10497             return typeof id == "object" ? id : list[id];
10498         },
10499
10500         /**
10501          * Brings the specified dialog to the front
10502          * @param {String/Object} dlg The id of the dialog or a dialog
10503          * @return {Roo.BasicDialog} this
10504          */
10505         bringToFront : function(dlg){
10506             dlg = this.get(dlg);
10507             if(dlg != front){
10508                 front = dlg;
10509                 dlg._lastAccess = new Date().getTime();
10510                 orderDialogs();
10511             }
10512             return dlg;
10513         },
10514
10515         /**
10516          * Sends the specified dialog to the back
10517          * @param {String/Object} dlg The id of the dialog or a dialog
10518          * @return {Roo.BasicDialog} this
10519          */
10520         sendToBack : function(dlg){
10521             dlg = this.get(dlg);
10522             dlg._lastAccess = -(new Date().getTime());
10523             orderDialogs();
10524             return dlg;
10525         },
10526
10527         /**
10528          * Hides all dialogs
10529          */
10530         hideAll : function(){
10531             for(var id in list){
10532                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10533                     list[id].hide();
10534                 }
10535             }
10536         }
10537     };
10538 }();
10539
10540 /**
10541  * @class Roo.LayoutDialog
10542  * @extends Roo.BasicDialog
10543  * Dialog which provides adjustments for working with a layout in a Dialog.
10544  * Add your necessary layout config options to the dialog's config.<br>
10545  * Example usage (including a nested layout):
10546  * <pre><code>
10547 if(!dialog){
10548     dialog = new Roo.LayoutDialog("download-dlg", {
10549         modal: true,
10550         width:600,
10551         height:450,
10552         shadow:true,
10553         minWidth:500,
10554         minHeight:350,
10555         autoTabs:true,
10556         proxyDrag:true,
10557         // layout config merges with the dialog config
10558         center:{
10559             tabPosition: "top",
10560             alwaysShowTabs: true
10561         }
10562     });
10563     dialog.addKeyListener(27, dialog.hide, dialog);
10564     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10565     dialog.addButton("Build It!", this.getDownload, this);
10566
10567     // we can even add nested layouts
10568     var innerLayout = new Roo.BorderLayout("dl-inner", {
10569         east: {
10570             initialSize: 200,
10571             autoScroll:true,
10572             split:true
10573         },
10574         center: {
10575             autoScroll:true
10576         }
10577     });
10578     innerLayout.beginUpdate();
10579     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10580     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10581     innerLayout.endUpdate(true);
10582
10583     var layout = dialog.getLayout();
10584     layout.beginUpdate();
10585     layout.add("center", new Roo.ContentPanel("standard-panel",
10586                         {title: "Download the Source", fitToFrame:true}));
10587     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10588                {title: "Build your own roo.js"}));
10589     layout.getRegion("center").showPanel(sp);
10590     layout.endUpdate();
10591 }
10592 </code></pre>
10593     * @constructor
10594     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10595     * @param {Object} config configuration options
10596   */
10597 Roo.LayoutDialog = function(el, cfg){
10598     
10599     var config=  cfg;
10600     if (typeof(cfg) == 'undefined') {
10601         config = Roo.apply({}, el);
10602         // not sure why we use documentElement here.. - it should always be body.
10603         // IE7 borks horribly if we use documentElement.
10604         // webkit also does not like documentElement - it creates a body element...
10605         el = Roo.get( document.body || document.documentElement ).createChild();
10606         //config.autoCreate = true;
10607     }
10608     
10609     
10610     config.autoTabs = false;
10611     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10612     this.body.setStyle({overflow:"hidden", position:"relative"});
10613     this.layout = new Roo.BorderLayout(this.body.dom, config);
10614     this.layout.monitorWindowResize = false;
10615     this.el.addClass("x-dlg-auto-layout");
10616     // fix case when center region overwrites center function
10617     this.center = Roo.BasicDialog.prototype.center;
10618     this.on("show", this.layout.layout, this.layout, true);
10619     if (config.items) {
10620         var xitems = config.items;
10621         delete config.items;
10622         Roo.each(xitems, this.addxtype, this);
10623     }
10624     
10625     
10626 };
10627 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10628     /**
10629      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10630      * @deprecated
10631      */
10632     endUpdate : function(){
10633         this.layout.endUpdate();
10634     },
10635
10636     /**
10637      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10638      *  @deprecated
10639      */
10640     beginUpdate : function(){
10641         this.layout.beginUpdate();
10642     },
10643
10644     /**
10645      * Get the BorderLayout for this dialog
10646      * @return {Roo.BorderLayout}
10647      */
10648     getLayout : function(){
10649         return this.layout;
10650     },
10651
10652     showEl : function(){
10653         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10654         if(Roo.isIE7){
10655             this.layout.layout();
10656         }
10657     },
10658
10659     // private
10660     // Use the syncHeightBeforeShow config option to control this automatically
10661     syncBodyHeight : function(){
10662         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10663         if(this.layout){this.layout.layout();}
10664     },
10665     
10666       /**
10667      * Add an xtype element (actually adds to the layout.)
10668      * @return {Object} xdata xtype object data.
10669      */
10670     
10671     addxtype : function(c) {
10672         return this.layout.addxtype(c);
10673     }
10674 });/*
10675  * Based on:
10676  * Ext JS Library 1.1.1
10677  * Copyright(c) 2006-2007, Ext JS, LLC.
10678  *
10679  * Originally Released Under LGPL - original licence link has changed is not relivant.
10680  *
10681  * Fork - LGPL
10682  * <script type="text/javascript">
10683  */
10684  
10685 /**
10686  * @class Roo.MessageBox
10687  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10688  * Example usage:
10689  *<pre><code>
10690 // Basic alert:
10691 Roo.Msg.alert('Status', 'Changes saved successfully.');
10692
10693 // Prompt for user data:
10694 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10695     if (btn == 'ok'){
10696         // process text value...
10697     }
10698 });
10699
10700 // Show a dialog using config options:
10701 Roo.Msg.show({
10702    title:'Save Changes?',
10703    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10704    buttons: Roo.Msg.YESNOCANCEL,
10705    fn: processResult,
10706    animEl: 'elId'
10707 });
10708 </code></pre>
10709  * @singleton
10710  */
10711 Roo.MessageBox = function(){
10712     var dlg, opt, mask, waitTimer;
10713     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10714     var buttons, activeTextEl, bwidth;
10715
10716     // private
10717     var handleButton = function(button){
10718         dlg.hide();
10719         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10720     };
10721
10722     // private
10723     var handleHide = function(){
10724         if(opt && opt.cls){
10725             dlg.el.removeClass(opt.cls);
10726         }
10727         if(waitTimer){
10728             Roo.TaskMgr.stop(waitTimer);
10729             waitTimer = null;
10730         }
10731     };
10732
10733     // private
10734     var updateButtons = function(b){
10735         var width = 0;
10736         if(!b){
10737             buttons["ok"].hide();
10738             buttons["cancel"].hide();
10739             buttons["yes"].hide();
10740             buttons["no"].hide();
10741             dlg.footer.dom.style.display = 'none';
10742             return width;
10743         }
10744         dlg.footer.dom.style.display = '';
10745         for(var k in buttons){
10746             if(typeof buttons[k] != "function"){
10747                 if(b[k]){
10748                     buttons[k].show();
10749                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10750                     width += buttons[k].el.getWidth()+15;
10751                 }else{
10752                     buttons[k].hide();
10753                 }
10754             }
10755         }
10756         return width;
10757     };
10758
10759     // private
10760     var handleEsc = function(d, k, e){
10761         if(opt && opt.closable !== false){
10762             dlg.hide();
10763         }
10764         if(e){
10765             e.stopEvent();
10766         }
10767     };
10768
10769     return {
10770         /**
10771          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10772          * @return {Roo.BasicDialog} The BasicDialog element
10773          */
10774         getDialog : function(){
10775            if(!dlg){
10776                 dlg = new Roo.BasicDialog("x-msg-box", {
10777                     autoCreate : true,
10778                     shadow: true,
10779                     draggable: true,
10780                     resizable:false,
10781                     constraintoviewport:false,
10782                     fixedcenter:true,
10783                     collapsible : false,
10784                     shim:true,
10785                     modal: true,
10786                     width:400, height:100,
10787                     buttonAlign:"center",
10788                     closeClick : function(){
10789                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10790                             handleButton("no");
10791                         }else{
10792                             handleButton("cancel");
10793                         }
10794                     }
10795                 });
10796                 dlg.on("hide", handleHide);
10797                 mask = dlg.mask;
10798                 dlg.addKeyListener(27, handleEsc);
10799                 buttons = {};
10800                 var bt = this.buttonText;
10801                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10802                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10803                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10804                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10805                 bodyEl = dlg.body.createChild({
10806
10807                     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">&#160;</div></div></div>'
10808                 });
10809                 msgEl = bodyEl.dom.firstChild;
10810                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10811                 textboxEl.enableDisplayMode();
10812                 textboxEl.addKeyListener([10,13], function(){
10813                     if(dlg.isVisible() && opt && opt.buttons){
10814                         if(opt.buttons.ok){
10815                             handleButton("ok");
10816                         }else if(opt.buttons.yes){
10817                             handleButton("yes");
10818                         }
10819                     }
10820                 });
10821                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10822                 textareaEl.enableDisplayMode();
10823                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10824                 progressEl.enableDisplayMode();
10825                 var pf = progressEl.dom.firstChild;
10826                 if (pf) {
10827                     pp = Roo.get(pf.firstChild);
10828                     pp.setHeight(pf.offsetHeight);
10829                 }
10830                 
10831             }
10832             return dlg;
10833         },
10834
10835         /**
10836          * Updates the message box body text
10837          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10838          * the XHTML-compliant non-breaking space character '&amp;#160;')
10839          * @return {Roo.MessageBox} This message box
10840          */
10841         updateText : function(text){
10842             if(!dlg.isVisible() && !opt.width){
10843                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10844             }
10845             msgEl.innerHTML = text || '&#160;';
10846       
10847             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10848             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10849             var w = Math.max(
10850                     Math.min(opt.width || cw , this.maxWidth), 
10851                     Math.max(opt.minWidth || this.minWidth, bwidth)
10852             );
10853             if(opt.prompt){
10854                 activeTextEl.setWidth(w);
10855             }
10856             if(dlg.isVisible()){
10857                 dlg.fixedcenter = false;
10858             }
10859             // to big, make it scroll. = But as usual stupid IE does not support
10860             // !important..
10861             
10862             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10863                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10864                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10865             } else {
10866                 bodyEl.dom.style.height = '';
10867                 bodyEl.dom.style.overflowY = '';
10868             }
10869             if (cw > w) {
10870                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10871             } else {
10872                 bodyEl.dom.style.overflowX = '';
10873             }
10874             
10875             dlg.setContentSize(w, bodyEl.getHeight());
10876             if(dlg.isVisible()){
10877                 dlg.fixedcenter = true;
10878             }
10879             return this;
10880         },
10881
10882         /**
10883          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10884          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10885          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10886          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10887          * @return {Roo.MessageBox} This message box
10888          */
10889         updateProgress : function(value, text){
10890             if(text){
10891                 this.updateText(text);
10892             }
10893             if (pp) { // weird bug on my firefox - for some reason this is not defined
10894                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10895             }
10896             return this;
10897         },        
10898
10899         /**
10900          * Returns true if the message box is currently displayed
10901          * @return {Boolean} True if the message box is visible, else false
10902          */
10903         isVisible : function(){
10904             return dlg && dlg.isVisible();  
10905         },
10906
10907         /**
10908          * Hides the message box if it is displayed
10909          */
10910         hide : function(){
10911             if(this.isVisible()){
10912                 dlg.hide();
10913             }  
10914         },
10915
10916         /**
10917          * Displays a new message box, or reinitializes an existing message box, based on the config options
10918          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10919          * The following config object properties are supported:
10920          * <pre>
10921 Property    Type             Description
10922 ----------  ---------------  ------------------------------------------------------------------------------------
10923 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10924                                    closes (defaults to undefined)
10925 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10926                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10927 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10928                                    progress and wait dialogs will ignore this property and always hide the
10929                                    close button as they can only be closed programmatically.
10930 cls               String           A custom CSS class to apply to the message box element
10931 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10932                                    displayed (defaults to 75)
10933 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10934                                    function will be btn (the name of the button that was clicked, if applicable,
10935                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10936                                    Progress and wait dialogs will ignore this option since they do not respond to
10937                                    user actions and can only be closed programmatically, so any required function
10938                                    should be called by the same code after it closes the dialog.
10939 icon              String           A CSS class that provides a background image to be used as an icon for
10940                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10941 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10942 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10943 modal             Boolean          False to allow user interaction with the page while the message box is
10944                                    displayed (defaults to true)
10945 msg               String           A string that will replace the existing message box body text (defaults
10946                                    to the XHTML-compliant non-breaking space character '&#160;')
10947 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10948 progress          Boolean          True to display a progress bar (defaults to false)
10949 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10950 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10951 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10952 title             String           The title text
10953 value             String           The string value to set into the active textbox element if displayed
10954 wait              Boolean          True to display a progress bar (defaults to false)
10955 width             Number           The width of the dialog in pixels
10956 </pre>
10957          *
10958          * Example usage:
10959          * <pre><code>
10960 Roo.Msg.show({
10961    title: 'Address',
10962    msg: 'Please enter your address:',
10963    width: 300,
10964    buttons: Roo.MessageBox.OKCANCEL,
10965    multiline: true,
10966    fn: saveAddress,
10967    animEl: 'addAddressBtn'
10968 });
10969 </code></pre>
10970          * @param {Object} config Configuration options
10971          * @return {Roo.MessageBox} This message box
10972          */
10973         show : function(options)
10974         {
10975             
10976             // this causes nightmares if you show one dialog after another
10977             // especially on callbacks..
10978              
10979             if(this.isVisible()){
10980                 
10981                 this.hide();
10982                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10983                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10984                 Roo.log("New Dialog Message:" +  options.msg )
10985                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10986                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10987                 
10988             }
10989             var d = this.getDialog();
10990             opt = options;
10991             d.setTitle(opt.title || "&#160;");
10992             d.close.setDisplayed(opt.closable !== false);
10993             activeTextEl = textboxEl;
10994             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10995             if(opt.prompt){
10996                 if(opt.multiline){
10997                     textboxEl.hide();
10998                     textareaEl.show();
10999                     textareaEl.setHeight(typeof opt.multiline == "number" ?
11000                         opt.multiline : this.defaultTextHeight);
11001                     activeTextEl = textareaEl;
11002                 }else{
11003                     textboxEl.show();
11004                     textareaEl.hide();
11005                 }
11006             }else{
11007                 textboxEl.hide();
11008                 textareaEl.hide();
11009             }
11010             progressEl.setDisplayed(opt.progress === true);
11011             this.updateProgress(0);
11012             activeTextEl.dom.value = opt.value || "";
11013             if(opt.prompt){
11014                 dlg.setDefaultButton(activeTextEl);
11015             }else{
11016                 var bs = opt.buttons;
11017                 var db = null;
11018                 if(bs && bs.ok){
11019                     db = buttons["ok"];
11020                 }else if(bs && bs.yes){
11021                     db = buttons["yes"];
11022                 }
11023                 dlg.setDefaultButton(db);
11024             }
11025             bwidth = updateButtons(opt.buttons);
11026             this.updateText(opt.msg);
11027             if(opt.cls){
11028                 d.el.addClass(opt.cls);
11029             }
11030             d.proxyDrag = opt.proxyDrag === true;
11031             d.modal = opt.modal !== false;
11032             d.mask = opt.modal !== false ? mask : false;
11033             if(!d.isVisible()){
11034                 // force it to the end of the z-index stack so it gets a cursor in FF
11035                 document.body.appendChild(dlg.el.dom);
11036                 d.animateTarget = null;
11037                 d.show(options.animEl);
11038             }
11039             return this;
11040         },
11041
11042         /**
11043          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11044          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11045          * and closing the message box when the process is complete.
11046          * @param {String} title The title bar text
11047          * @param {String} msg The message box body text
11048          * @return {Roo.MessageBox} This message box
11049          */
11050         progress : function(title, msg){
11051             this.show({
11052                 title : title,
11053                 msg : msg,
11054                 buttons: false,
11055                 progress:true,
11056                 closable:false,
11057                 minWidth: this.minProgressWidth,
11058                 modal : true
11059             });
11060             return this;
11061         },
11062
11063         /**
11064          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11065          * If a callback function is passed it will be called after the user clicks the button, and the
11066          * id of the button that was clicked will be passed as the only parameter to the callback
11067          * (could also be the top-right close button).
11068          * @param {String} title The title bar text
11069          * @param {String} msg The message box body text
11070          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11071          * @param {Object} scope (optional) The scope of the callback function
11072          * @return {Roo.MessageBox} This message box
11073          */
11074         alert : function(title, msg, fn, scope){
11075             this.show({
11076                 title : title,
11077                 msg : msg,
11078                 buttons: this.OK,
11079                 fn: fn,
11080                 scope : scope,
11081                 modal : true
11082             });
11083             return this;
11084         },
11085
11086         /**
11087          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11088          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11089          * You are responsible for closing the message box when the process is complete.
11090          * @param {String} msg The message box body text
11091          * @param {String} title (optional) The title bar text
11092          * @return {Roo.MessageBox} This message box
11093          */
11094         wait : function(msg, title){
11095             this.show({
11096                 title : title,
11097                 msg : msg,
11098                 buttons: false,
11099                 closable:false,
11100                 progress:true,
11101                 modal:true,
11102                 width:300,
11103                 wait:true
11104             });
11105             waitTimer = Roo.TaskMgr.start({
11106                 run: function(i){
11107                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11108                 },
11109                 interval: 1000
11110             });
11111             return this;
11112         },
11113
11114         /**
11115          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11116          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11117          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11118          * @param {String} title The title bar text
11119          * @param {String} msg The message box body text
11120          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11121          * @param {Object} scope (optional) The scope of the callback function
11122          * @return {Roo.MessageBox} This message box
11123          */
11124         confirm : function(title, msg, fn, scope){
11125             this.show({
11126                 title : title,
11127                 msg : msg,
11128                 buttons: this.YESNO,
11129                 fn: fn,
11130                 scope : scope,
11131                 modal : true
11132             });
11133             return this;
11134         },
11135
11136         /**
11137          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11138          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11139          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11140          * (could also be the top-right close button) and the text that was entered will be passed as the two
11141          * parameters to the callback.
11142          * @param {String} title The title bar text
11143          * @param {String} msg The message box body text
11144          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11145          * @param {Object} scope (optional) The scope of the callback function
11146          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11147          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11148          * @return {Roo.MessageBox} This message box
11149          */
11150         prompt : function(title, msg, fn, scope, multiline){
11151             this.show({
11152                 title : title,
11153                 msg : msg,
11154                 buttons: this.OKCANCEL,
11155                 fn: fn,
11156                 minWidth:250,
11157                 scope : scope,
11158                 prompt:true,
11159                 multiline: multiline,
11160                 modal : true
11161             });
11162             return this;
11163         },
11164
11165         /**
11166          * Button config that displays a single OK button
11167          * @type Object
11168          */
11169         OK : {ok:true},
11170         /**
11171          * Button config that displays Yes and No buttons
11172          * @type Object
11173          */
11174         YESNO : {yes:true, no:true},
11175         /**
11176          * Button config that displays OK and Cancel buttons
11177          * @type Object
11178          */
11179         OKCANCEL : {ok:true, cancel:true},
11180         /**
11181          * Button config that displays Yes, No and Cancel buttons
11182          * @type Object
11183          */
11184         YESNOCANCEL : {yes:true, no:true, cancel:true},
11185
11186         /**
11187          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11188          * @type Number
11189          */
11190         defaultTextHeight : 75,
11191         /**
11192          * The maximum width in pixels of the message box (defaults to 600)
11193          * @type Number
11194          */
11195         maxWidth : 600,
11196         /**
11197          * The minimum width in pixels of the message box (defaults to 100)
11198          * @type Number
11199          */
11200         minWidth : 100,
11201         /**
11202          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11203          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11204          * @type Number
11205          */
11206         minProgressWidth : 250,
11207         /**
11208          * An object containing the default button text strings that can be overriden for localized language support.
11209          * Supported properties are: ok, cancel, yes and no.
11210          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11211          * @type Object
11212          */
11213         buttonText : {
11214             ok : "OK",
11215             cancel : "Cancel",
11216             yes : "Yes",
11217             no : "No"
11218         }
11219     };
11220 }();
11221
11222 /**
11223  * Shorthand for {@link Roo.MessageBox}
11224  */
11225 Roo.Msg = Roo.MessageBox;/*
11226  * Based on:
11227  * Ext JS Library 1.1.1
11228  * Copyright(c) 2006-2007, Ext JS, LLC.
11229  *
11230  * Originally Released Under LGPL - original licence link has changed is not relivant.
11231  *
11232  * Fork - LGPL
11233  * <script type="text/javascript">
11234  */
11235 /**
11236  * @class Roo.QuickTips
11237  * Provides attractive and customizable tooltips for any element.
11238  * @singleton
11239  */
11240 Roo.QuickTips = function(){
11241     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11242     var ce, bd, xy, dd;
11243     var visible = false, disabled = true, inited = false;
11244     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11245     
11246     var onOver = function(e){
11247         if(disabled){
11248             return;
11249         }
11250         var t = e.getTarget();
11251         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11252             return;
11253         }
11254         if(ce && t == ce.el){
11255             clearTimeout(hideProc);
11256             return;
11257         }
11258         if(t && tagEls[t.id]){
11259             tagEls[t.id].el = t;
11260             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11261             return;
11262         }
11263         var ttp, et = Roo.fly(t);
11264         var ns = cfg.namespace;
11265         if(tm.interceptTitles && t.title){
11266             ttp = t.title;
11267             t.qtip = ttp;
11268             t.removeAttribute("title");
11269             e.preventDefault();
11270         }else{
11271             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11272         }
11273         if(ttp){
11274             showProc = show.defer(tm.showDelay, tm, [{
11275                 el: t, 
11276                 text: ttp.replace(/\\n/g,'<br/>'),
11277                 width: et.getAttributeNS(ns, cfg.width),
11278                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11279                 title: et.getAttributeNS(ns, cfg.title),
11280                     cls: et.getAttributeNS(ns, cfg.cls)
11281             }]);
11282         }
11283     };
11284     
11285     var onOut = function(e){
11286         clearTimeout(showProc);
11287         var t = e.getTarget();
11288         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11289             hideProc = setTimeout(hide, tm.hideDelay);
11290         }
11291     };
11292     
11293     var onMove = function(e){
11294         if(disabled){
11295             return;
11296         }
11297         xy = e.getXY();
11298         xy[1] += 18;
11299         if(tm.trackMouse && ce){
11300             el.setXY(xy);
11301         }
11302     };
11303     
11304     var onDown = function(e){
11305         clearTimeout(showProc);
11306         clearTimeout(hideProc);
11307         if(!e.within(el)){
11308             if(tm.hideOnClick){
11309                 hide();
11310                 tm.disable();
11311                 tm.enable.defer(100, tm);
11312             }
11313         }
11314     };
11315     
11316     var getPad = function(){
11317         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11318     };
11319
11320     var show = function(o){
11321         if(disabled){
11322             return;
11323         }
11324         clearTimeout(dismissProc);
11325         ce = o;
11326         if(removeCls){ // in case manually hidden
11327             el.removeClass(removeCls);
11328             removeCls = null;
11329         }
11330         if(ce.cls){
11331             el.addClass(ce.cls);
11332             removeCls = ce.cls;
11333         }
11334         if(ce.title){
11335             tipTitle.update(ce.title);
11336             tipTitle.show();
11337         }else{
11338             tipTitle.update('');
11339             tipTitle.hide();
11340         }
11341         el.dom.style.width  = tm.maxWidth+'px';
11342         //tipBody.dom.style.width = '';
11343         tipBodyText.update(o.text);
11344         var p = getPad(), w = ce.width;
11345         if(!w){
11346             var td = tipBodyText.dom;
11347             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11348             if(aw > tm.maxWidth){
11349                 w = tm.maxWidth;
11350             }else if(aw < tm.minWidth){
11351                 w = tm.minWidth;
11352             }else{
11353                 w = aw;
11354             }
11355         }
11356         //tipBody.setWidth(w);
11357         el.setWidth(parseInt(w, 10) + p);
11358         if(ce.autoHide === false){
11359             close.setDisplayed(true);
11360             if(dd){
11361                 dd.unlock();
11362             }
11363         }else{
11364             close.setDisplayed(false);
11365             if(dd){
11366                 dd.lock();
11367             }
11368         }
11369         if(xy){
11370             el.avoidY = xy[1]-18;
11371             el.setXY(xy);
11372         }
11373         if(tm.animate){
11374             el.setOpacity(.1);
11375             el.setStyle("visibility", "visible");
11376             el.fadeIn({callback: afterShow});
11377         }else{
11378             afterShow();
11379         }
11380     };
11381     
11382     var afterShow = function(){
11383         if(ce){
11384             el.show();
11385             esc.enable();
11386             if(tm.autoDismiss && ce.autoHide !== false){
11387                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11388             }
11389         }
11390     };
11391     
11392     var hide = function(noanim){
11393         clearTimeout(dismissProc);
11394         clearTimeout(hideProc);
11395         ce = null;
11396         if(el.isVisible()){
11397             esc.disable();
11398             if(noanim !== true && tm.animate){
11399                 el.fadeOut({callback: afterHide});
11400             }else{
11401                 afterHide();
11402             } 
11403         }
11404     };
11405     
11406     var afterHide = function(){
11407         el.hide();
11408         if(removeCls){
11409             el.removeClass(removeCls);
11410             removeCls = null;
11411         }
11412     };
11413     
11414     return {
11415         /**
11416         * @cfg {Number} minWidth
11417         * The minimum width of the quick tip (defaults to 40)
11418         */
11419        minWidth : 40,
11420         /**
11421         * @cfg {Number} maxWidth
11422         * The maximum width of the quick tip (defaults to 300)
11423         */
11424        maxWidth : 300,
11425         /**
11426         * @cfg {Boolean} interceptTitles
11427         * True to automatically use the element's DOM title value if available (defaults to false)
11428         */
11429        interceptTitles : false,
11430         /**
11431         * @cfg {Boolean} trackMouse
11432         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11433         */
11434        trackMouse : false,
11435         /**
11436         * @cfg {Boolean} hideOnClick
11437         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11438         */
11439        hideOnClick : true,
11440         /**
11441         * @cfg {Number} showDelay
11442         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11443         */
11444        showDelay : 500,
11445         /**
11446         * @cfg {Number} hideDelay
11447         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11448         */
11449        hideDelay : 200,
11450         /**
11451         * @cfg {Boolean} autoHide
11452         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11453         * Used in conjunction with hideDelay.
11454         */
11455        autoHide : true,
11456         /**
11457         * @cfg {Boolean}
11458         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11459         * (defaults to true).  Used in conjunction with autoDismissDelay.
11460         */
11461        autoDismiss : true,
11462         /**
11463         * @cfg {Number}
11464         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11465         */
11466        autoDismissDelay : 5000,
11467        /**
11468         * @cfg {Boolean} animate
11469         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11470         */
11471        animate : false,
11472
11473        /**
11474         * @cfg {String} title
11475         * Title text to display (defaults to '').  This can be any valid HTML markup.
11476         */
11477         title: '',
11478        /**
11479         * @cfg {String} text
11480         * Body text to display (defaults to '').  This can be any valid HTML markup.
11481         */
11482         text : '',
11483        /**
11484         * @cfg {String} cls
11485         * A CSS class to apply to the base quick tip element (defaults to '').
11486         */
11487         cls : '',
11488        /**
11489         * @cfg {Number} width
11490         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11491         * minWidth or maxWidth.
11492         */
11493         width : null,
11494
11495     /**
11496      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11497      * or display QuickTips in a page.
11498      */
11499        init : function(){
11500           tm = Roo.QuickTips;
11501           cfg = tm.tagConfig;
11502           if(!inited){
11503               if(!Roo.isReady){ // allow calling of init() before onReady
11504                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11505                   return;
11506               }
11507               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11508               el.fxDefaults = {stopFx: true};
11509               // maximum custom styling
11510               //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>');
11511               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>');              
11512               tipTitle = el.child('h3');
11513               tipTitle.enableDisplayMode("block");
11514               tipBody = el.child('div.x-tip-bd');
11515               tipBodyText = el.child('div.x-tip-bd-inner');
11516               //bdLeft = el.child('div.x-tip-bd-left');
11517               //bdRight = el.child('div.x-tip-bd-right');
11518               close = el.child('div.x-tip-close');
11519               close.enableDisplayMode("block");
11520               close.on("click", hide);
11521               var d = Roo.get(document);
11522               d.on("mousedown", onDown);
11523               d.on("mouseover", onOver);
11524               d.on("mouseout", onOut);
11525               d.on("mousemove", onMove);
11526               esc = d.addKeyListener(27, hide);
11527               esc.disable();
11528               if(Roo.dd.DD){
11529                   dd = el.initDD("default", null, {
11530                       onDrag : function(){
11531                           el.sync();  
11532                       }
11533                   });
11534                   dd.setHandleElId(tipTitle.id);
11535                   dd.lock();
11536               }
11537               inited = true;
11538           }
11539           this.enable(); 
11540        },
11541
11542     /**
11543      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11544      * are supported:
11545      * <pre>
11546 Property    Type                   Description
11547 ----------  ---------------------  ------------------------------------------------------------------------
11548 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11549      * </ul>
11550      * @param {Object} config The config object
11551      */
11552        register : function(config){
11553            var cs = config instanceof Array ? config : arguments;
11554            for(var i = 0, len = cs.length; i < len; i++) {
11555                var c = cs[i];
11556                var target = c.target;
11557                if(target){
11558                    if(target instanceof Array){
11559                        for(var j = 0, jlen = target.length; j < jlen; j++){
11560                            tagEls[target[j]] = c;
11561                        }
11562                    }else{
11563                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11564                    }
11565                }
11566            }
11567        },
11568
11569     /**
11570      * Removes this quick tip from its element and destroys it.
11571      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11572      */
11573        unregister : function(el){
11574            delete tagEls[Roo.id(el)];
11575        },
11576
11577     /**
11578      * Enable this quick tip.
11579      */
11580        enable : function(){
11581            if(inited && disabled){
11582                locks.pop();
11583                if(locks.length < 1){
11584                    disabled = false;
11585                }
11586            }
11587        },
11588
11589     /**
11590      * Disable this quick tip.
11591      */
11592        disable : function(){
11593           disabled = true;
11594           clearTimeout(showProc);
11595           clearTimeout(hideProc);
11596           clearTimeout(dismissProc);
11597           if(ce){
11598               hide(true);
11599           }
11600           locks.push(1);
11601        },
11602
11603     /**
11604      * Returns true if the quick tip is enabled, else false.
11605      */
11606        isEnabled : function(){
11607             return !disabled;
11608        },
11609
11610         // private
11611        tagConfig : {
11612            namespace : "roo", // was ext?? this may break..
11613            alt_namespace : "ext",
11614            attribute : "qtip",
11615            width : "width",
11616            target : "target",
11617            title : "qtitle",
11618            hide : "hide",
11619            cls : "qclass"
11620        }
11621    };
11622 }();
11623
11624 // backwards compat
11625 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11626  * Based on:
11627  * Ext JS Library 1.1.1
11628  * Copyright(c) 2006-2007, Ext JS, LLC.
11629  *
11630  * Originally Released Under LGPL - original licence link has changed is not relivant.
11631  *
11632  * Fork - LGPL
11633  * <script type="text/javascript">
11634  */
11635  
11636
11637 /**
11638  * @class Roo.tree.TreePanel
11639  * @extends Roo.data.Tree
11640
11641  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11642  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11643  * @cfg {Boolean} enableDD true to enable drag and drop
11644  * @cfg {Boolean} enableDrag true to enable just drag
11645  * @cfg {Boolean} enableDrop true to enable just drop
11646  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11647  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11648  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11649  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11650  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11651  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11652  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11653  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11654  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11655  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11656  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11657  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11658  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11659  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11660  * @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>
11661  * @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>
11662  * 
11663  * @constructor
11664  * @param {String/HTMLElement/Element} el The container element
11665  * @param {Object} config
11666  */
11667 Roo.tree.TreePanel = function(el, config){
11668     var root = false;
11669     var loader = false;
11670     if (config.root) {
11671         root = config.root;
11672         delete config.root;
11673     }
11674     if (config.loader) {
11675         loader = config.loader;
11676         delete config.loader;
11677     }
11678     
11679     Roo.apply(this, config);
11680     Roo.tree.TreePanel.superclass.constructor.call(this);
11681     this.el = Roo.get(el);
11682     this.el.addClass('x-tree');
11683     //console.log(root);
11684     if (root) {
11685         this.setRootNode( Roo.factory(root, Roo.tree));
11686     }
11687     if (loader) {
11688         this.loader = Roo.factory(loader, Roo.tree);
11689     }
11690    /**
11691     * Read-only. The id of the container element becomes this TreePanel's id.
11692     */
11693     this.id = this.el.id;
11694     this.addEvents({
11695         /**
11696         * @event beforeload
11697         * Fires before a node is loaded, return false to cancel
11698         * @param {Node} node The node being loaded
11699         */
11700         "beforeload" : true,
11701         /**
11702         * @event load
11703         * Fires when a node is loaded
11704         * @param {Node} node The node that was loaded
11705         */
11706         "load" : true,
11707         /**
11708         * @event textchange
11709         * Fires when the text for a node is changed
11710         * @param {Node} node The node
11711         * @param {String} text The new text
11712         * @param {String} oldText The old text
11713         */
11714         "textchange" : true,
11715         /**
11716         * @event beforeexpand
11717         * Fires before a node is expanded, return false to cancel.
11718         * @param {Node} node The node
11719         * @param {Boolean} deep
11720         * @param {Boolean} anim
11721         */
11722         "beforeexpand" : true,
11723         /**
11724         * @event beforecollapse
11725         * Fires before a node is collapsed, return false to cancel.
11726         * @param {Node} node The node
11727         * @param {Boolean} deep
11728         * @param {Boolean} anim
11729         */
11730         "beforecollapse" : true,
11731         /**
11732         * @event expand
11733         * Fires when a node is expanded
11734         * @param {Node} node The node
11735         */
11736         "expand" : true,
11737         /**
11738         * @event disabledchange
11739         * Fires when the disabled status of a node changes
11740         * @param {Node} node The node
11741         * @param {Boolean} disabled
11742         */
11743         "disabledchange" : true,
11744         /**
11745         * @event collapse
11746         * Fires when a node is collapsed
11747         * @param {Node} node The node
11748         */
11749         "collapse" : true,
11750         /**
11751         * @event beforeclick
11752         * Fires before click processing on a node. Return false to cancel the default action.
11753         * @param {Node} node The node
11754         * @param {Roo.EventObject} e The event object
11755         */
11756         "beforeclick":true,
11757         /**
11758         * @event checkchange
11759         * Fires when a node with a checkbox's checked property changes
11760         * @param {Node} this This node
11761         * @param {Boolean} checked
11762         */
11763         "checkchange":true,
11764         /**
11765         * @event click
11766         * Fires when a node is clicked
11767         * @param {Node} node The node
11768         * @param {Roo.EventObject} e The event object
11769         */
11770         "click":true,
11771         /**
11772         * @event dblclick
11773         * Fires when a node is double clicked
11774         * @param {Node} node The node
11775         * @param {Roo.EventObject} e The event object
11776         */
11777         "dblclick":true,
11778         /**
11779         * @event contextmenu
11780         * Fires when a node is right clicked
11781         * @param {Node} node The node
11782         * @param {Roo.EventObject} e The event object
11783         */
11784         "contextmenu":true,
11785         /**
11786         * @event beforechildrenrendered
11787         * Fires right before the child nodes for a node are rendered
11788         * @param {Node} node The node
11789         */
11790         "beforechildrenrendered":true,
11791         /**
11792         * @event startdrag
11793         * Fires when a node starts being dragged
11794         * @param {Roo.tree.TreePanel} this
11795         * @param {Roo.tree.TreeNode} node
11796         * @param {event} e The raw browser event
11797         */ 
11798        "startdrag" : true,
11799        /**
11800         * @event enddrag
11801         * Fires when a drag operation is complete
11802         * @param {Roo.tree.TreePanel} this
11803         * @param {Roo.tree.TreeNode} node
11804         * @param {event} e The raw browser event
11805         */
11806        "enddrag" : true,
11807        /**
11808         * @event dragdrop
11809         * Fires when a dragged node is dropped on a valid DD target
11810         * @param {Roo.tree.TreePanel} this
11811         * @param {Roo.tree.TreeNode} node
11812         * @param {DD} dd The dd it was dropped on
11813         * @param {event} e The raw browser event
11814         */
11815        "dragdrop" : true,
11816        /**
11817         * @event beforenodedrop
11818         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11819         * passed to handlers has the following properties:<br />
11820         * <ul style="padding:5px;padding-left:16px;">
11821         * <li>tree - The TreePanel</li>
11822         * <li>target - The node being targeted for the drop</li>
11823         * <li>data - The drag data from the drag source</li>
11824         * <li>point - The point of the drop - append, above or below</li>
11825         * <li>source - The drag source</li>
11826         * <li>rawEvent - Raw mouse event</li>
11827         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11828         * to be inserted by setting them on this object.</li>
11829         * <li>cancel - Set this to true to cancel the drop.</li>
11830         * </ul>
11831         * @param {Object} dropEvent
11832         */
11833        "beforenodedrop" : true,
11834        /**
11835         * @event nodedrop
11836         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11837         * passed to handlers has the following properties:<br />
11838         * <ul style="padding:5px;padding-left:16px;">
11839         * <li>tree - The TreePanel</li>
11840         * <li>target - The node being targeted for the drop</li>
11841         * <li>data - The drag data from the drag source</li>
11842         * <li>point - The point of the drop - append, above or below</li>
11843         * <li>source - The drag source</li>
11844         * <li>rawEvent - Raw mouse event</li>
11845         * <li>dropNode - Dropped node(s).</li>
11846         * </ul>
11847         * @param {Object} dropEvent
11848         */
11849        "nodedrop" : true,
11850         /**
11851         * @event nodedragover
11852         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11853         * passed to handlers has the following properties:<br />
11854         * <ul style="padding:5px;padding-left:16px;">
11855         * <li>tree - The TreePanel</li>
11856         * <li>target - The node being targeted for the drop</li>
11857         * <li>data - The drag data from the drag source</li>
11858         * <li>point - The point of the drop - append, above or below</li>
11859         * <li>source - The drag source</li>
11860         * <li>rawEvent - Raw mouse event</li>
11861         * <li>dropNode - Drop node(s) provided by the source.</li>
11862         * <li>cancel - Set this to true to signal drop not allowed.</li>
11863         * </ul>
11864         * @param {Object} dragOverEvent
11865         */
11866        "nodedragover" : true,
11867        /**
11868         * @event appendnode
11869         * Fires when append node to the tree
11870         * @param {Roo.tree.TreePanel} this
11871         * @param {Roo.tree.TreeNode} node
11872         * @param {Number} index The index of the newly appended node
11873         */
11874        "appendnode" : true
11875         
11876     });
11877     if(this.singleExpand){
11878        this.on("beforeexpand", this.restrictExpand, this);
11879     }
11880     if (this.editor) {
11881         this.editor.tree = this;
11882         this.editor = Roo.factory(this.editor, Roo.tree);
11883     }
11884     
11885     if (this.selModel) {
11886         this.selModel = Roo.factory(this.selModel, Roo.tree);
11887     }
11888    
11889 };
11890 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11891     rootVisible : true,
11892     animate: Roo.enableFx,
11893     lines : true,
11894     enableDD : false,
11895     hlDrop : Roo.enableFx,
11896   
11897     renderer: false,
11898     
11899     rendererTip: false,
11900     // private
11901     restrictExpand : function(node){
11902         var p = node.parentNode;
11903         if(p){
11904             if(p.expandedChild && p.expandedChild.parentNode == p){
11905                 p.expandedChild.collapse();
11906             }
11907             p.expandedChild = node;
11908         }
11909     },
11910
11911     // private override
11912     setRootNode : function(node){
11913         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11914         if(!this.rootVisible){
11915             node.ui = new Roo.tree.RootTreeNodeUI(node);
11916         }
11917         return node;
11918     },
11919
11920     /**
11921      * Returns the container element for this TreePanel
11922      */
11923     getEl : function(){
11924         return this.el;
11925     },
11926
11927     /**
11928      * Returns the default TreeLoader for this TreePanel
11929      */
11930     getLoader : function(){
11931         return this.loader;
11932     },
11933
11934     /**
11935      * Expand all nodes
11936      */
11937     expandAll : function(){
11938         this.root.expand(true);
11939     },
11940
11941     /**
11942      * Collapse all nodes
11943      */
11944     collapseAll : function(){
11945         this.root.collapse(true);
11946     },
11947
11948     /**
11949      * Returns the selection model used by this TreePanel
11950      */
11951     getSelectionModel : function(){
11952         if(!this.selModel){
11953             this.selModel = new Roo.tree.DefaultSelectionModel();
11954         }
11955         return this.selModel;
11956     },
11957
11958     /**
11959      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11960      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11961      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11962      * @return {Array}
11963      */
11964     getChecked : function(a, startNode){
11965         startNode = startNode || this.root;
11966         var r = [];
11967         var f = function(){
11968             if(this.attributes.checked){
11969                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11970             }
11971         }
11972         startNode.cascade(f);
11973         return r;
11974     },
11975
11976     /**
11977      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11978      * @param {String} path
11979      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11980      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11981      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11982      */
11983     expandPath : function(path, attr, callback){
11984         attr = attr || "id";
11985         var keys = path.split(this.pathSeparator);
11986         var curNode = this.root;
11987         if(curNode.attributes[attr] != keys[1]){ // invalid root
11988             if(callback){
11989                 callback(false, null);
11990             }
11991             return;
11992         }
11993         var index = 1;
11994         var f = function(){
11995             if(++index == keys.length){
11996                 if(callback){
11997                     callback(true, curNode);
11998                 }
11999                 return;
12000             }
12001             var c = curNode.findChild(attr, keys[index]);
12002             if(!c){
12003                 if(callback){
12004                     callback(false, curNode);
12005                 }
12006                 return;
12007             }
12008             curNode = c;
12009             c.expand(false, false, f);
12010         };
12011         curNode.expand(false, false, f);
12012     },
12013
12014     /**
12015      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
12016      * @param {String} path
12017      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
12018      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
12019      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
12020      */
12021     selectPath : function(path, attr, callback){
12022         attr = attr || "id";
12023         var keys = path.split(this.pathSeparator);
12024         var v = keys.pop();
12025         if(keys.length > 0){
12026             var f = function(success, node){
12027                 if(success && node){
12028                     var n = node.findChild(attr, v);
12029                     if(n){
12030                         n.select();
12031                         if(callback){
12032                             callback(true, n);
12033                         }
12034                     }else if(callback){
12035                         callback(false, n);
12036                     }
12037                 }else{
12038                     if(callback){
12039                         callback(false, n);
12040                     }
12041                 }
12042             };
12043             this.expandPath(keys.join(this.pathSeparator), attr, f);
12044         }else{
12045             this.root.select();
12046             if(callback){
12047                 callback(true, this.root);
12048             }
12049         }
12050     },
12051
12052     getTreeEl : function(){
12053         return this.el;
12054     },
12055
12056     /**
12057      * Trigger rendering of this TreePanel
12058      */
12059     render : function(){
12060         if (this.innerCt) {
12061             return this; // stop it rendering more than once!!
12062         }
12063         
12064         this.innerCt = this.el.createChild({tag:"ul",
12065                cls:"x-tree-root-ct " +
12066                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12067
12068         if(this.containerScroll){
12069             Roo.dd.ScrollManager.register(this.el);
12070         }
12071         if((this.enableDD || this.enableDrop) && !this.dropZone){
12072            /**
12073             * The dropZone used by this tree if drop is enabled
12074             * @type Roo.tree.TreeDropZone
12075             */
12076              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12077                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12078            });
12079         }
12080         if((this.enableDD || this.enableDrag) && !this.dragZone){
12081            /**
12082             * The dragZone used by this tree if drag is enabled
12083             * @type Roo.tree.TreeDragZone
12084             */
12085             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12086                ddGroup: this.ddGroup || "TreeDD",
12087                scroll: this.ddScroll
12088            });
12089         }
12090         this.getSelectionModel().init(this);
12091         if (!this.root) {
12092             Roo.log("ROOT not set in tree");
12093             return this;
12094         }
12095         this.root.render();
12096         if(!this.rootVisible){
12097             this.root.renderChildren();
12098         }
12099         return this;
12100     }
12101 });/*
12102  * Based on:
12103  * Ext JS Library 1.1.1
12104  * Copyright(c) 2006-2007, Ext JS, LLC.
12105  *
12106  * Originally Released Under LGPL - original licence link has changed is not relivant.
12107  *
12108  * Fork - LGPL
12109  * <script type="text/javascript">
12110  */
12111  
12112
12113 /**
12114  * @class Roo.tree.DefaultSelectionModel
12115  * @extends Roo.util.Observable
12116  * The default single selection for a TreePanel.
12117  * @param {Object} cfg Configuration
12118  */
12119 Roo.tree.DefaultSelectionModel = function(cfg){
12120    this.selNode = null;
12121    
12122    
12123    
12124    this.addEvents({
12125        /**
12126         * @event selectionchange
12127         * Fires when the selected node changes
12128         * @param {DefaultSelectionModel} this
12129         * @param {TreeNode} node the new selection
12130         */
12131        "selectionchange" : true,
12132
12133        /**
12134         * @event beforeselect
12135         * Fires before the selected node changes, return false to cancel the change
12136         * @param {DefaultSelectionModel} this
12137         * @param {TreeNode} node the new selection
12138         * @param {TreeNode} node the old selection
12139         */
12140        "beforeselect" : true
12141    });
12142    
12143     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12144 };
12145
12146 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12147     init : function(tree){
12148         this.tree = tree;
12149         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12150         tree.on("click", this.onNodeClick, this);
12151     },
12152     
12153     onNodeClick : function(node, e){
12154         if (e.ctrlKey && this.selNode == node)  {
12155             this.unselect(node);
12156             return;
12157         }
12158         this.select(node);
12159     },
12160     
12161     /**
12162      * Select a node.
12163      * @param {TreeNode} node The node to select
12164      * @return {TreeNode} The selected node
12165      */
12166     select : function(node){
12167         var last = this.selNode;
12168         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12169             if(last){
12170                 last.ui.onSelectedChange(false);
12171             }
12172             this.selNode = node;
12173             node.ui.onSelectedChange(true);
12174             this.fireEvent("selectionchange", this, node, last);
12175         }
12176         return node;
12177     },
12178     
12179     /**
12180      * Deselect a node.
12181      * @param {TreeNode} node The node to unselect
12182      */
12183     unselect : function(node){
12184         if(this.selNode == node){
12185             this.clearSelections();
12186         }    
12187     },
12188     
12189     /**
12190      * Clear all selections
12191      */
12192     clearSelections : function(){
12193         var n = this.selNode;
12194         if(n){
12195             n.ui.onSelectedChange(false);
12196             this.selNode = null;
12197             this.fireEvent("selectionchange", this, null);
12198         }
12199         return n;
12200     },
12201     
12202     /**
12203      * Get the selected node
12204      * @return {TreeNode} The selected node
12205      */
12206     getSelectedNode : function(){
12207         return this.selNode;    
12208     },
12209     
12210     /**
12211      * Returns true if the node is selected
12212      * @param {TreeNode} node The node to check
12213      * @return {Boolean}
12214      */
12215     isSelected : function(node){
12216         return this.selNode == node;  
12217     },
12218
12219     /**
12220      * Selects the node above the selected node in the tree, intelligently walking the nodes
12221      * @return TreeNode The new selection
12222      */
12223     selectPrevious : function(){
12224         var s = this.selNode || this.lastSelNode;
12225         if(!s){
12226             return null;
12227         }
12228         var ps = s.previousSibling;
12229         if(ps){
12230             if(!ps.isExpanded() || ps.childNodes.length < 1){
12231                 return this.select(ps);
12232             } else{
12233                 var lc = ps.lastChild;
12234                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12235                     lc = lc.lastChild;
12236                 }
12237                 return this.select(lc);
12238             }
12239         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12240             return this.select(s.parentNode);
12241         }
12242         return null;
12243     },
12244
12245     /**
12246      * Selects the node above the selected node in the tree, intelligently walking the nodes
12247      * @return TreeNode The new selection
12248      */
12249     selectNext : function(){
12250         var s = this.selNode || this.lastSelNode;
12251         if(!s){
12252             return null;
12253         }
12254         if(s.firstChild && s.isExpanded()){
12255              return this.select(s.firstChild);
12256          }else if(s.nextSibling){
12257              return this.select(s.nextSibling);
12258          }else if(s.parentNode){
12259             var newS = null;
12260             s.parentNode.bubble(function(){
12261                 if(this.nextSibling){
12262                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12263                     return false;
12264                 }
12265             });
12266             return newS;
12267          }
12268         return null;
12269     },
12270
12271     onKeyDown : function(e){
12272         var s = this.selNode || this.lastSelNode;
12273         // undesirable, but required
12274         var sm = this;
12275         if(!s){
12276             return;
12277         }
12278         var k = e.getKey();
12279         switch(k){
12280              case e.DOWN:
12281                  e.stopEvent();
12282                  this.selectNext();
12283              break;
12284              case e.UP:
12285                  e.stopEvent();
12286                  this.selectPrevious();
12287              break;
12288              case e.RIGHT:
12289                  e.preventDefault();
12290                  if(s.hasChildNodes()){
12291                      if(!s.isExpanded()){
12292                          s.expand();
12293                      }else if(s.firstChild){
12294                          this.select(s.firstChild, e);
12295                      }
12296                  }
12297              break;
12298              case e.LEFT:
12299                  e.preventDefault();
12300                  if(s.hasChildNodes() && s.isExpanded()){
12301                      s.collapse();
12302                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12303                      this.select(s.parentNode, e);
12304                  }
12305              break;
12306         };
12307     }
12308 });
12309
12310 /**
12311  * @class Roo.tree.MultiSelectionModel
12312  * @extends Roo.util.Observable
12313  * Multi selection for a TreePanel.
12314  * @param {Object} cfg Configuration
12315  */
12316 Roo.tree.MultiSelectionModel = function(){
12317    this.selNodes = [];
12318    this.selMap = {};
12319    this.addEvents({
12320        /**
12321         * @event selectionchange
12322         * Fires when the selected nodes change
12323         * @param {MultiSelectionModel} this
12324         * @param {Array} nodes Array of the selected nodes
12325         */
12326        "selectionchange" : true
12327    });
12328    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12329    
12330 };
12331
12332 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12333     init : function(tree){
12334         this.tree = tree;
12335         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12336         tree.on("click", this.onNodeClick, this);
12337     },
12338     
12339     onNodeClick : function(node, e){
12340         this.select(node, e, e.ctrlKey);
12341     },
12342     
12343     /**
12344      * Select a node.
12345      * @param {TreeNode} node The node to select
12346      * @param {EventObject} e (optional) An event associated with the selection
12347      * @param {Boolean} keepExisting True to retain existing selections
12348      * @return {TreeNode} The selected node
12349      */
12350     select : function(node, e, keepExisting){
12351         if(keepExisting !== true){
12352             this.clearSelections(true);
12353         }
12354         if(this.isSelected(node)){
12355             this.lastSelNode = node;
12356             return node;
12357         }
12358         this.selNodes.push(node);
12359         this.selMap[node.id] = node;
12360         this.lastSelNode = node;
12361         node.ui.onSelectedChange(true);
12362         this.fireEvent("selectionchange", this, this.selNodes);
12363         return node;
12364     },
12365     
12366     /**
12367      * Deselect a node.
12368      * @param {TreeNode} node The node to unselect
12369      */
12370     unselect : function(node){
12371         if(this.selMap[node.id]){
12372             node.ui.onSelectedChange(false);
12373             var sn = this.selNodes;
12374             var index = -1;
12375             if(sn.indexOf){
12376                 index = sn.indexOf(node);
12377             }else{
12378                 for(var i = 0, len = sn.length; i < len; i++){
12379                     if(sn[i] == node){
12380                         index = i;
12381                         break;
12382                     }
12383                 }
12384             }
12385             if(index != -1){
12386                 this.selNodes.splice(index, 1);
12387             }
12388             delete this.selMap[node.id];
12389             this.fireEvent("selectionchange", this, this.selNodes);
12390         }
12391     },
12392     
12393     /**
12394      * Clear all selections
12395      */
12396     clearSelections : function(suppressEvent){
12397         var sn = this.selNodes;
12398         if(sn.length > 0){
12399             for(var i = 0, len = sn.length; i < len; i++){
12400                 sn[i].ui.onSelectedChange(false);
12401             }
12402             this.selNodes = [];
12403             this.selMap = {};
12404             if(suppressEvent !== true){
12405                 this.fireEvent("selectionchange", this, this.selNodes);
12406             }
12407         }
12408     },
12409     
12410     /**
12411      * Returns true if the node is selected
12412      * @param {TreeNode} node The node to check
12413      * @return {Boolean}
12414      */
12415     isSelected : function(node){
12416         return this.selMap[node.id] ? true : false;  
12417     },
12418     
12419     /**
12420      * Returns an array of the selected nodes
12421      * @return {Array}
12422      */
12423     getSelectedNodes : function(){
12424         return this.selNodes;    
12425     },
12426
12427     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12428
12429     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12430
12431     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12432 });/*
12433  * Based on:
12434  * Ext JS Library 1.1.1
12435  * Copyright(c) 2006-2007, Ext JS, LLC.
12436  *
12437  * Originally Released Under LGPL - original licence link has changed is not relivant.
12438  *
12439  * Fork - LGPL
12440  * <script type="text/javascript">
12441  */
12442  
12443 /**
12444  * @class Roo.tree.TreeNode
12445  * @extends Roo.data.Node
12446  * @cfg {String} text The text for this node
12447  * @cfg {Boolean} expanded true to start the node expanded
12448  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12449  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12450  * @cfg {Boolean} disabled true to start the node disabled
12451  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12452  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12453  * @cfg {String} cls A css class to be added to the node
12454  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12455  * @cfg {String} href URL of the link used for the node (defaults to #)
12456  * @cfg {String} hrefTarget target frame for the link
12457  * @cfg {String} qtip An Ext QuickTip for the node
12458  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12459  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12460  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12461  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12462  * (defaults to undefined with no checkbox rendered)
12463  * @constructor
12464  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12465  */
12466 Roo.tree.TreeNode = function(attributes){
12467     attributes = attributes || {};
12468     if(typeof attributes == "string"){
12469         attributes = {text: attributes};
12470     }
12471     this.childrenRendered = false;
12472     this.rendered = false;
12473     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12474     this.expanded = attributes.expanded === true;
12475     this.isTarget = attributes.isTarget !== false;
12476     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12477     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12478
12479     /**
12480      * Read-only. The text for this node. To change it use setText().
12481      * @type String
12482      */
12483     this.text = attributes.text;
12484     /**
12485      * True if this node is disabled.
12486      * @type Boolean
12487      */
12488     this.disabled = attributes.disabled === true;
12489
12490     this.addEvents({
12491         /**
12492         * @event textchange
12493         * Fires when the text for this node is changed
12494         * @param {Node} this This node
12495         * @param {String} text The new text
12496         * @param {String} oldText The old text
12497         */
12498         "textchange" : true,
12499         /**
12500         * @event beforeexpand
12501         * Fires before this node is expanded, return false to cancel.
12502         * @param {Node} this This node
12503         * @param {Boolean} deep
12504         * @param {Boolean} anim
12505         */
12506         "beforeexpand" : true,
12507         /**
12508         * @event beforecollapse
12509         * Fires before this node is collapsed, return false to cancel.
12510         * @param {Node} this This node
12511         * @param {Boolean} deep
12512         * @param {Boolean} anim
12513         */
12514         "beforecollapse" : true,
12515         /**
12516         * @event expand
12517         * Fires when this node is expanded
12518         * @param {Node} this This node
12519         */
12520         "expand" : true,
12521         /**
12522         * @event disabledchange
12523         * Fires when the disabled status of this node changes
12524         * @param {Node} this This node
12525         * @param {Boolean} disabled
12526         */
12527         "disabledchange" : true,
12528         /**
12529         * @event collapse
12530         * Fires when this node is collapsed
12531         * @param {Node} this This node
12532         */
12533         "collapse" : true,
12534         /**
12535         * @event beforeclick
12536         * Fires before click processing. Return false to cancel the default action.
12537         * @param {Node} this This node
12538         * @param {Roo.EventObject} e The event object
12539         */
12540         "beforeclick":true,
12541         /**
12542         * @event checkchange
12543         * Fires when a node with a checkbox's checked property changes
12544         * @param {Node} this This node
12545         * @param {Boolean} checked
12546         */
12547         "checkchange":true,
12548         /**
12549         * @event click
12550         * Fires when this node is clicked
12551         * @param {Node} this This node
12552         * @param {Roo.EventObject} e The event object
12553         */
12554         "click":true,
12555         /**
12556         * @event dblclick
12557         * Fires when this node is double clicked
12558         * @param {Node} this This node
12559         * @param {Roo.EventObject} e The event object
12560         */
12561         "dblclick":true,
12562         /**
12563         * @event contextmenu
12564         * Fires when this node is right clicked
12565         * @param {Node} this This node
12566         * @param {Roo.EventObject} e The event object
12567         */
12568         "contextmenu":true,
12569         /**
12570         * @event beforechildrenrendered
12571         * Fires right before the child nodes for this node are rendered
12572         * @param {Node} this This node
12573         */
12574         "beforechildrenrendered":true
12575     });
12576
12577     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12578
12579     /**
12580      * Read-only. The UI for this node
12581      * @type TreeNodeUI
12582      */
12583     this.ui = new uiClass(this);
12584     
12585     // finally support items[]
12586     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12587         return;
12588     }
12589     
12590     
12591     Roo.each(this.attributes.items, function(c) {
12592         this.appendChild(Roo.factory(c,Roo.Tree));
12593     }, this);
12594     delete this.attributes.items;
12595     
12596     
12597     
12598 };
12599 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12600     preventHScroll: true,
12601     /**
12602      * Returns true if this node is expanded
12603      * @return {Boolean}
12604      */
12605     isExpanded : function(){
12606         return this.expanded;
12607     },
12608
12609     /**
12610      * Returns the UI object for this node
12611      * @return {TreeNodeUI}
12612      */
12613     getUI : function(){
12614         return this.ui;
12615     },
12616
12617     // private override
12618     setFirstChild : function(node){
12619         var of = this.firstChild;
12620         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12621         if(this.childrenRendered && of && node != of){
12622             of.renderIndent(true, true);
12623         }
12624         if(this.rendered){
12625             this.renderIndent(true, true);
12626         }
12627     },
12628
12629     // private override
12630     setLastChild : function(node){
12631         var ol = this.lastChild;
12632         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12633         if(this.childrenRendered && ol && node != ol){
12634             ol.renderIndent(true, true);
12635         }
12636         if(this.rendered){
12637             this.renderIndent(true, true);
12638         }
12639     },
12640
12641     // these methods are overridden to provide lazy rendering support
12642     // private override
12643     appendChild : function()
12644     {
12645         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12646         if(node && this.childrenRendered){
12647             node.render();
12648         }
12649         this.ui.updateExpandIcon();
12650         return node;
12651     },
12652
12653     // private override
12654     removeChild : function(node){
12655         this.ownerTree.getSelectionModel().unselect(node);
12656         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12657         // if it's been rendered remove dom node
12658         if(this.childrenRendered){
12659             node.ui.remove();
12660         }
12661         if(this.childNodes.length < 1){
12662             this.collapse(false, false);
12663         }else{
12664             this.ui.updateExpandIcon();
12665         }
12666         if(!this.firstChild) {
12667             this.childrenRendered = false;
12668         }
12669         return node;
12670     },
12671
12672     // private override
12673     insertBefore : function(node, refNode){
12674         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12675         if(newNode && refNode && this.childrenRendered){
12676             node.render();
12677         }
12678         this.ui.updateExpandIcon();
12679         return newNode;
12680     },
12681
12682     /**
12683      * Sets the text for this node
12684      * @param {String} text
12685      */
12686     setText : function(text){
12687         var oldText = this.text;
12688         this.text = text;
12689         this.attributes.text = text;
12690         if(this.rendered){ // event without subscribing
12691             this.ui.onTextChange(this, text, oldText);
12692         }
12693         this.fireEvent("textchange", this, text, oldText);
12694     },
12695
12696     /**
12697      * Triggers selection of this node
12698      */
12699     select : function(){
12700         this.getOwnerTree().getSelectionModel().select(this);
12701     },
12702
12703     /**
12704      * Triggers deselection of this node
12705      */
12706     unselect : function(){
12707         this.getOwnerTree().getSelectionModel().unselect(this);
12708     },
12709
12710     /**
12711      * Returns true if this node is selected
12712      * @return {Boolean}
12713      */
12714     isSelected : function(){
12715         return this.getOwnerTree().getSelectionModel().isSelected(this);
12716     },
12717
12718     /**
12719      * Expand this node.
12720      * @param {Boolean} deep (optional) True to expand all children as well
12721      * @param {Boolean} anim (optional) false to cancel the default animation
12722      * @param {Function} callback (optional) A callback to be called when
12723      * expanding this node completes (does not wait for deep expand to complete).
12724      * Called with 1 parameter, this node.
12725      */
12726     expand : function(deep, anim, callback){
12727         if(!this.expanded){
12728             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12729                 return;
12730             }
12731             if(!this.childrenRendered){
12732                 this.renderChildren();
12733             }
12734             this.expanded = true;
12735             
12736             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12737                 this.ui.animExpand(function(){
12738                     this.fireEvent("expand", this);
12739                     if(typeof callback == "function"){
12740                         callback(this);
12741                     }
12742                     if(deep === true){
12743                         this.expandChildNodes(true);
12744                     }
12745                 }.createDelegate(this));
12746                 return;
12747             }else{
12748                 this.ui.expand();
12749                 this.fireEvent("expand", this);
12750                 if(typeof callback == "function"){
12751                     callback(this);
12752                 }
12753             }
12754         }else{
12755            if(typeof callback == "function"){
12756                callback(this);
12757            }
12758         }
12759         if(deep === true){
12760             this.expandChildNodes(true);
12761         }
12762     },
12763
12764     isHiddenRoot : function(){
12765         return this.isRoot && !this.getOwnerTree().rootVisible;
12766     },
12767
12768     /**
12769      * Collapse this node.
12770      * @param {Boolean} deep (optional) True to collapse all children as well
12771      * @param {Boolean} anim (optional) false to cancel the default animation
12772      */
12773     collapse : function(deep, anim){
12774         if(this.expanded && !this.isHiddenRoot()){
12775             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12776                 return;
12777             }
12778             this.expanded = false;
12779             if((this.getOwnerTree().animate && anim !== false) || anim){
12780                 this.ui.animCollapse(function(){
12781                     this.fireEvent("collapse", this);
12782                     if(deep === true){
12783                         this.collapseChildNodes(true);
12784                     }
12785                 }.createDelegate(this));
12786                 return;
12787             }else{
12788                 this.ui.collapse();
12789                 this.fireEvent("collapse", this);
12790             }
12791         }
12792         if(deep === true){
12793             var cs = this.childNodes;
12794             for(var i = 0, len = cs.length; i < len; i++) {
12795                 cs[i].collapse(true, false);
12796             }
12797         }
12798     },
12799
12800     // private
12801     delayedExpand : function(delay){
12802         if(!this.expandProcId){
12803             this.expandProcId = this.expand.defer(delay, this);
12804         }
12805     },
12806
12807     // private
12808     cancelExpand : function(){
12809         if(this.expandProcId){
12810             clearTimeout(this.expandProcId);
12811         }
12812         this.expandProcId = false;
12813     },
12814
12815     /**
12816      * Toggles expanded/collapsed state of the node
12817      */
12818     toggle : function(){
12819         if(this.expanded){
12820             this.collapse();
12821         }else{
12822             this.expand();
12823         }
12824     },
12825
12826     /**
12827      * Ensures all parent nodes are expanded
12828      */
12829     ensureVisible : function(callback){
12830         var tree = this.getOwnerTree();
12831         tree.expandPath(this.parentNode.getPath(), false, function(){
12832             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12833             Roo.callback(callback);
12834         }.createDelegate(this));
12835     },
12836
12837     /**
12838      * Expand all child nodes
12839      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12840      */
12841     expandChildNodes : function(deep){
12842         var cs = this.childNodes;
12843         for(var i = 0, len = cs.length; i < len; i++) {
12844                 cs[i].expand(deep);
12845         }
12846     },
12847
12848     /**
12849      * Collapse all child nodes
12850      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12851      */
12852     collapseChildNodes : function(deep){
12853         var cs = this.childNodes;
12854         for(var i = 0, len = cs.length; i < len; i++) {
12855                 cs[i].collapse(deep);
12856         }
12857     },
12858
12859     /**
12860      * Disables this node
12861      */
12862     disable : function(){
12863         this.disabled = true;
12864         this.unselect();
12865         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12866             this.ui.onDisableChange(this, true);
12867         }
12868         this.fireEvent("disabledchange", this, true);
12869     },
12870
12871     /**
12872      * Enables this node
12873      */
12874     enable : function(){
12875         this.disabled = false;
12876         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12877             this.ui.onDisableChange(this, false);
12878         }
12879         this.fireEvent("disabledchange", this, false);
12880     },
12881
12882     // private
12883     renderChildren : function(suppressEvent){
12884         if(suppressEvent !== false){
12885             this.fireEvent("beforechildrenrendered", this);
12886         }
12887         var cs = this.childNodes;
12888         for(var i = 0, len = cs.length; i < len; i++){
12889             cs[i].render(true);
12890         }
12891         this.childrenRendered = true;
12892     },
12893
12894     // private
12895     sort : function(fn, scope){
12896         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12897         if(this.childrenRendered){
12898             var cs = this.childNodes;
12899             for(var i = 0, len = cs.length; i < len; i++){
12900                 cs[i].render(true);
12901             }
12902         }
12903     },
12904
12905     // private
12906     render : function(bulkRender){
12907         this.ui.render(bulkRender);
12908         if(!this.rendered){
12909             this.rendered = true;
12910             if(this.expanded){
12911                 this.expanded = false;
12912                 this.expand(false, false);
12913             }
12914         }
12915     },
12916
12917     // private
12918     renderIndent : function(deep, refresh){
12919         if(refresh){
12920             this.ui.childIndent = null;
12921         }
12922         this.ui.renderIndent();
12923         if(deep === true && this.childrenRendered){
12924             var cs = this.childNodes;
12925             for(var i = 0, len = cs.length; i < len; i++){
12926                 cs[i].renderIndent(true, refresh);
12927             }
12928         }
12929     }
12930 });/*
12931  * Based on:
12932  * Ext JS Library 1.1.1
12933  * Copyright(c) 2006-2007, Ext JS, LLC.
12934  *
12935  * Originally Released Under LGPL - original licence link has changed is not relivant.
12936  *
12937  * Fork - LGPL
12938  * <script type="text/javascript">
12939  */
12940  
12941 /**
12942  * @class Roo.tree.AsyncTreeNode
12943  * @extends Roo.tree.TreeNode
12944  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12945  * @constructor
12946  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12947  */
12948  Roo.tree.AsyncTreeNode = function(config){
12949     this.loaded = false;
12950     this.loading = false;
12951     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12952     /**
12953     * @event beforeload
12954     * Fires before this node is loaded, return false to cancel
12955     * @param {Node} this This node
12956     */
12957     this.addEvents({'beforeload':true, 'load': true});
12958     /**
12959     * @event load
12960     * Fires when this node is loaded
12961     * @param {Node} this This node
12962     */
12963     /**
12964      * The loader used by this node (defaults to using the tree's defined loader)
12965      * @type TreeLoader
12966      * @property loader
12967      */
12968 };
12969 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12970     expand : function(deep, anim, callback){
12971         if(this.loading){ // if an async load is already running, waiting til it's done
12972             var timer;
12973             var f = function(){
12974                 if(!this.loading){ // done loading
12975                     clearInterval(timer);
12976                     this.expand(deep, anim, callback);
12977                 }
12978             }.createDelegate(this);
12979             timer = setInterval(f, 200);
12980             return;
12981         }
12982         if(!this.loaded){
12983             if(this.fireEvent("beforeload", this) === false){
12984                 return;
12985             }
12986             this.loading = true;
12987             this.ui.beforeLoad(this);
12988             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12989             if(loader){
12990                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12991                 return;
12992             }
12993         }
12994         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12995     },
12996     
12997     /**
12998      * Returns true if this node is currently loading
12999      * @return {Boolean}
13000      */
13001     isLoading : function(){
13002         return this.loading;  
13003     },
13004     
13005     loadComplete : function(deep, anim, callback){
13006         this.loading = false;
13007         this.loaded = true;
13008         this.ui.afterLoad(this);
13009         this.fireEvent("load", this);
13010         this.expand(deep, anim, callback);
13011     },
13012     
13013     /**
13014      * Returns true if this node has been loaded
13015      * @return {Boolean}
13016      */
13017     isLoaded : function(){
13018         return this.loaded;
13019     },
13020     
13021     hasChildNodes : function(){
13022         if(!this.isLeaf() && !this.loaded){
13023             return true;
13024         }else{
13025             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13026         }
13027     },
13028
13029     /**
13030      * Trigger a reload for this node
13031      * @param {Function} callback
13032      */
13033     reload : function(callback){
13034         this.collapse(false, false);
13035         while(this.firstChild){
13036             this.removeChild(this.firstChild);
13037         }
13038         this.childrenRendered = false;
13039         this.loaded = false;
13040         if(this.isHiddenRoot()){
13041             this.expanded = false;
13042         }
13043         this.expand(false, false, callback);
13044     }
13045 });/*
13046  * Based on:
13047  * Ext JS Library 1.1.1
13048  * Copyright(c) 2006-2007, Ext JS, LLC.
13049  *
13050  * Originally Released Under LGPL - original licence link has changed is not relivant.
13051  *
13052  * Fork - LGPL
13053  * <script type="text/javascript">
13054  */
13055  
13056 /**
13057  * @class Roo.tree.TreeNodeUI
13058  * @constructor
13059  * @param {Object} node The node to render
13060  * The TreeNode UI implementation is separate from the
13061  * tree implementation. Unless you are customizing the tree UI,
13062  * you should never have to use this directly.
13063  */
13064 Roo.tree.TreeNodeUI = function(node){
13065     this.node = node;
13066     this.rendered = false;
13067     this.animating = false;
13068     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13069 };
13070
13071 Roo.tree.TreeNodeUI.prototype = {
13072     removeChild : function(node){
13073         if(this.rendered){
13074             this.ctNode.removeChild(node.ui.getEl());
13075         }
13076     },
13077
13078     beforeLoad : function(){
13079          this.addClass("x-tree-node-loading");
13080     },
13081
13082     afterLoad : function(){
13083          this.removeClass("x-tree-node-loading");
13084     },
13085
13086     onTextChange : function(node, text, oldText){
13087         if(this.rendered){
13088             this.textNode.innerHTML = text;
13089         }
13090     },
13091
13092     onDisableChange : function(node, state){
13093         this.disabled = state;
13094         if(state){
13095             this.addClass("x-tree-node-disabled");
13096         }else{
13097             this.removeClass("x-tree-node-disabled");
13098         }
13099     },
13100
13101     onSelectedChange : function(state){
13102         if(state){
13103             this.focus();
13104             this.addClass("x-tree-selected");
13105         }else{
13106             //this.blur();
13107             this.removeClass("x-tree-selected");
13108         }
13109     },
13110
13111     onMove : function(tree, node, oldParent, newParent, index, refNode){
13112         this.childIndent = null;
13113         if(this.rendered){
13114             var targetNode = newParent.ui.getContainer();
13115             if(!targetNode){//target not rendered
13116                 this.holder = document.createElement("div");
13117                 this.holder.appendChild(this.wrap);
13118                 return;
13119             }
13120             var insertBefore = refNode ? refNode.ui.getEl() : null;
13121             if(insertBefore){
13122                 targetNode.insertBefore(this.wrap, insertBefore);
13123             }else{
13124                 targetNode.appendChild(this.wrap);
13125             }
13126             this.node.renderIndent(true);
13127         }
13128     },
13129
13130     addClass : function(cls){
13131         if(this.elNode){
13132             Roo.fly(this.elNode).addClass(cls);
13133         }
13134     },
13135
13136     removeClass : function(cls){
13137         if(this.elNode){
13138             Roo.fly(this.elNode).removeClass(cls);
13139         }
13140     },
13141
13142     remove : function(){
13143         if(this.rendered){
13144             this.holder = document.createElement("div");
13145             this.holder.appendChild(this.wrap);
13146         }
13147     },
13148
13149     fireEvent : function(){
13150         return this.node.fireEvent.apply(this.node, arguments);
13151     },
13152
13153     initEvents : function(){
13154         this.node.on("move", this.onMove, this);
13155         var E = Roo.EventManager;
13156         var a = this.anchor;
13157
13158         var el = Roo.fly(a, '_treeui');
13159
13160         if(Roo.isOpera){ // opera render bug ignores the CSS
13161             el.setStyle("text-decoration", "none");
13162         }
13163
13164         el.on("click", this.onClick, this);
13165         el.on("dblclick", this.onDblClick, this);
13166
13167         if(this.checkbox){
13168             Roo.EventManager.on(this.checkbox,
13169                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13170         }
13171
13172         el.on("contextmenu", this.onContextMenu, this);
13173
13174         var icon = Roo.fly(this.iconNode);
13175         icon.on("click", this.onClick, this);
13176         icon.on("dblclick", this.onDblClick, this);
13177         icon.on("contextmenu", this.onContextMenu, this);
13178         E.on(this.ecNode, "click", this.ecClick, this, true);
13179
13180         if(this.node.disabled){
13181             this.addClass("x-tree-node-disabled");
13182         }
13183         if(this.node.hidden){
13184             this.addClass("x-tree-node-disabled");
13185         }
13186         var ot = this.node.getOwnerTree();
13187         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13188         if(dd && (!this.node.isRoot || ot.rootVisible)){
13189             Roo.dd.Registry.register(this.elNode, {
13190                 node: this.node,
13191                 handles: this.getDDHandles(),
13192                 isHandle: false
13193             });
13194         }
13195     },
13196
13197     getDDHandles : function(){
13198         return [this.iconNode, this.textNode];
13199     },
13200
13201     hide : function(){
13202         if(this.rendered){
13203             this.wrap.style.display = "none";
13204         }
13205     },
13206
13207     show : function(){
13208         if(this.rendered){
13209             this.wrap.style.display = "";
13210         }
13211     },
13212
13213     onContextMenu : function(e){
13214         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13215             e.preventDefault();
13216             this.focus();
13217             this.fireEvent("contextmenu", this.node, e);
13218         }
13219     },
13220
13221     onClick : function(e){
13222         if(this.dropping){
13223             e.stopEvent();
13224             return;
13225         }
13226         if(this.fireEvent("beforeclick", this.node, e) !== false){
13227             if(!this.disabled && this.node.attributes.href){
13228                 this.fireEvent("click", this.node, e);
13229                 return;
13230             }
13231             e.preventDefault();
13232             if(this.disabled){
13233                 return;
13234             }
13235
13236             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13237                 this.node.toggle();
13238             }
13239
13240             this.fireEvent("click", this.node, e);
13241         }else{
13242             e.stopEvent();
13243         }
13244     },
13245
13246     onDblClick : function(e){
13247         e.preventDefault();
13248         if(this.disabled){
13249             return;
13250         }
13251         if(this.checkbox){
13252             this.toggleCheck();
13253         }
13254         if(!this.animating && this.node.hasChildNodes()){
13255             this.node.toggle();
13256         }
13257         this.fireEvent("dblclick", this.node, e);
13258     },
13259
13260     onCheckChange : function(){
13261         var checked = this.checkbox.checked;
13262         this.node.attributes.checked = checked;
13263         this.fireEvent('checkchange', this.node, checked);
13264     },
13265
13266     ecClick : function(e){
13267         if(!this.animating && this.node.hasChildNodes()){
13268             this.node.toggle();
13269         }
13270     },
13271
13272     startDrop : function(){
13273         this.dropping = true;
13274     },
13275
13276     // delayed drop so the click event doesn't get fired on a drop
13277     endDrop : function(){
13278        setTimeout(function(){
13279            this.dropping = false;
13280        }.createDelegate(this), 50);
13281     },
13282
13283     expand : function(){
13284         this.updateExpandIcon();
13285         this.ctNode.style.display = "";
13286     },
13287
13288     focus : function(){
13289         if(!this.node.preventHScroll){
13290             try{this.anchor.focus();
13291             }catch(e){}
13292         }else if(!Roo.isIE){
13293             try{
13294                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13295                 var l = noscroll.scrollLeft;
13296                 this.anchor.focus();
13297                 noscroll.scrollLeft = l;
13298             }catch(e){}
13299         }
13300     },
13301
13302     toggleCheck : function(value){
13303         var cb = this.checkbox;
13304         if(cb){
13305             cb.checked = (value === undefined ? !cb.checked : value);
13306         }
13307     },
13308
13309     blur : function(){
13310         try{
13311             this.anchor.blur();
13312         }catch(e){}
13313     },
13314
13315     animExpand : function(callback){
13316         var ct = Roo.get(this.ctNode);
13317         ct.stopFx();
13318         if(!this.node.hasChildNodes()){
13319             this.updateExpandIcon();
13320             this.ctNode.style.display = "";
13321             Roo.callback(callback);
13322             return;
13323         }
13324         this.animating = true;
13325         this.updateExpandIcon();
13326
13327         ct.slideIn('t', {
13328            callback : function(){
13329                this.animating = false;
13330                Roo.callback(callback);
13331             },
13332             scope: this,
13333             duration: this.node.ownerTree.duration || .25
13334         });
13335     },
13336
13337     highlight : function(){
13338         var tree = this.node.getOwnerTree();
13339         Roo.fly(this.wrap).highlight(
13340             tree.hlColor || "C3DAF9",
13341             {endColor: tree.hlBaseColor}
13342         );
13343     },
13344
13345     collapse : function(){
13346         this.updateExpandIcon();
13347         this.ctNode.style.display = "none";
13348     },
13349
13350     animCollapse : function(callback){
13351         var ct = Roo.get(this.ctNode);
13352         ct.enableDisplayMode('block');
13353         ct.stopFx();
13354
13355         this.animating = true;
13356         this.updateExpandIcon();
13357
13358         ct.slideOut('t', {
13359             callback : function(){
13360                this.animating = false;
13361                Roo.callback(callback);
13362             },
13363             scope: this,
13364             duration: this.node.ownerTree.duration || .25
13365         });
13366     },
13367
13368     getContainer : function(){
13369         return this.ctNode;
13370     },
13371
13372     getEl : function(){
13373         return this.wrap;
13374     },
13375
13376     appendDDGhost : function(ghostNode){
13377         ghostNode.appendChild(this.elNode.cloneNode(true));
13378     },
13379
13380     getDDRepairXY : function(){
13381         return Roo.lib.Dom.getXY(this.iconNode);
13382     },
13383
13384     onRender : function(){
13385         this.render();
13386     },
13387
13388     render : function(bulkRender){
13389         var n = this.node, a = n.attributes;
13390         var targetNode = n.parentNode ?
13391               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13392
13393         if(!this.rendered){
13394             this.rendered = true;
13395
13396             this.renderElements(n, a, targetNode, bulkRender);
13397
13398             if(a.qtip){
13399                if(this.textNode.setAttributeNS){
13400                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13401                    if(a.qtipTitle){
13402                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13403                    }
13404                }else{
13405                    this.textNode.setAttribute("ext:qtip", a.qtip);
13406                    if(a.qtipTitle){
13407                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13408                    }
13409                }
13410             }else if(a.qtipCfg){
13411                 a.qtipCfg.target = Roo.id(this.textNode);
13412                 Roo.QuickTips.register(a.qtipCfg);
13413             }
13414             this.initEvents();
13415             if(!this.node.expanded){
13416                 this.updateExpandIcon();
13417             }
13418         }else{
13419             if(bulkRender === true) {
13420                 targetNode.appendChild(this.wrap);
13421             }
13422         }
13423     },
13424
13425     renderElements : function(n, a, targetNode, bulkRender)
13426     {
13427         // add some indent caching, this helps performance when rendering a large tree
13428         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13429         var t = n.getOwnerTree();
13430         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13431         if (typeof(n.attributes.html) != 'undefined') {
13432             txt = n.attributes.html;
13433         }
13434         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13435         var cb = typeof a.checked == 'boolean';
13436         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13437         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13438             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13439             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13440             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13441             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13442             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13443              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13444                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13445             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13446             "</li>"];
13447
13448         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13449             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13450                                 n.nextSibling.ui.getEl(), buf.join(""));
13451         }else{
13452             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13453         }
13454
13455         this.elNode = this.wrap.childNodes[0];
13456         this.ctNode = this.wrap.childNodes[1];
13457         var cs = this.elNode.childNodes;
13458         this.indentNode = cs[0];
13459         this.ecNode = cs[1];
13460         this.iconNode = cs[2];
13461         var index = 3;
13462         if(cb){
13463             this.checkbox = cs[3];
13464             index++;
13465         }
13466         this.anchor = cs[index];
13467         this.textNode = cs[index].firstChild;
13468     },
13469
13470     getAnchor : function(){
13471         return this.anchor;
13472     },
13473
13474     getTextEl : function(){
13475         return this.textNode;
13476     },
13477
13478     getIconEl : function(){
13479         return this.iconNode;
13480     },
13481
13482     isChecked : function(){
13483         return this.checkbox ? this.checkbox.checked : false;
13484     },
13485
13486     updateExpandIcon : function(){
13487         if(this.rendered){
13488             var n = this.node, c1, c2;
13489             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13490             var hasChild = n.hasChildNodes();
13491             if(hasChild){
13492                 if(n.expanded){
13493                     cls += "-minus";
13494                     c1 = "x-tree-node-collapsed";
13495                     c2 = "x-tree-node-expanded";
13496                 }else{
13497                     cls += "-plus";
13498                     c1 = "x-tree-node-expanded";
13499                     c2 = "x-tree-node-collapsed";
13500                 }
13501                 if(this.wasLeaf){
13502                     this.removeClass("x-tree-node-leaf");
13503                     this.wasLeaf = false;
13504                 }
13505                 if(this.c1 != c1 || this.c2 != c2){
13506                     Roo.fly(this.elNode).replaceClass(c1, c2);
13507                     this.c1 = c1; this.c2 = c2;
13508                 }
13509             }else{
13510                 // this changes non-leafs into leafs if they have no children.
13511                 // it's not very rational behaviour..
13512                 
13513                 if(!this.wasLeaf && this.node.leaf){
13514                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13515                     delete this.c1;
13516                     delete this.c2;
13517                     this.wasLeaf = true;
13518                 }
13519             }
13520             var ecc = "x-tree-ec-icon "+cls;
13521             if(this.ecc != ecc){
13522                 this.ecNode.className = ecc;
13523                 this.ecc = ecc;
13524             }
13525         }
13526     },
13527
13528     getChildIndent : function(){
13529         if(!this.childIndent){
13530             var buf = [];
13531             var p = this.node;
13532             while(p){
13533                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13534                     if(!p.isLast()) {
13535                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13536                     } else {
13537                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13538                     }
13539                 }
13540                 p = p.parentNode;
13541             }
13542             this.childIndent = buf.join("");
13543         }
13544         return this.childIndent;
13545     },
13546
13547     renderIndent : function(){
13548         if(this.rendered){
13549             var indent = "";
13550             var p = this.node.parentNode;
13551             if(p){
13552                 indent = p.ui.getChildIndent();
13553             }
13554             if(this.indentMarkup != indent){ // don't rerender if not required
13555                 this.indentNode.innerHTML = indent;
13556                 this.indentMarkup = indent;
13557             }
13558             this.updateExpandIcon();
13559         }
13560     }
13561 };
13562
13563 Roo.tree.RootTreeNodeUI = function(){
13564     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13565 };
13566 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13567     render : function(){
13568         if(!this.rendered){
13569             var targetNode = this.node.ownerTree.innerCt.dom;
13570             this.node.expanded = true;
13571             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13572             this.wrap = this.ctNode = targetNode.firstChild;
13573         }
13574     },
13575     collapse : function(){
13576     },
13577     expand : function(){
13578     }
13579 });/*
13580  * Based on:
13581  * Ext JS Library 1.1.1
13582  * Copyright(c) 2006-2007, Ext JS, LLC.
13583  *
13584  * Originally Released Under LGPL - original licence link has changed is not relivant.
13585  *
13586  * Fork - LGPL
13587  * <script type="text/javascript">
13588  */
13589 /**
13590  * @class Roo.tree.TreeLoader
13591  * @extends Roo.util.Observable
13592  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13593  * nodes from a specified URL. The response must be a javascript Array definition
13594  * who's elements are node definition objects. eg:
13595  * <pre><code>
13596 {  success : true,
13597    data :      [
13598    
13599     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13600     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13601     ]
13602 }
13603
13604
13605 </code></pre>
13606  * <br><br>
13607  * The old style respose with just an array is still supported, but not recommended.
13608  * <br><br>
13609  *
13610  * A server request is sent, and child nodes are loaded only when a node is expanded.
13611  * The loading node's id is passed to the server under the parameter name "node" to
13612  * enable the server to produce the correct child nodes.
13613  * <br><br>
13614  * To pass extra parameters, an event handler may be attached to the "beforeload"
13615  * event, and the parameters specified in the TreeLoader's baseParams property:
13616  * <pre><code>
13617     myTreeLoader.on("beforeload", function(treeLoader, node) {
13618         this.baseParams.category = node.attributes.category;
13619     }, this);
13620     
13621 </code></pre>
13622  *
13623  * This would pass an HTTP parameter called "category" to the server containing
13624  * the value of the Node's "category" attribute.
13625  * @constructor
13626  * Creates a new Treeloader.
13627  * @param {Object} config A config object containing config properties.
13628  */
13629 Roo.tree.TreeLoader = function(config){
13630     this.baseParams = {};
13631     this.requestMethod = "POST";
13632     Roo.apply(this, config);
13633
13634     this.addEvents({
13635     
13636         /**
13637          * @event beforeload
13638          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13639          * @param {Object} This TreeLoader object.
13640          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13641          * @param {Object} callback The callback function specified in the {@link #load} call.
13642          */
13643         beforeload : true,
13644         /**
13645          * @event load
13646          * Fires when the node has been successfuly loaded.
13647          * @param {Object} This TreeLoader object.
13648          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13649          * @param {Object} response The response object containing the data from the server.
13650          */
13651         load : true,
13652         /**
13653          * @event loadexception
13654          * Fires if the network request failed.
13655          * @param {Object} This TreeLoader object.
13656          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13657          * @param {Object} response The response object containing the data from the server.
13658          */
13659         loadexception : true,
13660         /**
13661          * @event create
13662          * Fires before a node is created, enabling you to return custom Node types 
13663          * @param {Object} This TreeLoader object.
13664          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13665          */
13666         create : true
13667     });
13668
13669     Roo.tree.TreeLoader.superclass.constructor.call(this);
13670 };
13671
13672 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13673     /**
13674     * @cfg {String} dataUrl The URL from which to request a Json string which
13675     * specifies an array of node definition object representing the child nodes
13676     * to be loaded.
13677     */
13678     /**
13679     * @cfg {String} requestMethod either GET or POST
13680     * defaults to POST (due to BC)
13681     * to be loaded.
13682     */
13683     /**
13684     * @cfg {Object} baseParams (optional) An object containing properties which
13685     * specify HTTP parameters to be passed to each request for child nodes.
13686     */
13687     /**
13688     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13689     * created by this loader. If the attributes sent by the server have an attribute in this object,
13690     * they take priority.
13691     */
13692     /**
13693     * @cfg {Object} uiProviders (optional) An object containing properties which
13694     * 
13695     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13696     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13697     * <i>uiProvider</i> attribute of a returned child node is a string rather
13698     * than a reference to a TreeNodeUI implementation, this that string value
13699     * is used as a property name in the uiProviders object. You can define the provider named
13700     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13701     */
13702     uiProviders : {},
13703
13704     /**
13705     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13706     * child nodes before loading.
13707     */
13708     clearOnLoad : true,
13709
13710     /**
13711     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13712     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13713     * Grid query { data : [ .....] }
13714     */
13715     
13716     root : false,
13717      /**
13718     * @cfg {String} queryParam (optional) 
13719     * Name of the query as it will be passed on the querystring (defaults to 'node')
13720     * eg. the request will be ?node=[id]
13721     */
13722     
13723     
13724     queryParam: false,
13725     
13726     /**
13727      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13728      * This is called automatically when a node is expanded, but may be used to reload
13729      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13730      * @param {Roo.tree.TreeNode} node
13731      * @param {Function} callback
13732      */
13733     load : function(node, callback){
13734         if(this.clearOnLoad){
13735             while(node.firstChild){
13736                 node.removeChild(node.firstChild);
13737             }
13738         }
13739         if(node.attributes.children){ // preloaded json children
13740             var cs = node.attributes.children;
13741             for(var i = 0, len = cs.length; i < len; i++){
13742                 node.appendChild(this.createNode(cs[i]));
13743             }
13744             if(typeof callback == "function"){
13745                 callback();
13746             }
13747         }else if(this.dataUrl){
13748             this.requestData(node, callback);
13749         }
13750     },
13751
13752     getParams: function(node){
13753         var buf = [], bp = this.baseParams;
13754         for(var key in bp){
13755             if(typeof bp[key] != "function"){
13756                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13757             }
13758         }
13759         var n = this.queryParam === false ? 'node' : this.queryParam;
13760         buf.push(n + "=", encodeURIComponent(node.id));
13761         return buf.join("");
13762     },
13763
13764     requestData : function(node, callback){
13765         if(this.fireEvent("beforeload", this, node, callback) !== false){
13766             this.transId = Roo.Ajax.request({
13767                 method:this.requestMethod,
13768                 url: this.dataUrl||this.url,
13769                 success: this.handleResponse,
13770                 failure: this.handleFailure,
13771                 scope: this,
13772                 argument: {callback: callback, node: node},
13773                 params: this.getParams(node)
13774             });
13775         }else{
13776             // if the load is cancelled, make sure we notify
13777             // the node that we are done
13778             if(typeof callback == "function"){
13779                 callback();
13780             }
13781         }
13782     },
13783
13784     isLoading : function(){
13785         return this.transId ? true : false;
13786     },
13787
13788     abort : function(){
13789         if(this.isLoading()){
13790             Roo.Ajax.abort(this.transId);
13791         }
13792     },
13793
13794     // private
13795     createNode : function(attr)
13796     {
13797         // apply baseAttrs, nice idea Corey!
13798         if(this.baseAttrs){
13799             Roo.applyIf(attr, this.baseAttrs);
13800         }
13801         if(this.applyLoader !== false){
13802             attr.loader = this;
13803         }
13804         // uiProvider = depreciated..
13805         
13806         if(typeof(attr.uiProvider) == 'string'){
13807            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13808                 /**  eval:var:attr */ eval(attr.uiProvider);
13809         }
13810         if(typeof(this.uiProviders['default']) != 'undefined') {
13811             attr.uiProvider = this.uiProviders['default'];
13812         }
13813         
13814         this.fireEvent('create', this, attr);
13815         
13816         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13817         return(attr.leaf ?
13818                         new Roo.tree.TreeNode(attr) :
13819                         new Roo.tree.AsyncTreeNode(attr));
13820     },
13821
13822     processResponse : function(response, node, callback)
13823     {
13824         var json = response.responseText;
13825         try {
13826             
13827             var o = Roo.decode(json);
13828             
13829             if (this.root === false && typeof(o.success) != undefined) {
13830                 this.root = 'data'; // the default behaviour for list like data..
13831                 }
13832                 
13833             if (this.root !== false &&  !o.success) {
13834                 // it's a failure condition.
13835                 var a = response.argument;
13836                 this.fireEvent("loadexception", this, a.node, response);
13837                 Roo.log("Load failed - should have a handler really");
13838                 return;
13839             }
13840             
13841             
13842             
13843             if (this.root !== false) {
13844                  o = o[this.root];
13845             }
13846             
13847             for(var i = 0, len = o.length; i < len; i++){
13848                 var n = this.createNode(o[i]);
13849                 if(n){
13850                     node.appendChild(n);
13851                 }
13852             }
13853             if(typeof callback == "function"){
13854                 callback(this, node);
13855             }
13856         }catch(e){
13857             this.handleFailure(response);
13858         }
13859     },
13860
13861     handleResponse : function(response){
13862         this.transId = false;
13863         var a = response.argument;
13864         this.processResponse(response, a.node, a.callback);
13865         this.fireEvent("load", this, a.node, response);
13866     },
13867
13868     handleFailure : function(response)
13869     {
13870         // should handle failure better..
13871         this.transId = false;
13872         var a = response.argument;
13873         this.fireEvent("loadexception", this, a.node, response);
13874         if(typeof a.callback == "function"){
13875             a.callback(this, a.node);
13876         }
13877     }
13878 });/*
13879  * Based on:
13880  * Ext JS Library 1.1.1
13881  * Copyright(c) 2006-2007, Ext JS, LLC.
13882  *
13883  * Originally Released Under LGPL - original licence link has changed is not relivant.
13884  *
13885  * Fork - LGPL
13886  * <script type="text/javascript">
13887  */
13888
13889 /**
13890 * @class Roo.tree.TreeFilter
13891 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13892 * @param {TreePanel} tree
13893 * @param {Object} config (optional)
13894  */
13895 Roo.tree.TreeFilter = function(tree, config){
13896     this.tree = tree;
13897     this.filtered = {};
13898     Roo.apply(this, config);
13899 };
13900
13901 Roo.tree.TreeFilter.prototype = {
13902     clearBlank:false,
13903     reverse:false,
13904     autoClear:false,
13905     remove:false,
13906
13907      /**
13908      * Filter the data by a specific attribute.
13909      * @param {String/RegExp} value Either string that the attribute value
13910      * should start with or a RegExp to test against the attribute
13911      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13912      * @param {TreeNode} startNode (optional) The node to start the filter at.
13913      */
13914     filter : function(value, attr, startNode){
13915         attr = attr || "text";
13916         var f;
13917         if(typeof value == "string"){
13918             var vlen = value.length;
13919             // auto clear empty filter
13920             if(vlen == 0 && this.clearBlank){
13921                 this.clear();
13922                 return;
13923             }
13924             value = value.toLowerCase();
13925             f = function(n){
13926                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13927             };
13928         }else if(value.exec){ // regex?
13929             f = function(n){
13930                 return value.test(n.attributes[attr]);
13931             };
13932         }else{
13933             throw 'Illegal filter type, must be string or regex';
13934         }
13935         this.filterBy(f, null, startNode);
13936         },
13937
13938     /**
13939      * Filter by a function. The passed function will be called with each
13940      * node in the tree (or from the startNode). If the function returns true, the node is kept
13941      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13942      * @param {Function} fn The filter function
13943      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13944      */
13945     filterBy : function(fn, scope, startNode){
13946         startNode = startNode || this.tree.root;
13947         if(this.autoClear){
13948             this.clear();
13949         }
13950         var af = this.filtered, rv = this.reverse;
13951         var f = function(n){
13952             if(n == startNode){
13953                 return true;
13954             }
13955             if(af[n.id]){
13956                 return false;
13957             }
13958             var m = fn.call(scope || n, n);
13959             if(!m || rv){
13960                 af[n.id] = n;
13961                 n.ui.hide();
13962                 return false;
13963             }
13964             return true;
13965         };
13966         startNode.cascade(f);
13967         if(this.remove){
13968            for(var id in af){
13969                if(typeof id != "function"){
13970                    var n = af[id];
13971                    if(n && n.parentNode){
13972                        n.parentNode.removeChild(n);
13973                    }
13974                }
13975            }
13976         }
13977     },
13978
13979     /**
13980      * Clears the current filter. Note: with the "remove" option
13981      * set a filter cannot be cleared.
13982      */
13983     clear : function(){
13984         var t = this.tree;
13985         var af = this.filtered;
13986         for(var id in af){
13987             if(typeof id != "function"){
13988                 var n = af[id];
13989                 if(n){
13990                     n.ui.show();
13991                 }
13992             }
13993         }
13994         this.filtered = {};
13995     }
13996 };
13997 /*
13998  * Based on:
13999  * Ext JS Library 1.1.1
14000  * Copyright(c) 2006-2007, Ext JS, LLC.
14001  *
14002  * Originally Released Under LGPL - original licence link has changed is not relivant.
14003  *
14004  * Fork - LGPL
14005  * <script type="text/javascript">
14006  */
14007  
14008
14009 /**
14010  * @class Roo.tree.TreeSorter
14011  * Provides sorting of nodes in a TreePanel
14012  * 
14013  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
14014  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
14015  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
14016  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
14017  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
14018  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
14019  * @constructor
14020  * @param {TreePanel} tree
14021  * @param {Object} config
14022  */
14023 Roo.tree.TreeSorter = function(tree, config){
14024     Roo.apply(this, config);
14025     tree.on("beforechildrenrendered", this.doSort, this);
14026     tree.on("append", this.updateSort, this);
14027     tree.on("insert", this.updateSort, this);
14028     
14029     var dsc = this.dir && this.dir.toLowerCase() == "desc";
14030     var p = this.property || "text";
14031     var sortType = this.sortType;
14032     var fs = this.folderSort;
14033     var cs = this.caseSensitive === true;
14034     var leafAttr = this.leafAttr || 'leaf';
14035
14036     this.sortFn = function(n1, n2){
14037         if(fs){
14038             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14039                 return 1;
14040             }
14041             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14042                 return -1;
14043             }
14044         }
14045         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14046         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14047         if(v1 < v2){
14048                         return dsc ? +1 : -1;
14049                 }else if(v1 > v2){
14050                         return dsc ? -1 : +1;
14051         }else{
14052                 return 0;
14053         }
14054     };
14055 };
14056
14057 Roo.tree.TreeSorter.prototype = {
14058     doSort : function(node){
14059         node.sort(this.sortFn);
14060     },
14061     
14062     compareNodes : function(n1, n2){
14063         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14064     },
14065     
14066     updateSort : function(tree, node){
14067         if(node.childrenRendered){
14068             this.doSort.defer(1, this, [node]);
14069         }
14070     }
14071 };/*
14072  * Based on:
14073  * Ext JS Library 1.1.1
14074  * Copyright(c) 2006-2007, Ext JS, LLC.
14075  *
14076  * Originally Released Under LGPL - original licence link has changed is not relivant.
14077  *
14078  * Fork - LGPL
14079  * <script type="text/javascript">
14080  */
14081
14082 if(Roo.dd.DropZone){
14083     
14084 Roo.tree.TreeDropZone = function(tree, config){
14085     this.allowParentInsert = false;
14086     this.allowContainerDrop = false;
14087     this.appendOnly = false;
14088     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14089     this.tree = tree;
14090     this.lastInsertClass = "x-tree-no-status";
14091     this.dragOverData = {};
14092 };
14093
14094 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14095     ddGroup : "TreeDD",
14096     scroll:  true,
14097     
14098     expandDelay : 1000,
14099     
14100     expandNode : function(node){
14101         if(node.hasChildNodes() && !node.isExpanded()){
14102             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14103         }
14104     },
14105     
14106     queueExpand : function(node){
14107         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14108     },
14109     
14110     cancelExpand : function(){
14111         if(this.expandProcId){
14112             clearTimeout(this.expandProcId);
14113             this.expandProcId = false;
14114         }
14115     },
14116     
14117     isValidDropPoint : function(n, pt, dd, e, data){
14118         if(!n || !data){ return false; }
14119         var targetNode = n.node;
14120         var dropNode = data.node;
14121         // default drop rules
14122         if(!(targetNode && targetNode.isTarget && pt)){
14123             return false;
14124         }
14125         if(pt == "append" && targetNode.allowChildren === false){
14126             return false;
14127         }
14128         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14129             return false;
14130         }
14131         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14132             return false;
14133         }
14134         // reuse the object
14135         var overEvent = this.dragOverData;
14136         overEvent.tree = this.tree;
14137         overEvent.target = targetNode;
14138         overEvent.data = data;
14139         overEvent.point = pt;
14140         overEvent.source = dd;
14141         overEvent.rawEvent = e;
14142         overEvent.dropNode = dropNode;
14143         overEvent.cancel = false;  
14144         var result = this.tree.fireEvent("nodedragover", overEvent);
14145         return overEvent.cancel === false && result !== false;
14146     },
14147     
14148     getDropPoint : function(e, n, dd)
14149     {
14150         var tn = n.node;
14151         if(tn.isRoot){
14152             return tn.allowChildren !== false ? "append" : false; // always append for root
14153         }
14154         var dragEl = n.ddel;
14155         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14156         var y = Roo.lib.Event.getPageY(e);
14157         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14158         
14159         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14160         var noAppend = tn.allowChildren === false;
14161         if(this.appendOnly || tn.parentNode.allowChildren === false){
14162             return noAppend ? false : "append";
14163         }
14164         var noBelow = false;
14165         if(!this.allowParentInsert){
14166             noBelow = tn.hasChildNodes() && tn.isExpanded();
14167         }
14168         var q = (b - t) / (noAppend ? 2 : 3);
14169         if(y >= t && y < (t + q)){
14170             return "above";
14171         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14172             return "below";
14173         }else{
14174             return "append";
14175         }
14176     },
14177     
14178     onNodeEnter : function(n, dd, e, data)
14179     {
14180         this.cancelExpand();
14181     },
14182     
14183     onNodeOver : function(n, dd, e, data)
14184     {
14185        
14186         var pt = this.getDropPoint(e, n, dd);
14187         var node = n.node;
14188         
14189         // auto node expand check
14190         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14191             this.queueExpand(node);
14192         }else if(pt != "append"){
14193             this.cancelExpand();
14194         }
14195         
14196         // set the insert point style on the target node
14197         var returnCls = this.dropNotAllowed;
14198         if(this.isValidDropPoint(n, pt, dd, e, data)){
14199            if(pt){
14200                var el = n.ddel;
14201                var cls;
14202                if(pt == "above"){
14203                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14204                    cls = "x-tree-drag-insert-above";
14205                }else if(pt == "below"){
14206                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14207                    cls = "x-tree-drag-insert-below";
14208                }else{
14209                    returnCls = "x-tree-drop-ok-append";
14210                    cls = "x-tree-drag-append";
14211                }
14212                if(this.lastInsertClass != cls){
14213                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14214                    this.lastInsertClass = cls;
14215                }
14216            }
14217        }
14218        return returnCls;
14219     },
14220     
14221     onNodeOut : function(n, dd, e, data){
14222         
14223         this.cancelExpand();
14224         this.removeDropIndicators(n);
14225     },
14226     
14227     onNodeDrop : function(n, dd, e, data){
14228         var point = this.getDropPoint(e, n, dd);
14229         var targetNode = n.node;
14230         targetNode.ui.startDrop();
14231         if(!this.isValidDropPoint(n, point, dd, e, data)){
14232             targetNode.ui.endDrop();
14233             return false;
14234         }
14235         // first try to find the drop node
14236         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14237         var dropEvent = {
14238             tree : this.tree,
14239             target: targetNode,
14240             data: data,
14241             point: point,
14242             source: dd,
14243             rawEvent: e,
14244             dropNode: dropNode,
14245             cancel: !dropNode   
14246         };
14247         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14248         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14249             targetNode.ui.endDrop();
14250             return false;
14251         }
14252         // allow target changing
14253         targetNode = dropEvent.target;
14254         if(point == "append" && !targetNode.isExpanded()){
14255             targetNode.expand(false, null, function(){
14256                 this.completeDrop(dropEvent);
14257             }.createDelegate(this));
14258         }else{
14259             this.completeDrop(dropEvent);
14260         }
14261         return true;
14262     },
14263     
14264     completeDrop : function(de){
14265         var ns = de.dropNode, p = de.point, t = de.target;
14266         if(!(ns instanceof Array)){
14267             ns = [ns];
14268         }
14269         var n;
14270         for(var i = 0, len = ns.length; i < len; i++){
14271             n = ns[i];
14272             if(p == "above"){
14273                 t.parentNode.insertBefore(n, t);
14274             }else if(p == "below"){
14275                 t.parentNode.insertBefore(n, t.nextSibling);
14276             }else{
14277                 t.appendChild(n);
14278             }
14279         }
14280         n.ui.focus();
14281         if(this.tree.hlDrop){
14282             n.ui.highlight();
14283         }
14284         t.ui.endDrop();
14285         this.tree.fireEvent("nodedrop", de);
14286     },
14287     
14288     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14289         if(this.tree.hlDrop){
14290             dropNode.ui.focus();
14291             dropNode.ui.highlight();
14292         }
14293         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14294     },
14295     
14296     getTree : function(){
14297         return this.tree;
14298     },
14299     
14300     removeDropIndicators : function(n){
14301         if(n && n.ddel){
14302             var el = n.ddel;
14303             Roo.fly(el).removeClass([
14304                     "x-tree-drag-insert-above",
14305                     "x-tree-drag-insert-below",
14306                     "x-tree-drag-append"]);
14307             this.lastInsertClass = "_noclass";
14308         }
14309     },
14310     
14311     beforeDragDrop : function(target, e, id){
14312         this.cancelExpand();
14313         return true;
14314     },
14315     
14316     afterRepair : function(data){
14317         if(data && Roo.enableFx){
14318             data.node.ui.highlight();
14319         }
14320         this.hideProxy();
14321     } 
14322     
14323 });
14324
14325 }
14326 /*
14327  * Based on:
14328  * Ext JS Library 1.1.1
14329  * Copyright(c) 2006-2007, Ext JS, LLC.
14330  *
14331  * Originally Released Under LGPL - original licence link has changed is not relivant.
14332  *
14333  * Fork - LGPL
14334  * <script type="text/javascript">
14335  */
14336  
14337
14338 if(Roo.dd.DragZone){
14339 Roo.tree.TreeDragZone = function(tree, config){
14340     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14341     this.tree = tree;
14342 };
14343
14344 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14345     ddGroup : "TreeDD",
14346    
14347     onBeforeDrag : function(data, e){
14348         var n = data.node;
14349         return n && n.draggable && !n.disabled;
14350     },
14351      
14352     
14353     onInitDrag : function(e){
14354         var data = this.dragData;
14355         this.tree.getSelectionModel().select(data.node);
14356         this.proxy.update("");
14357         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14358         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14359     },
14360     
14361     getRepairXY : function(e, data){
14362         return data.node.ui.getDDRepairXY();
14363     },
14364     
14365     onEndDrag : function(data, e){
14366         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14367         
14368         
14369     },
14370     
14371     onValidDrop : function(dd, e, id){
14372         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14373         this.hideProxy();
14374     },
14375     
14376     beforeInvalidDrop : function(e, id){
14377         // this scrolls the original position back into view
14378         var sm = this.tree.getSelectionModel();
14379         sm.clearSelections();
14380         sm.select(this.dragData.node);
14381     }
14382 });
14383 }/*
14384  * Based on:
14385  * Ext JS Library 1.1.1
14386  * Copyright(c) 2006-2007, Ext JS, LLC.
14387  *
14388  * Originally Released Under LGPL - original licence link has changed is not relivant.
14389  *
14390  * Fork - LGPL
14391  * <script type="text/javascript">
14392  */
14393 /**
14394  * @class Roo.tree.TreeEditor
14395  * @extends Roo.Editor
14396  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14397  * as the editor field.
14398  * @constructor
14399  * @param {Object} config (used to be the tree panel.)
14400  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14401  * 
14402  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14403  * @cfg {Roo.form.TextField|Object} field The field configuration
14404  *
14405  * 
14406  */
14407 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14408     var tree = config;
14409     var field;
14410     if (oldconfig) { // old style..
14411         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14412     } else {
14413         // new style..
14414         tree = config.tree;
14415         config.field = config.field  || {};
14416         config.field.xtype = 'TextField';
14417         field = Roo.factory(config.field, Roo.form);
14418     }
14419     config = config || {};
14420     
14421     
14422     this.addEvents({
14423         /**
14424          * @event beforenodeedit
14425          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14426          * false from the handler of this event.
14427          * @param {Editor} this
14428          * @param {Roo.tree.Node} node 
14429          */
14430         "beforenodeedit" : true
14431     });
14432     
14433     //Roo.log(config);
14434     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14435
14436     this.tree = tree;
14437
14438     tree.on('beforeclick', this.beforeNodeClick, this);
14439     tree.getTreeEl().on('mousedown', this.hide, this);
14440     this.on('complete', this.updateNode, this);
14441     this.on('beforestartedit', this.fitToTree, this);
14442     this.on('startedit', this.bindScroll, this, {delay:10});
14443     this.on('specialkey', this.onSpecialKey, this);
14444 };
14445
14446 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14447     /**
14448      * @cfg {String} alignment
14449      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14450      */
14451     alignment: "l-l",
14452     // inherit
14453     autoSize: false,
14454     /**
14455      * @cfg {Boolean} hideEl
14456      * True to hide the bound element while the editor is displayed (defaults to false)
14457      */
14458     hideEl : false,
14459     /**
14460      * @cfg {String} cls
14461      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14462      */
14463     cls: "x-small-editor x-tree-editor",
14464     /**
14465      * @cfg {Boolean} shim
14466      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14467      */
14468     shim:false,
14469     // inherit
14470     shadow:"frame",
14471     /**
14472      * @cfg {Number} maxWidth
14473      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14474      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14475      * scroll and client offsets into account prior to each edit.
14476      */
14477     maxWidth: 250,
14478
14479     editDelay : 350,
14480
14481     // private
14482     fitToTree : function(ed, el){
14483         var td = this.tree.getTreeEl().dom, nd = el.dom;
14484         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14485             td.scrollLeft = nd.offsetLeft;
14486         }
14487         var w = Math.min(
14488                 this.maxWidth,
14489                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14490         this.setSize(w, '');
14491         
14492         return this.fireEvent('beforenodeedit', this, this.editNode);
14493         
14494     },
14495
14496     // private
14497     triggerEdit : function(node){
14498         this.completeEdit();
14499         this.editNode = node;
14500         this.startEdit(node.ui.textNode, node.text);
14501     },
14502
14503     // private
14504     bindScroll : function(){
14505         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14506     },
14507
14508     // private
14509     beforeNodeClick : function(node, e){
14510         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14511         this.lastClick = new Date();
14512         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14513             e.stopEvent();
14514             this.triggerEdit(node);
14515             return false;
14516         }
14517         return true;
14518     },
14519
14520     // private
14521     updateNode : function(ed, value){
14522         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14523         this.editNode.setText(value);
14524     },
14525
14526     // private
14527     onHide : function(){
14528         Roo.tree.TreeEditor.superclass.onHide.call(this);
14529         if(this.editNode){
14530             this.editNode.ui.focus();
14531         }
14532     },
14533
14534     // private
14535     onSpecialKey : function(field, e){
14536         var k = e.getKey();
14537         if(k == e.ESC){
14538             e.stopEvent();
14539             this.cancelEdit();
14540         }else if(k == e.ENTER && !e.hasModifier()){
14541             e.stopEvent();
14542             this.completeEdit();
14543         }
14544     }
14545 });//<Script type="text/javascript">
14546 /*
14547  * Based on:
14548  * Ext JS Library 1.1.1
14549  * Copyright(c) 2006-2007, Ext JS, LLC.
14550  *
14551  * Originally Released Under LGPL - original licence link has changed is not relivant.
14552  *
14553  * Fork - LGPL
14554  * <script type="text/javascript">
14555  */
14556  
14557 /**
14558  * Not documented??? - probably should be...
14559  */
14560
14561 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14562     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14563     
14564     renderElements : function(n, a, targetNode, bulkRender){
14565         //consel.log("renderElements?");
14566         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14567
14568         var t = n.getOwnerTree();
14569         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14570         
14571         var cols = t.columns;
14572         var bw = t.borderWidth;
14573         var c = cols[0];
14574         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14575          var cb = typeof a.checked == "boolean";
14576         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14577         var colcls = 'x-t-' + tid + '-c0';
14578         var buf = [
14579             '<li class="x-tree-node">',
14580             
14581                 
14582                 '<div class="x-tree-node-el ', a.cls,'">',
14583                     // extran...
14584                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14585                 
14586                 
14587                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14588                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14589                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14590                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14591                            (a.iconCls ? ' '+a.iconCls : ''),
14592                            '" unselectable="on" />',
14593                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14594                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14595                              
14596                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14597                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14598                             '<span unselectable="on" qtip="' + tx + '">',
14599                              tx,
14600                              '</span></a>' ,
14601                     '</div>',
14602                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14603                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14604                  ];
14605         for(var i = 1, len = cols.length; i < len; i++){
14606             c = cols[i];
14607             colcls = 'x-t-' + tid + '-c' +i;
14608             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14609             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14610                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14611                       "</div>");
14612          }
14613          
14614          buf.push(
14615             '</a>',
14616             '<div class="x-clear"></div></div>',
14617             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14618             "</li>");
14619         
14620         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14621             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14622                                 n.nextSibling.ui.getEl(), buf.join(""));
14623         }else{
14624             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14625         }
14626         var el = this.wrap.firstChild;
14627         this.elRow = el;
14628         this.elNode = el.firstChild;
14629         this.ranchor = el.childNodes[1];
14630         this.ctNode = this.wrap.childNodes[1];
14631         var cs = el.firstChild.childNodes;
14632         this.indentNode = cs[0];
14633         this.ecNode = cs[1];
14634         this.iconNode = cs[2];
14635         var index = 3;
14636         if(cb){
14637             this.checkbox = cs[3];
14638             index++;
14639         }
14640         this.anchor = cs[index];
14641         
14642         this.textNode = cs[index].firstChild;
14643         
14644         //el.on("click", this.onClick, this);
14645         //el.on("dblclick", this.onDblClick, this);
14646         
14647         
14648        // console.log(this);
14649     },
14650     initEvents : function(){
14651         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14652         
14653             
14654         var a = this.ranchor;
14655
14656         var el = Roo.get(a);
14657
14658         if(Roo.isOpera){ // opera render bug ignores the CSS
14659             el.setStyle("text-decoration", "none");
14660         }
14661
14662         el.on("click", this.onClick, this);
14663         el.on("dblclick", this.onDblClick, this);
14664         el.on("contextmenu", this.onContextMenu, this);
14665         
14666     },
14667     
14668     /*onSelectedChange : function(state){
14669         if(state){
14670             this.focus();
14671             this.addClass("x-tree-selected");
14672         }else{
14673             //this.blur();
14674             this.removeClass("x-tree-selected");
14675         }
14676     },*/
14677     addClass : function(cls){
14678         if(this.elRow){
14679             Roo.fly(this.elRow).addClass(cls);
14680         }
14681         
14682     },
14683     
14684     
14685     removeClass : function(cls){
14686         if(this.elRow){
14687             Roo.fly(this.elRow).removeClass(cls);
14688         }
14689     }
14690
14691     
14692     
14693 });//<Script type="text/javascript">
14694
14695 /*
14696  * Based on:
14697  * Ext JS Library 1.1.1
14698  * Copyright(c) 2006-2007, Ext JS, LLC.
14699  *
14700  * Originally Released Under LGPL - original licence link has changed is not relivant.
14701  *
14702  * Fork - LGPL
14703  * <script type="text/javascript">
14704  */
14705  
14706
14707 /**
14708  * @class Roo.tree.ColumnTree
14709  * @extends Roo.data.TreePanel
14710  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14711  * @cfg {int} borderWidth  compined right/left border allowance
14712  * @constructor
14713  * @param {String/HTMLElement/Element} el The container element
14714  * @param {Object} config
14715  */
14716 Roo.tree.ColumnTree =  function(el, config)
14717 {
14718    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14719    this.addEvents({
14720         /**
14721         * @event resize
14722         * Fire this event on a container when it resizes
14723         * @param {int} w Width
14724         * @param {int} h Height
14725         */
14726        "resize" : true
14727     });
14728     this.on('resize', this.onResize, this);
14729 };
14730
14731 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14732     //lines:false,
14733     
14734     
14735     borderWidth: Roo.isBorderBox ? 0 : 2, 
14736     headEls : false,
14737     
14738     render : function(){
14739         // add the header.....
14740        
14741         Roo.tree.ColumnTree.superclass.render.apply(this);
14742         
14743         this.el.addClass('x-column-tree');
14744         
14745         this.headers = this.el.createChild(
14746             {cls:'x-tree-headers'},this.innerCt.dom);
14747    
14748         var cols = this.columns, c;
14749         var totalWidth = 0;
14750         this.headEls = [];
14751         var  len = cols.length;
14752         for(var i = 0; i < len; i++){
14753              c = cols[i];
14754              totalWidth += c.width;
14755             this.headEls.push(this.headers.createChild({
14756                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14757                  cn: {
14758                      cls:'x-tree-hd-text',
14759                      html: c.header
14760                  },
14761                  style:'width:'+(c.width-this.borderWidth)+'px;'
14762              }));
14763         }
14764         this.headers.createChild({cls:'x-clear'});
14765         // prevent floats from wrapping when clipped
14766         this.headers.setWidth(totalWidth);
14767         //this.innerCt.setWidth(totalWidth);
14768         this.innerCt.setStyle({ overflow: 'auto' });
14769         this.onResize(this.width, this.height);
14770              
14771         
14772     },
14773     onResize : function(w,h)
14774     {
14775         this.height = h;
14776         this.width = w;
14777         // resize cols..
14778         this.innerCt.setWidth(this.width);
14779         this.innerCt.setHeight(this.height-20);
14780         
14781         // headers...
14782         var cols = this.columns, c;
14783         var totalWidth = 0;
14784         var expEl = false;
14785         var len = cols.length;
14786         for(var i = 0; i < len; i++){
14787             c = cols[i];
14788             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14789                 // it's the expander..
14790                 expEl  = this.headEls[i];
14791                 continue;
14792             }
14793             totalWidth += c.width;
14794             
14795         }
14796         if (expEl) {
14797             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14798         }
14799         this.headers.setWidth(w-20);
14800
14801         
14802         
14803         
14804     }
14805 });
14806 /*
14807  * Based on:
14808  * Ext JS Library 1.1.1
14809  * Copyright(c) 2006-2007, Ext JS, LLC.
14810  *
14811  * Originally Released Under LGPL - original licence link has changed is not relivant.
14812  *
14813  * Fork - LGPL
14814  * <script type="text/javascript">
14815  */
14816  
14817 /**
14818  * @class Roo.menu.Menu
14819  * @extends Roo.util.Observable
14820  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14821  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14822  * @constructor
14823  * Creates a new Menu
14824  * @param {Object} config Configuration options
14825  */
14826 Roo.menu.Menu = function(config){
14827     
14828     Roo.menu.Menu.superclass.constructor.call(this, config);
14829     
14830     this.id = this.id || Roo.id();
14831     this.addEvents({
14832         /**
14833          * @event beforeshow
14834          * Fires before this menu is displayed
14835          * @param {Roo.menu.Menu} this
14836          */
14837         beforeshow : true,
14838         /**
14839          * @event beforehide
14840          * Fires before this menu is hidden
14841          * @param {Roo.menu.Menu} this
14842          */
14843         beforehide : true,
14844         /**
14845          * @event show
14846          * Fires after this menu is displayed
14847          * @param {Roo.menu.Menu} this
14848          */
14849         show : true,
14850         /**
14851          * @event hide
14852          * Fires after this menu is hidden
14853          * @param {Roo.menu.Menu} this
14854          */
14855         hide : true,
14856         /**
14857          * @event click
14858          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14859          * @param {Roo.menu.Menu} this
14860          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14861          * @param {Roo.EventObject} e
14862          */
14863         click : true,
14864         /**
14865          * @event mouseover
14866          * Fires when the mouse is hovering over this menu
14867          * @param {Roo.menu.Menu} this
14868          * @param {Roo.EventObject} e
14869          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14870          */
14871         mouseover : true,
14872         /**
14873          * @event mouseout
14874          * Fires when the mouse exits this menu
14875          * @param {Roo.menu.Menu} this
14876          * @param {Roo.EventObject} e
14877          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14878          */
14879         mouseout : true,
14880         /**
14881          * @event itemclick
14882          * Fires when a menu item contained in this menu is clicked
14883          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14884          * @param {Roo.EventObject} e
14885          */
14886         itemclick: true
14887     });
14888     if (this.registerMenu) {
14889         Roo.menu.MenuMgr.register(this);
14890     }
14891     
14892     var mis = this.items;
14893     this.items = new Roo.util.MixedCollection();
14894     if(mis){
14895         this.add.apply(this, mis);
14896     }
14897 };
14898
14899 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14900     /**
14901      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14902      */
14903     minWidth : 120,
14904     /**
14905      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14906      * for bottom-right shadow (defaults to "sides")
14907      */
14908     shadow : "sides",
14909     /**
14910      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14911      * this menu (defaults to "tl-tr?")
14912      */
14913     subMenuAlign : "tl-tr?",
14914     /**
14915      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14916      * relative to its element of origin (defaults to "tl-bl?")
14917      */
14918     defaultAlign : "tl-bl?",
14919     /**
14920      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14921      */
14922     allowOtherMenus : false,
14923     /**
14924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14925      */
14926     registerMenu : true,
14927
14928     hidden:true,
14929
14930     // private
14931     render : function(){
14932         if(this.el){
14933             return;
14934         }
14935         var el = this.el = new Roo.Layer({
14936             cls: "x-menu",
14937             shadow:this.shadow,
14938             constrain: false,
14939             parentEl: this.parentEl || document.body,
14940             zindex:15000
14941         });
14942
14943         this.keyNav = new Roo.menu.MenuNav(this);
14944
14945         if(this.plain){
14946             el.addClass("x-menu-plain");
14947         }
14948         if(this.cls){
14949             el.addClass(this.cls);
14950         }
14951         // generic focus element
14952         this.focusEl = el.createChild({
14953             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14954         });
14955         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14956         //disabling touch- as it's causing issues ..
14957         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14958         ul.on('click'   , this.onClick, this);
14959         
14960         
14961         ul.on("mouseover", this.onMouseOver, this);
14962         ul.on("mouseout", this.onMouseOut, this);
14963         this.items.each(function(item){
14964             if (item.hidden) {
14965                 return;
14966             }
14967             
14968             var li = document.createElement("li");
14969             li.className = "x-menu-list-item";
14970             ul.dom.appendChild(li);
14971             item.render(li, this);
14972         }, this);
14973         this.ul = ul;
14974         this.autoWidth();
14975     },
14976
14977     // private
14978     autoWidth : function(){
14979         var el = this.el, ul = this.ul;
14980         if(!el){
14981             return;
14982         }
14983         var w = this.width;
14984         if(w){
14985             el.setWidth(w);
14986         }else if(Roo.isIE){
14987             el.setWidth(this.minWidth);
14988             var t = el.dom.offsetWidth; // force recalc
14989             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14990         }
14991     },
14992
14993     // private
14994     delayAutoWidth : function(){
14995         if(this.rendered){
14996             if(!this.awTask){
14997                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14998             }
14999             this.awTask.delay(20);
15000         }
15001     },
15002
15003     // private
15004     findTargetItem : function(e){
15005         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
15006         if(t && t.menuItemId){
15007             return this.items.get(t.menuItemId);
15008         }
15009     },
15010
15011     // private
15012     onClick : function(e){
15013         Roo.log("menu.onClick");
15014         var t = this.findTargetItem(e);
15015         if(!t){
15016             return;
15017         }
15018         Roo.log(e);
15019         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
15020             if(t == this.activeItem && t.shouldDeactivate(e)){
15021                 this.activeItem.deactivate();
15022                 delete this.activeItem;
15023                 return;
15024             }
15025             if(t.canActivate){
15026                 this.setActiveItem(t, true);
15027             }
15028             return;
15029             
15030             
15031         }
15032         
15033         t.onClick(e);
15034         this.fireEvent("click", this, t, e);
15035     },
15036
15037     // private
15038     setActiveItem : function(item, autoExpand){
15039         if(item != this.activeItem){
15040             if(this.activeItem){
15041                 this.activeItem.deactivate();
15042             }
15043             this.activeItem = item;
15044             item.activate(autoExpand);
15045         }else if(autoExpand){
15046             item.expandMenu();
15047         }
15048     },
15049
15050     // private
15051     tryActivate : function(start, step){
15052         var items = this.items;
15053         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15054             var item = items.get(i);
15055             if(!item.disabled && item.canActivate){
15056                 this.setActiveItem(item, false);
15057                 return item;
15058             }
15059         }
15060         return false;
15061     },
15062
15063     // private
15064     onMouseOver : function(e){
15065         var t;
15066         if(t = this.findTargetItem(e)){
15067             if(t.canActivate && !t.disabled){
15068                 this.setActiveItem(t, true);
15069             }
15070         }
15071         this.fireEvent("mouseover", this, e, t);
15072     },
15073
15074     // private
15075     onMouseOut : function(e){
15076         var t;
15077         if(t = this.findTargetItem(e)){
15078             if(t == this.activeItem && t.shouldDeactivate(e)){
15079                 this.activeItem.deactivate();
15080                 delete this.activeItem;
15081             }
15082         }
15083         this.fireEvent("mouseout", this, e, t);
15084     },
15085
15086     /**
15087      * Read-only.  Returns true if the menu is currently displayed, else false.
15088      * @type Boolean
15089      */
15090     isVisible : function(){
15091         return this.el && !this.hidden;
15092     },
15093
15094     /**
15095      * Displays this menu relative to another element
15096      * @param {String/HTMLElement/Roo.Element} element The element to align to
15097      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15098      * the element (defaults to this.defaultAlign)
15099      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15100      */
15101     show : function(el, pos, parentMenu){
15102         this.parentMenu = parentMenu;
15103         if(!this.el){
15104             this.render();
15105         }
15106         this.fireEvent("beforeshow", this);
15107         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15108     },
15109
15110     /**
15111      * Displays this menu at a specific xy position
15112      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15113      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15114      */
15115     showAt : function(xy, parentMenu, /* private: */_e){
15116         this.parentMenu = parentMenu;
15117         if(!this.el){
15118             this.render();
15119         }
15120         if(_e !== false){
15121             this.fireEvent("beforeshow", this);
15122             xy = this.el.adjustForConstraints(xy);
15123         }
15124         this.el.setXY(xy);
15125         this.el.show();
15126         this.hidden = false;
15127         this.focus();
15128         this.fireEvent("show", this);
15129     },
15130
15131     focus : function(){
15132         if(!this.hidden){
15133             this.doFocus.defer(50, this);
15134         }
15135     },
15136
15137     doFocus : function(){
15138         if(!this.hidden){
15139             this.focusEl.focus();
15140         }
15141     },
15142
15143     /**
15144      * Hides this menu and optionally all parent menus
15145      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15146      */
15147     hide : function(deep){
15148         if(this.el && this.isVisible()){
15149             this.fireEvent("beforehide", this);
15150             if(this.activeItem){
15151                 this.activeItem.deactivate();
15152                 this.activeItem = null;
15153             }
15154             this.el.hide();
15155             this.hidden = true;
15156             this.fireEvent("hide", this);
15157         }
15158         if(deep === true && this.parentMenu){
15159             this.parentMenu.hide(true);
15160         }
15161     },
15162
15163     /**
15164      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15165      * Any of the following are valid:
15166      * <ul>
15167      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15168      * <li>An HTMLElement object which will be converted to a menu item</li>
15169      * <li>A menu item config object that will be created as a new menu item</li>
15170      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15171      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15172      * </ul>
15173      * Usage:
15174      * <pre><code>
15175 // Create the menu
15176 var menu = new Roo.menu.Menu();
15177
15178 // Create a menu item to add by reference
15179 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15180
15181 // Add a bunch of items at once using different methods.
15182 // Only the last item added will be returned.
15183 var item = menu.add(
15184     menuItem,                // add existing item by ref
15185     'Dynamic Item',          // new TextItem
15186     '-',                     // new separator
15187     { text: 'Config Item' }  // new item by config
15188 );
15189 </code></pre>
15190      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15191      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15192      */
15193     add : function(){
15194         var a = arguments, l = a.length, item;
15195         for(var i = 0; i < l; i++){
15196             var el = a[i];
15197             if ((typeof(el) == "object") && el.xtype && el.xns) {
15198                 el = Roo.factory(el, Roo.menu);
15199             }
15200             
15201             if(el.render){ // some kind of Item
15202                 item = this.addItem(el);
15203             }else if(typeof el == "string"){ // string
15204                 if(el == "separator" || el == "-"){
15205                     item = this.addSeparator();
15206                 }else{
15207                     item = this.addText(el);
15208                 }
15209             }else if(el.tagName || el.el){ // element
15210                 item = this.addElement(el);
15211             }else if(typeof el == "object"){ // must be menu item config?
15212                 item = this.addMenuItem(el);
15213             }
15214         }
15215         return item;
15216     },
15217
15218     /**
15219      * Returns this menu's underlying {@link Roo.Element} object
15220      * @return {Roo.Element} The element
15221      */
15222     getEl : function(){
15223         if(!this.el){
15224             this.render();
15225         }
15226         return this.el;
15227     },
15228
15229     /**
15230      * Adds a separator bar to the menu
15231      * @return {Roo.menu.Item} The menu item that was added
15232      */
15233     addSeparator : function(){
15234         return this.addItem(new Roo.menu.Separator());
15235     },
15236
15237     /**
15238      * Adds an {@link Roo.Element} object to the menu
15239      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15240      * @return {Roo.menu.Item} The menu item that was added
15241      */
15242     addElement : function(el){
15243         return this.addItem(new Roo.menu.BaseItem(el));
15244     },
15245
15246     /**
15247      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15248      * @param {Roo.menu.Item} item The menu item to add
15249      * @return {Roo.menu.Item} The menu item that was added
15250      */
15251     addItem : function(item){
15252         this.items.add(item);
15253         if(this.ul){
15254             var li = document.createElement("li");
15255             li.className = "x-menu-list-item";
15256             this.ul.dom.appendChild(li);
15257             item.render(li, this);
15258             this.delayAutoWidth();
15259         }
15260         return item;
15261     },
15262
15263     /**
15264      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15265      * @param {Object} config A MenuItem config object
15266      * @return {Roo.menu.Item} The menu item that was added
15267      */
15268     addMenuItem : function(config){
15269         if(!(config instanceof Roo.menu.Item)){
15270             if(typeof config.checked == "boolean"){ // must be check menu item config?
15271                 config = new Roo.menu.CheckItem(config);
15272             }else{
15273                 config = new Roo.menu.Item(config);
15274             }
15275         }
15276         return this.addItem(config);
15277     },
15278
15279     /**
15280      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15281      * @param {String} text The text to display in the menu item
15282      * @return {Roo.menu.Item} The menu item that was added
15283      */
15284     addText : function(text){
15285         return this.addItem(new Roo.menu.TextItem({ text : text }));
15286     },
15287
15288     /**
15289      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15290      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15291      * @param {Roo.menu.Item} item The menu item to add
15292      * @return {Roo.menu.Item} The menu item that was added
15293      */
15294     insert : function(index, item){
15295         this.items.insert(index, item);
15296         if(this.ul){
15297             var li = document.createElement("li");
15298             li.className = "x-menu-list-item";
15299             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15300             item.render(li, this);
15301             this.delayAutoWidth();
15302         }
15303         return item;
15304     },
15305
15306     /**
15307      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15308      * @param {Roo.menu.Item} item The menu item to remove
15309      */
15310     remove : function(item){
15311         this.items.removeKey(item.id);
15312         item.destroy();
15313     },
15314
15315     /**
15316      * Removes and destroys all items in the menu
15317      */
15318     removeAll : function(){
15319         var f;
15320         while(f = this.items.first()){
15321             this.remove(f);
15322         }
15323     }
15324 });
15325
15326 // MenuNav is a private utility class used internally by the Menu
15327 Roo.menu.MenuNav = function(menu){
15328     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15329     this.scope = this.menu = menu;
15330 };
15331
15332 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15333     doRelay : function(e, h){
15334         var k = e.getKey();
15335         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15336             this.menu.tryActivate(0, 1);
15337             return false;
15338         }
15339         return h.call(this.scope || this, e, this.menu);
15340     },
15341
15342     up : function(e, m){
15343         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15344             m.tryActivate(m.items.length-1, -1);
15345         }
15346     },
15347
15348     down : function(e, m){
15349         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15350             m.tryActivate(0, 1);
15351         }
15352     },
15353
15354     right : function(e, m){
15355         if(m.activeItem){
15356             m.activeItem.expandMenu(true);
15357         }
15358     },
15359
15360     left : function(e, m){
15361         m.hide();
15362         if(m.parentMenu && m.parentMenu.activeItem){
15363             m.parentMenu.activeItem.activate();
15364         }
15365     },
15366
15367     enter : function(e, m){
15368         if(m.activeItem){
15369             e.stopPropagation();
15370             m.activeItem.onClick(e);
15371             m.fireEvent("click", this, m.activeItem);
15372             return true;
15373         }
15374     }
15375 });/*
15376  * Based on:
15377  * Ext JS Library 1.1.1
15378  * Copyright(c) 2006-2007, Ext JS, LLC.
15379  *
15380  * Originally Released Under LGPL - original licence link has changed is not relivant.
15381  *
15382  * Fork - LGPL
15383  * <script type="text/javascript">
15384  */
15385  
15386 /**
15387  * @class Roo.menu.MenuMgr
15388  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15389  * @singleton
15390  */
15391 Roo.menu.MenuMgr = function(){
15392    var menus, active, groups = {}, attached = false, lastShow = new Date();
15393
15394    // private - called when first menu is created
15395    function init(){
15396        menus = {};
15397        active = new Roo.util.MixedCollection();
15398        Roo.get(document).addKeyListener(27, function(){
15399            if(active.length > 0){
15400                hideAll();
15401            }
15402        });
15403    }
15404
15405    // private
15406    function hideAll(){
15407        if(active && active.length > 0){
15408            var c = active.clone();
15409            c.each(function(m){
15410                m.hide();
15411            });
15412        }
15413    }
15414
15415    // private
15416    function onHide(m){
15417        active.remove(m);
15418        if(active.length < 1){
15419            Roo.get(document).un("mousedown", onMouseDown);
15420            attached = false;
15421        }
15422    }
15423
15424    // private
15425    function onShow(m){
15426        var last = active.last();
15427        lastShow = new Date();
15428        active.add(m);
15429        if(!attached){
15430            Roo.get(document).on("mousedown", onMouseDown);
15431            attached = true;
15432        }
15433        if(m.parentMenu){
15434           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15435           m.parentMenu.activeChild = m;
15436        }else if(last && last.isVisible()){
15437           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15438        }
15439    }
15440
15441    // private
15442    function onBeforeHide(m){
15443        if(m.activeChild){
15444            m.activeChild.hide();
15445        }
15446        if(m.autoHideTimer){
15447            clearTimeout(m.autoHideTimer);
15448            delete m.autoHideTimer;
15449        }
15450    }
15451
15452    // private
15453    function onBeforeShow(m){
15454        var pm = m.parentMenu;
15455        if(!pm && !m.allowOtherMenus){
15456            hideAll();
15457        }else if(pm && pm.activeChild && active != m){
15458            pm.activeChild.hide();
15459        }
15460    }
15461
15462    // private
15463    function onMouseDown(e){
15464        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15465            hideAll();
15466        }
15467    }
15468
15469    // private
15470    function onBeforeCheck(mi, state){
15471        if(state){
15472            var g = groups[mi.group];
15473            for(var i = 0, l = g.length; i < l; i++){
15474                if(g[i] != mi){
15475                    g[i].setChecked(false);
15476                }
15477            }
15478        }
15479    }
15480
15481    return {
15482
15483        /**
15484         * Hides all menus that are currently visible
15485         */
15486        hideAll : function(){
15487             hideAll();  
15488        },
15489
15490        // private
15491        register : function(menu){
15492            if(!menus){
15493                init();
15494            }
15495            menus[menu.id] = menu;
15496            menu.on("beforehide", onBeforeHide);
15497            menu.on("hide", onHide);
15498            menu.on("beforeshow", onBeforeShow);
15499            menu.on("show", onShow);
15500            var g = menu.group;
15501            if(g && menu.events["checkchange"]){
15502                if(!groups[g]){
15503                    groups[g] = [];
15504                }
15505                groups[g].push(menu);
15506                menu.on("checkchange", onCheck);
15507            }
15508        },
15509
15510         /**
15511          * Returns a {@link Roo.menu.Menu} object
15512          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15513          * be used to generate and return a new Menu instance.
15514          */
15515        get : function(menu){
15516            if(typeof menu == "string"){ // menu id
15517                return menus[menu];
15518            }else if(menu.events){  // menu instance
15519                return menu;
15520            }else if(typeof menu.length == 'number'){ // array of menu items?
15521                return new Roo.menu.Menu({items:menu});
15522            }else{ // otherwise, must be a config
15523                return new Roo.menu.Menu(menu);
15524            }
15525        },
15526
15527        // private
15528        unregister : function(menu){
15529            delete menus[menu.id];
15530            menu.un("beforehide", onBeforeHide);
15531            menu.un("hide", onHide);
15532            menu.un("beforeshow", onBeforeShow);
15533            menu.un("show", onShow);
15534            var g = menu.group;
15535            if(g && menu.events["checkchange"]){
15536                groups[g].remove(menu);
15537                menu.un("checkchange", onCheck);
15538            }
15539        },
15540
15541        // private
15542        registerCheckable : function(menuItem){
15543            var g = menuItem.group;
15544            if(g){
15545                if(!groups[g]){
15546                    groups[g] = [];
15547                }
15548                groups[g].push(menuItem);
15549                menuItem.on("beforecheckchange", onBeforeCheck);
15550            }
15551        },
15552
15553        // private
15554        unregisterCheckable : function(menuItem){
15555            var g = menuItem.group;
15556            if(g){
15557                groups[g].remove(menuItem);
15558                menuItem.un("beforecheckchange", onBeforeCheck);
15559            }
15560        }
15561    };
15562 }();/*
15563  * Based on:
15564  * Ext JS Library 1.1.1
15565  * Copyright(c) 2006-2007, Ext JS, LLC.
15566  *
15567  * Originally Released Under LGPL - original licence link has changed is not relivant.
15568  *
15569  * Fork - LGPL
15570  * <script type="text/javascript">
15571  */
15572  
15573
15574 /**
15575  * @class Roo.menu.BaseItem
15576  * @extends Roo.Component
15577  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15578  * management and base configuration options shared by all menu components.
15579  * @constructor
15580  * Creates a new BaseItem
15581  * @param {Object} config Configuration options
15582  */
15583 Roo.menu.BaseItem = function(config){
15584     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15585
15586     this.addEvents({
15587         /**
15588          * @event click
15589          * Fires when this item is clicked
15590          * @param {Roo.menu.BaseItem} this
15591          * @param {Roo.EventObject} e
15592          */
15593         click: true,
15594         /**
15595          * @event activate
15596          * Fires when this item is activated
15597          * @param {Roo.menu.BaseItem} this
15598          */
15599         activate : true,
15600         /**
15601          * @event deactivate
15602          * Fires when this item is deactivated
15603          * @param {Roo.menu.BaseItem} this
15604          */
15605         deactivate : true
15606     });
15607
15608     if(this.handler){
15609         this.on("click", this.handler, this.scope, true);
15610     }
15611 };
15612
15613 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15614     /**
15615      * @cfg {Function} handler
15616      * A function that will handle the click event of this menu item (defaults to undefined)
15617      */
15618     /**
15619      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15620      */
15621     canActivate : false,
15622     
15623      /**
15624      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15625      */
15626     hidden: false,
15627     
15628     /**
15629      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15630      */
15631     activeClass : "x-menu-item-active",
15632     /**
15633      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15634      */
15635     hideOnClick : true,
15636     /**
15637      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15638      */
15639     hideDelay : 100,
15640
15641     // private
15642     ctype: "Roo.menu.BaseItem",
15643
15644     // private
15645     actionMode : "container",
15646
15647     // private
15648     render : function(container, parentMenu){
15649         this.parentMenu = parentMenu;
15650         Roo.menu.BaseItem.superclass.render.call(this, container);
15651         this.container.menuItemId = this.id;
15652     },
15653
15654     // private
15655     onRender : function(container, position){
15656         this.el = Roo.get(this.el);
15657         container.dom.appendChild(this.el.dom);
15658     },
15659
15660     // private
15661     onClick : function(e){
15662         if(!this.disabled && this.fireEvent("click", this, e) !== false
15663                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15664             this.handleClick(e);
15665         }else{
15666             e.stopEvent();
15667         }
15668     },
15669
15670     // private
15671     activate : function(){
15672         if(this.disabled){
15673             return false;
15674         }
15675         var li = this.container;
15676         li.addClass(this.activeClass);
15677         this.region = li.getRegion().adjust(2, 2, -2, -2);
15678         this.fireEvent("activate", this);
15679         return true;
15680     },
15681
15682     // private
15683     deactivate : function(){
15684         this.container.removeClass(this.activeClass);
15685         this.fireEvent("deactivate", this);
15686     },
15687
15688     // private
15689     shouldDeactivate : function(e){
15690         return !this.region || !this.region.contains(e.getPoint());
15691     },
15692
15693     // private
15694     handleClick : function(e){
15695         if(this.hideOnClick){
15696             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15697         }
15698     },
15699
15700     // private
15701     expandMenu : function(autoActivate){
15702         // do nothing
15703     },
15704
15705     // private
15706     hideMenu : function(){
15707         // do nothing
15708     }
15709 });/*
15710  * Based on:
15711  * Ext JS Library 1.1.1
15712  * Copyright(c) 2006-2007, Ext JS, LLC.
15713  *
15714  * Originally Released Under LGPL - original licence link has changed is not relivant.
15715  *
15716  * Fork - LGPL
15717  * <script type="text/javascript">
15718  */
15719  
15720 /**
15721  * @class Roo.menu.Adapter
15722  * @extends Roo.menu.BaseItem
15723  * 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.
15724  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15725  * @constructor
15726  * Creates a new Adapter
15727  * @param {Object} config Configuration options
15728  */
15729 Roo.menu.Adapter = function(component, config){
15730     Roo.menu.Adapter.superclass.constructor.call(this, config);
15731     this.component = component;
15732 };
15733 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15734     // private
15735     canActivate : true,
15736
15737     // private
15738     onRender : function(container, position){
15739         this.component.render(container);
15740         this.el = this.component.getEl();
15741     },
15742
15743     // private
15744     activate : function(){
15745         if(this.disabled){
15746             return false;
15747         }
15748         this.component.focus();
15749         this.fireEvent("activate", this);
15750         return true;
15751     },
15752
15753     // private
15754     deactivate : function(){
15755         this.fireEvent("deactivate", this);
15756     },
15757
15758     // private
15759     disable : function(){
15760         this.component.disable();
15761         Roo.menu.Adapter.superclass.disable.call(this);
15762     },
15763
15764     // private
15765     enable : function(){
15766         this.component.enable();
15767         Roo.menu.Adapter.superclass.enable.call(this);
15768     }
15769 });/*
15770  * Based on:
15771  * Ext JS Library 1.1.1
15772  * Copyright(c) 2006-2007, Ext JS, LLC.
15773  *
15774  * Originally Released Under LGPL - original licence link has changed is not relivant.
15775  *
15776  * Fork - LGPL
15777  * <script type="text/javascript">
15778  */
15779
15780 /**
15781  * @class Roo.menu.TextItem
15782  * @extends Roo.menu.BaseItem
15783  * Adds a static text string to a menu, usually used as either a heading or group separator.
15784  * Note: old style constructor with text is still supported.
15785  * 
15786  * @constructor
15787  * Creates a new TextItem
15788  * @param {Object} cfg Configuration
15789  */
15790 Roo.menu.TextItem = function(cfg){
15791     if (typeof(cfg) == 'string') {
15792         this.text = cfg;
15793     } else {
15794         Roo.apply(this,cfg);
15795     }
15796     
15797     Roo.menu.TextItem.superclass.constructor.call(this);
15798 };
15799
15800 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15801     /**
15802      * @cfg {Boolean} text Text to show on item.
15803      */
15804     text : '',
15805     
15806     /**
15807      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15808      */
15809     hideOnClick : false,
15810     /**
15811      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15812      */
15813     itemCls : "x-menu-text",
15814
15815     // private
15816     onRender : function(){
15817         var s = document.createElement("span");
15818         s.className = this.itemCls;
15819         s.innerHTML = this.text;
15820         this.el = s;
15821         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15822     }
15823 });/*
15824  * Based on:
15825  * Ext JS Library 1.1.1
15826  * Copyright(c) 2006-2007, Ext JS, LLC.
15827  *
15828  * Originally Released Under LGPL - original licence link has changed is not relivant.
15829  *
15830  * Fork - LGPL
15831  * <script type="text/javascript">
15832  */
15833
15834 /**
15835  * @class Roo.menu.Separator
15836  * @extends Roo.menu.BaseItem
15837  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15838  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15839  * @constructor
15840  * @param {Object} config Configuration options
15841  */
15842 Roo.menu.Separator = function(config){
15843     Roo.menu.Separator.superclass.constructor.call(this, config);
15844 };
15845
15846 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15847     /**
15848      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15849      */
15850     itemCls : "x-menu-sep",
15851     /**
15852      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15853      */
15854     hideOnClick : false,
15855
15856     // private
15857     onRender : function(li){
15858         var s = document.createElement("span");
15859         s.className = this.itemCls;
15860         s.innerHTML = "&#160;";
15861         this.el = s;
15862         li.addClass("x-menu-sep-li");
15863         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15864     }
15865 });/*
15866  * Based on:
15867  * Ext JS Library 1.1.1
15868  * Copyright(c) 2006-2007, Ext JS, LLC.
15869  *
15870  * Originally Released Under LGPL - original licence link has changed is not relivant.
15871  *
15872  * Fork - LGPL
15873  * <script type="text/javascript">
15874  */
15875 /**
15876  * @class Roo.menu.Item
15877  * @extends Roo.menu.BaseItem
15878  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15879  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15880  * activation and click handling.
15881  * @constructor
15882  * Creates a new Item
15883  * @param {Object} config Configuration options
15884  */
15885 Roo.menu.Item = function(config){
15886     Roo.menu.Item.superclass.constructor.call(this, config);
15887     if(this.menu){
15888         this.menu = Roo.menu.MenuMgr.get(this.menu);
15889     }
15890 };
15891 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15892     
15893     /**
15894      * @cfg {String} text
15895      * The text to show on the menu item.
15896      */
15897     text: '',
15898      /**
15899      * @cfg {String} HTML to render in menu
15900      * The text to show on the menu item (HTML version).
15901      */
15902     html: '',
15903     /**
15904      * @cfg {String} icon
15905      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15906      */
15907     icon: undefined,
15908     /**
15909      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15910      */
15911     itemCls : "x-menu-item",
15912     /**
15913      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15914      */
15915     canActivate : true,
15916     /**
15917      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15918      */
15919     showDelay: 200,
15920     // doc'd in BaseItem
15921     hideDelay: 200,
15922
15923     // private
15924     ctype: "Roo.menu.Item",
15925     
15926     // private
15927     onRender : function(container, position){
15928         var el = document.createElement("a");
15929         el.hideFocus = true;
15930         el.unselectable = "on";
15931         el.href = this.href || "#";
15932         if(this.hrefTarget){
15933             el.target = this.hrefTarget;
15934         }
15935         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15936         
15937         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15938         
15939         el.innerHTML = String.format(
15940                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15941                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15942         this.el = el;
15943         Roo.menu.Item.superclass.onRender.call(this, container, position);
15944     },
15945
15946     /**
15947      * Sets the text to display in this menu item
15948      * @param {String} text The text to display
15949      * @param {Boolean} isHTML true to indicate text is pure html.
15950      */
15951     setText : function(text, isHTML){
15952         if (isHTML) {
15953             this.html = text;
15954         } else {
15955             this.text = text;
15956             this.html = '';
15957         }
15958         if(this.rendered){
15959             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15960      
15961             this.el.update(String.format(
15962                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15963                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15964             this.parentMenu.autoWidth();
15965         }
15966     },
15967
15968     // private
15969     handleClick : function(e){
15970         if(!this.href){ // if no link defined, stop the event automatically
15971             e.stopEvent();
15972         }
15973         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15974     },
15975
15976     // private
15977     activate : function(autoExpand){
15978         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15979             this.focus();
15980             if(autoExpand){
15981                 this.expandMenu();
15982             }
15983         }
15984         return true;
15985     },
15986
15987     // private
15988     shouldDeactivate : function(e){
15989         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15990             if(this.menu && this.menu.isVisible()){
15991                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15992             }
15993             return true;
15994         }
15995         return false;
15996     },
15997
15998     // private
15999     deactivate : function(){
16000         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
16001         this.hideMenu();
16002     },
16003
16004     // private
16005     expandMenu : function(autoActivate){
16006         if(!this.disabled && this.menu){
16007             clearTimeout(this.hideTimer);
16008             delete this.hideTimer;
16009             if(!this.menu.isVisible() && !this.showTimer){
16010                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
16011             }else if (this.menu.isVisible() && autoActivate){
16012                 this.menu.tryActivate(0, 1);
16013             }
16014         }
16015     },
16016
16017     // private
16018     deferExpand : function(autoActivate){
16019         delete this.showTimer;
16020         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
16021         if(autoActivate){
16022             this.menu.tryActivate(0, 1);
16023         }
16024     },
16025
16026     // private
16027     hideMenu : function(){
16028         clearTimeout(this.showTimer);
16029         delete this.showTimer;
16030         if(!this.hideTimer && this.menu && this.menu.isVisible()){
16031             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16032         }
16033     },
16034
16035     // private
16036     deferHide : function(){
16037         delete this.hideTimer;
16038         this.menu.hide();
16039     }
16040 });/*
16041  * Based on:
16042  * Ext JS Library 1.1.1
16043  * Copyright(c) 2006-2007, Ext JS, LLC.
16044  *
16045  * Originally Released Under LGPL - original licence link has changed is not relivant.
16046  *
16047  * Fork - LGPL
16048  * <script type="text/javascript">
16049  */
16050  
16051 /**
16052  * @class Roo.menu.CheckItem
16053  * @extends Roo.menu.Item
16054  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16055  * @constructor
16056  * Creates a new CheckItem
16057  * @param {Object} config Configuration options
16058  */
16059 Roo.menu.CheckItem = function(config){
16060     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16061     this.addEvents({
16062         /**
16063          * @event beforecheckchange
16064          * Fires before the checked value is set, providing an opportunity to cancel if needed
16065          * @param {Roo.menu.CheckItem} this
16066          * @param {Boolean} checked The new checked value that will be set
16067          */
16068         "beforecheckchange" : true,
16069         /**
16070          * @event checkchange
16071          * Fires after the checked value has been set
16072          * @param {Roo.menu.CheckItem} this
16073          * @param {Boolean} checked The checked value that was set
16074          */
16075         "checkchange" : true
16076     });
16077     if(this.checkHandler){
16078         this.on('checkchange', this.checkHandler, this.scope);
16079     }
16080 };
16081 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16082     /**
16083      * @cfg {String} group
16084      * All check items with the same group name will automatically be grouped into a single-select
16085      * radio button group (defaults to '')
16086      */
16087     /**
16088      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16089      */
16090     itemCls : "x-menu-item x-menu-check-item",
16091     /**
16092      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16093      */
16094     groupClass : "x-menu-group-item",
16095
16096     /**
16097      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16098      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16099      * initialized with checked = true will be rendered as checked.
16100      */
16101     checked: false,
16102
16103     // private
16104     ctype: "Roo.menu.CheckItem",
16105
16106     // private
16107     onRender : function(c){
16108         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16109         if(this.group){
16110             this.el.addClass(this.groupClass);
16111         }
16112         Roo.menu.MenuMgr.registerCheckable(this);
16113         if(this.checked){
16114             this.checked = false;
16115             this.setChecked(true, true);
16116         }
16117     },
16118
16119     // private
16120     destroy : function(){
16121         if(this.rendered){
16122             Roo.menu.MenuMgr.unregisterCheckable(this);
16123         }
16124         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16125     },
16126
16127     /**
16128      * Set the checked state of this item
16129      * @param {Boolean} checked The new checked value
16130      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16131      */
16132     setChecked : function(state, suppressEvent){
16133         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16134             if(this.container){
16135                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16136             }
16137             this.checked = state;
16138             if(suppressEvent !== true){
16139                 this.fireEvent("checkchange", this, state);
16140             }
16141         }
16142     },
16143
16144     // private
16145     handleClick : function(e){
16146        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16147            this.setChecked(!this.checked);
16148        }
16149        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16150     }
16151 });/*
16152  * Based on:
16153  * Ext JS Library 1.1.1
16154  * Copyright(c) 2006-2007, Ext JS, LLC.
16155  *
16156  * Originally Released Under LGPL - original licence link has changed is not relivant.
16157  *
16158  * Fork - LGPL
16159  * <script type="text/javascript">
16160  */
16161  
16162 /**
16163  * @class Roo.menu.DateItem
16164  * @extends Roo.menu.Adapter
16165  * A menu item that wraps the {@link Roo.DatPicker} component.
16166  * @constructor
16167  * Creates a new DateItem
16168  * @param {Object} config Configuration options
16169  */
16170 Roo.menu.DateItem = function(config){
16171     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16172     /** The Roo.DatePicker object @type Roo.DatePicker */
16173     this.picker = this.component;
16174     this.addEvents({select: true});
16175     
16176     this.picker.on("render", function(picker){
16177         picker.getEl().swallowEvent("click");
16178         picker.container.addClass("x-menu-date-item");
16179     });
16180
16181     this.picker.on("select", this.onSelect, this);
16182 };
16183
16184 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16185     // private
16186     onSelect : function(picker, date){
16187         this.fireEvent("select", this, date, picker);
16188         Roo.menu.DateItem.superclass.handleClick.call(this);
16189     }
16190 });/*
16191  * Based on:
16192  * Ext JS Library 1.1.1
16193  * Copyright(c) 2006-2007, Ext JS, LLC.
16194  *
16195  * Originally Released Under LGPL - original licence link has changed is not relivant.
16196  *
16197  * Fork - LGPL
16198  * <script type="text/javascript">
16199  */
16200  
16201 /**
16202  * @class Roo.menu.ColorItem
16203  * @extends Roo.menu.Adapter
16204  * A menu item that wraps the {@link Roo.ColorPalette} component.
16205  * @constructor
16206  * Creates a new ColorItem
16207  * @param {Object} config Configuration options
16208  */
16209 Roo.menu.ColorItem = function(config){
16210     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16211     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16212     this.palette = this.component;
16213     this.relayEvents(this.palette, ["select"]);
16214     if(this.selectHandler){
16215         this.on('select', this.selectHandler, this.scope);
16216     }
16217 };
16218 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16219  * Based on:
16220  * Ext JS Library 1.1.1
16221  * Copyright(c) 2006-2007, Ext JS, LLC.
16222  *
16223  * Originally Released Under LGPL - original licence link has changed is not relivant.
16224  *
16225  * Fork - LGPL
16226  * <script type="text/javascript">
16227  */
16228  
16229
16230 /**
16231  * @class Roo.menu.DateMenu
16232  * @extends Roo.menu.Menu
16233  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16234  * @constructor
16235  * Creates a new DateMenu
16236  * @param {Object} config Configuration options
16237  */
16238 Roo.menu.DateMenu = function(config){
16239     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16240     this.plain = true;
16241     var di = new Roo.menu.DateItem(config);
16242     this.add(di);
16243     /**
16244      * The {@link Roo.DatePicker} instance for this DateMenu
16245      * @type DatePicker
16246      */
16247     this.picker = di.picker;
16248     /**
16249      * @event select
16250      * @param {DatePicker} picker
16251      * @param {Date} date
16252      */
16253     this.relayEvents(di, ["select"]);
16254     this.on('beforeshow', function(){
16255         if(this.picker){
16256             this.picker.hideMonthPicker(false);
16257         }
16258     }, this);
16259 };
16260 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16261     cls:'x-date-menu'
16262 });/*
16263  * Based on:
16264  * Ext JS Library 1.1.1
16265  * Copyright(c) 2006-2007, Ext JS, LLC.
16266  *
16267  * Originally Released Under LGPL - original licence link has changed is not relivant.
16268  *
16269  * Fork - LGPL
16270  * <script type="text/javascript">
16271  */
16272  
16273
16274 /**
16275  * @class Roo.menu.ColorMenu
16276  * @extends Roo.menu.Menu
16277  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16278  * @constructor
16279  * Creates a new ColorMenu
16280  * @param {Object} config Configuration options
16281  */
16282 Roo.menu.ColorMenu = function(config){
16283     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16284     this.plain = true;
16285     var ci = new Roo.menu.ColorItem(config);
16286     this.add(ci);
16287     /**
16288      * The {@link Roo.ColorPalette} instance for this ColorMenu
16289      * @type ColorPalette
16290      */
16291     this.palette = ci.palette;
16292     /**
16293      * @event select
16294      * @param {ColorPalette} palette
16295      * @param {String} color
16296      */
16297     this.relayEvents(ci, ["select"]);
16298 };
16299 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16300  * Based on:
16301  * Ext JS Library 1.1.1
16302  * Copyright(c) 2006-2007, Ext JS, LLC.
16303  *
16304  * Originally Released Under LGPL - original licence link has changed is not relivant.
16305  *
16306  * Fork - LGPL
16307  * <script type="text/javascript">
16308  */
16309  
16310 /**
16311  * @class Roo.form.TextItem
16312  * @extends Roo.BoxComponent
16313  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16314  * @constructor
16315  * Creates a new TextItem
16316  * @param {Object} config Configuration options
16317  */
16318 Roo.form.TextItem = function(config){
16319     Roo.form.TextItem.superclass.constructor.call(this, config);
16320 };
16321
16322 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16323     
16324     /**
16325      * @cfg {String} tag the tag for this item (default div)
16326      */
16327     tag : 'div',
16328     /**
16329      * @cfg {String} html the content for this item
16330      */
16331     html : '',
16332     
16333     getAutoCreate : function()
16334     {
16335         var cfg = {
16336             id: this.id,
16337             tag: this.tag,
16338             html: this.html,
16339             cls: 'x-form-item'
16340         };
16341         
16342         return cfg;
16343         
16344     },
16345     
16346     onRender : function(ct, position)
16347     {
16348         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16349         
16350         if(!this.el){
16351             var cfg = this.getAutoCreate();
16352             if(!cfg.name){
16353                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16354             }
16355             if (!cfg.name.length) {
16356                 delete cfg.name;
16357             }
16358             this.el = ct.createChild(cfg, position);
16359         }
16360     }
16361     
16362 });/*
16363  * Based on:
16364  * Ext JS Library 1.1.1
16365  * Copyright(c) 2006-2007, Ext JS, LLC.
16366  *
16367  * Originally Released Under LGPL - original licence link has changed is not relivant.
16368  *
16369  * Fork - LGPL
16370  * <script type="text/javascript">
16371  */
16372  
16373 /**
16374  * @class Roo.form.Field
16375  * @extends Roo.BoxComponent
16376  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16377  * @constructor
16378  * Creates a new Field
16379  * @param {Object} config Configuration options
16380  */
16381 Roo.form.Field = function(config){
16382     Roo.form.Field.superclass.constructor.call(this, config);
16383 };
16384
16385 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16386     /**
16387      * @cfg {String} fieldLabel Label to use when rendering a form.
16388      */
16389        /**
16390      * @cfg {String} qtip Mouse over tip
16391      */
16392      
16393     /**
16394      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16395      */
16396     invalidClass : "x-form-invalid",
16397     /**
16398      * @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")
16399      */
16400     invalidText : "The value in this field is invalid",
16401     /**
16402      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16403      */
16404     focusClass : "x-form-focus",
16405     /**
16406      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16407       automatic validation (defaults to "keyup").
16408      */
16409     validationEvent : "keyup",
16410     /**
16411      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16412      */
16413     validateOnBlur : true,
16414     /**
16415      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16416      */
16417     validationDelay : 250,
16418     /**
16419      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16420      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16421      */
16422     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16423     /**
16424      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16425      */
16426     fieldClass : "x-form-field",
16427     /**
16428      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16429      *<pre>
16430 Value         Description
16431 -----------   ----------------------------------------------------------------------
16432 qtip          Display a quick tip when the user hovers over the field
16433 title         Display a default browser title attribute popup
16434 under         Add a block div beneath the field containing the error text
16435 side          Add an error icon to the right of the field with a popup on hover
16436 [element id]  Add the error text directly to the innerHTML of the specified element
16437 </pre>
16438      */
16439     msgTarget : 'qtip',
16440     /**
16441      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16442      */
16443     msgFx : 'normal',
16444
16445     /**
16446      * @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.
16447      */
16448     readOnly : false,
16449
16450     /**
16451      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16452      */
16453     disabled : false,
16454
16455     /**
16456      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16457      */
16458     inputType : undefined,
16459     
16460     /**
16461      * @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).
16462          */
16463         tabIndex : undefined,
16464         
16465     // private
16466     isFormField : true,
16467
16468     // private
16469     hasFocus : false,
16470     /**
16471      * @property {Roo.Element} fieldEl
16472      * Element Containing the rendered Field (with label etc.)
16473      */
16474     /**
16475      * @cfg {Mixed} value A value to initialize this field with.
16476      */
16477     value : undefined,
16478
16479     /**
16480      * @cfg {String} name The field's HTML name attribute.
16481      */
16482     /**
16483      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16484      */
16485     // private
16486     loadedValue : false,
16487      
16488      
16489         // private ??
16490         initComponent : function(){
16491         Roo.form.Field.superclass.initComponent.call(this);
16492         this.addEvents({
16493             /**
16494              * @event focus
16495              * Fires when this field receives input focus.
16496              * @param {Roo.form.Field} this
16497              */
16498             focus : true,
16499             /**
16500              * @event blur
16501              * Fires when this field loses input focus.
16502              * @param {Roo.form.Field} this
16503              */
16504             blur : true,
16505             /**
16506              * @event specialkey
16507              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16508              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16509              * @param {Roo.form.Field} this
16510              * @param {Roo.EventObject} e The event object
16511              */
16512             specialkey : true,
16513             /**
16514              * @event change
16515              * Fires just before the field blurs if the field value has changed.
16516              * @param {Roo.form.Field} this
16517              * @param {Mixed} newValue The new value
16518              * @param {Mixed} oldValue The original value
16519              */
16520             change : true,
16521             /**
16522              * @event invalid
16523              * Fires after the field has been marked as invalid.
16524              * @param {Roo.form.Field} this
16525              * @param {String} msg The validation message
16526              */
16527             invalid : true,
16528             /**
16529              * @event valid
16530              * Fires after the field has been validated with no errors.
16531              * @param {Roo.form.Field} this
16532              */
16533             valid : true,
16534              /**
16535              * @event keyup
16536              * Fires after the key up
16537              * @param {Roo.form.Field} this
16538              * @param {Roo.EventObject}  e The event Object
16539              */
16540             keyup : true
16541         });
16542     },
16543
16544     /**
16545      * Returns the name attribute of the field if available
16546      * @return {String} name The field name
16547      */
16548     getName: function(){
16549          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16550     },
16551
16552     // private
16553     onRender : function(ct, position){
16554         Roo.form.Field.superclass.onRender.call(this, ct, position);
16555         if(!this.el){
16556             var cfg = this.getAutoCreate();
16557             if(!cfg.name){
16558                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16559             }
16560             if (!cfg.name.length) {
16561                 delete cfg.name;
16562             }
16563             if(this.inputType){
16564                 cfg.type = this.inputType;
16565             }
16566             this.el = ct.createChild(cfg, position);
16567         }
16568         var type = this.el.dom.type;
16569         if(type){
16570             if(type == 'password'){
16571                 type = 'text';
16572             }
16573             this.el.addClass('x-form-'+type);
16574         }
16575         if(this.readOnly){
16576             this.el.dom.readOnly = true;
16577         }
16578         if(this.tabIndex !== undefined){
16579             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16580         }
16581
16582         this.el.addClass([this.fieldClass, this.cls]);
16583         this.initValue();
16584     },
16585
16586     /**
16587      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16588      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16589      * @return {Roo.form.Field} this
16590      */
16591     applyTo : function(target){
16592         this.allowDomMove = false;
16593         this.el = Roo.get(target);
16594         this.render(this.el.dom.parentNode);
16595         return this;
16596     },
16597
16598     // private
16599     initValue : function(){
16600         if(this.value !== undefined){
16601             this.setValue(this.value);
16602         }else if(this.el.dom.value.length > 0){
16603             this.setValue(this.el.dom.value);
16604         }
16605     },
16606
16607     /**
16608      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16609      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16610      */
16611     isDirty : function() {
16612         if(this.disabled) {
16613             return false;
16614         }
16615         return String(this.getValue()) !== String(this.originalValue);
16616     },
16617
16618     /**
16619      * stores the current value in loadedValue
16620      */
16621     resetHasChanged : function()
16622     {
16623         this.loadedValue = String(this.getValue());
16624     },
16625     /**
16626      * checks the current value against the 'loaded' value.
16627      * Note - will return false if 'resetHasChanged' has not been called first.
16628      */
16629     hasChanged : function()
16630     {
16631         if(this.disabled || this.readOnly) {
16632             return false;
16633         }
16634         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16635     },
16636     
16637     
16638     
16639     // private
16640     afterRender : function(){
16641         Roo.form.Field.superclass.afterRender.call(this);
16642         this.initEvents();
16643     },
16644
16645     // private
16646     fireKey : function(e){
16647         //Roo.log('field ' + e.getKey());
16648         if(e.isNavKeyPress()){
16649             this.fireEvent("specialkey", this, e);
16650         }
16651     },
16652
16653     /**
16654      * Resets the current field value to the originally loaded value and clears any validation messages
16655      */
16656     reset : function(){
16657         this.setValue(this.resetValue);
16658         this.originalValue = this.getValue();
16659         this.clearInvalid();
16660     },
16661
16662     // private
16663     initEvents : function(){
16664         // safari killled keypress - so keydown is now used..
16665         this.el.on("keydown" , this.fireKey,  this);
16666         this.el.on("focus", this.onFocus,  this);
16667         this.el.on("blur", this.onBlur,  this);
16668         this.el.relayEvent('keyup', this);
16669
16670         // reference to original value for reset
16671         this.originalValue = this.getValue();
16672         this.resetValue =  this.getValue();
16673     },
16674
16675     // private
16676     onFocus : function(){
16677         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16678             this.el.addClass(this.focusClass);
16679         }
16680         if(!this.hasFocus){
16681             this.hasFocus = true;
16682             this.startValue = this.getValue();
16683             this.fireEvent("focus", this);
16684         }
16685     },
16686
16687     beforeBlur : Roo.emptyFn,
16688
16689     // private
16690     onBlur : function(){
16691         this.beforeBlur();
16692         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16693             this.el.removeClass(this.focusClass);
16694         }
16695         this.hasFocus = false;
16696         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16697             this.validate();
16698         }
16699         var v = this.getValue();
16700         if(String(v) !== String(this.startValue)){
16701             this.fireEvent('change', this, v, this.startValue);
16702         }
16703         this.fireEvent("blur", this);
16704     },
16705
16706     /**
16707      * Returns whether or not the field value is currently valid
16708      * @param {Boolean} preventMark True to disable marking the field invalid
16709      * @return {Boolean} True if the value is valid, else false
16710      */
16711     isValid : function(preventMark){
16712         if(this.disabled){
16713             return true;
16714         }
16715         var restore = this.preventMark;
16716         this.preventMark = preventMark === true;
16717         var v = this.validateValue(this.processValue(this.getRawValue()));
16718         this.preventMark = restore;
16719         return v;
16720     },
16721
16722     /**
16723      * Validates the field value
16724      * @return {Boolean} True if the value is valid, else false
16725      */
16726     validate : function(){
16727         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16728             this.clearInvalid();
16729             return true;
16730         }
16731         return false;
16732     },
16733
16734     processValue : function(value){
16735         return value;
16736     },
16737
16738     // private
16739     // Subclasses should provide the validation implementation by overriding this
16740     validateValue : function(value){
16741         return true;
16742     },
16743
16744     /**
16745      * Mark this field as invalid
16746      * @param {String} msg The validation message
16747      */
16748     markInvalid : function(msg){
16749         if(!this.rendered || this.preventMark){ // not rendered
16750             return;
16751         }
16752         
16753         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16754         
16755         obj.el.addClass(this.invalidClass);
16756         msg = msg || this.invalidText;
16757         switch(this.msgTarget){
16758             case 'qtip':
16759                 obj.el.dom.qtip = msg;
16760                 obj.el.dom.qclass = 'x-form-invalid-tip';
16761                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16762                     Roo.QuickTips.enable();
16763                 }
16764                 break;
16765             case 'title':
16766                 this.el.dom.title = msg;
16767                 break;
16768             case 'under':
16769                 if(!this.errorEl){
16770                     var elp = this.el.findParent('.x-form-element', 5, true);
16771                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16772                     this.errorEl.setWidth(elp.getWidth(true)-20);
16773                 }
16774                 this.errorEl.update(msg);
16775                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16776                 break;
16777             case 'side':
16778                 if(!this.errorIcon){
16779                     var elp = this.el.findParent('.x-form-element', 5, true);
16780                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16781                 }
16782                 this.alignErrorIcon();
16783                 this.errorIcon.dom.qtip = msg;
16784                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16785                 this.errorIcon.show();
16786                 this.on('resize', this.alignErrorIcon, this);
16787                 break;
16788             default:
16789                 var t = Roo.getDom(this.msgTarget);
16790                 t.innerHTML = msg;
16791                 t.style.display = this.msgDisplay;
16792                 break;
16793         }
16794         this.fireEvent('invalid', this, msg);
16795     },
16796
16797     // private
16798     alignErrorIcon : function(){
16799         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16800     },
16801
16802     /**
16803      * Clear any invalid styles/messages for this field
16804      */
16805     clearInvalid : function(){
16806         if(!this.rendered || this.preventMark){ // not rendered
16807             return;
16808         }
16809         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16810         
16811         obj.el.removeClass(this.invalidClass);
16812         switch(this.msgTarget){
16813             case 'qtip':
16814                 obj.el.dom.qtip = '';
16815                 break;
16816             case 'title':
16817                 this.el.dom.title = '';
16818                 break;
16819             case 'under':
16820                 if(this.errorEl){
16821                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16822                 }
16823                 break;
16824             case 'side':
16825                 if(this.errorIcon){
16826                     this.errorIcon.dom.qtip = '';
16827                     this.errorIcon.hide();
16828                     this.un('resize', this.alignErrorIcon, this);
16829                 }
16830                 break;
16831             default:
16832                 var t = Roo.getDom(this.msgTarget);
16833                 t.innerHTML = '';
16834                 t.style.display = 'none';
16835                 break;
16836         }
16837         this.fireEvent('valid', this);
16838     },
16839
16840     /**
16841      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16842      * @return {Mixed} value The field value
16843      */
16844     getRawValue : function(){
16845         var v = this.el.getValue();
16846         
16847         return v;
16848     },
16849
16850     /**
16851      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16852      * @return {Mixed} value The field value
16853      */
16854     getValue : function(){
16855         var v = this.el.getValue();
16856          
16857         return v;
16858     },
16859
16860     /**
16861      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16862      * @param {Mixed} value The value to set
16863      */
16864     setRawValue : function(v){
16865         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16866     },
16867
16868     /**
16869      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16870      * @param {Mixed} value The value to set
16871      */
16872     setValue : function(v){
16873         this.value = v;
16874         if(this.rendered){
16875             this.el.dom.value = (v === null || v === undefined ? '' : v);
16876              this.validate();
16877         }
16878     },
16879
16880     adjustSize : function(w, h){
16881         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16882         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16883         return s;
16884     },
16885
16886     adjustWidth : function(tag, w){
16887         tag = tag.toLowerCase();
16888         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16889             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16890                 if(tag == 'input'){
16891                     return w + 2;
16892                 }
16893                 if(tag == 'textarea'){
16894                     return w-2;
16895                 }
16896             }else if(Roo.isOpera){
16897                 if(tag == 'input'){
16898                     return w + 2;
16899                 }
16900                 if(tag == 'textarea'){
16901                     return w-2;
16902                 }
16903             }
16904         }
16905         return w;
16906     }
16907 });
16908
16909
16910 // anything other than normal should be considered experimental
16911 Roo.form.Field.msgFx = {
16912     normal : {
16913         show: function(msgEl, f){
16914             msgEl.setDisplayed('block');
16915         },
16916
16917         hide : function(msgEl, f){
16918             msgEl.setDisplayed(false).update('');
16919         }
16920     },
16921
16922     slide : {
16923         show: function(msgEl, f){
16924             msgEl.slideIn('t', {stopFx:true});
16925         },
16926
16927         hide : function(msgEl, f){
16928             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16929         }
16930     },
16931
16932     slideRight : {
16933         show: function(msgEl, f){
16934             msgEl.fixDisplay();
16935             msgEl.alignTo(f.el, 'tl-tr');
16936             msgEl.slideIn('l', {stopFx:true});
16937         },
16938
16939         hide : function(msgEl, f){
16940             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16941         }
16942     }
16943 };/*
16944  * Based on:
16945  * Ext JS Library 1.1.1
16946  * Copyright(c) 2006-2007, Ext JS, LLC.
16947  *
16948  * Originally Released Under LGPL - original licence link has changed is not relivant.
16949  *
16950  * Fork - LGPL
16951  * <script type="text/javascript">
16952  */
16953  
16954
16955 /**
16956  * @class Roo.form.TextField
16957  * @extends Roo.form.Field
16958  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16959  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16960  * @constructor
16961  * Creates a new TextField
16962  * @param {Object} config Configuration options
16963  */
16964 Roo.form.TextField = function(config){
16965     Roo.form.TextField.superclass.constructor.call(this, config);
16966     this.addEvents({
16967         /**
16968          * @event autosize
16969          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16970          * according to the default logic, but this event provides a hook for the developer to apply additional
16971          * logic at runtime to resize the field if needed.
16972              * @param {Roo.form.Field} this This text field
16973              * @param {Number} width The new field width
16974              */
16975         autosize : true
16976     });
16977 };
16978
16979 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16980     /**
16981      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16982      */
16983     grow : false,
16984     /**
16985      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16986      */
16987     growMin : 30,
16988     /**
16989      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16990      */
16991     growMax : 800,
16992     /**
16993      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16994      */
16995     vtype : null,
16996     /**
16997      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16998      */
16999     maskRe : null,
17000     /**
17001      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
17002      */
17003     disableKeyFilter : false,
17004     /**
17005      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
17006      */
17007     allowBlank : true,
17008     /**
17009      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
17010      */
17011     minLength : 0,
17012     /**
17013      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
17014      */
17015     maxLength : Number.MAX_VALUE,
17016     /**
17017      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
17018      */
17019     minLengthText : "The minimum length for this field is {0}",
17020     /**
17021      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17022      */
17023     maxLengthText : "The maximum length for this field is {0}",
17024     /**
17025      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17026      */
17027     selectOnFocus : false,
17028     /**
17029      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
17030      */    
17031     allowLeadingSpace : false,
17032     /**
17033      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17034      */
17035     blankText : "This field is required",
17036     /**
17037      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17038      * If available, this function will be called only after the basic validators all return true, and will be passed the
17039      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17040      */
17041     validator : null,
17042     /**
17043      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17044      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17045      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17046      */
17047     regex : null,
17048     /**
17049      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17050      */
17051     regexText : "",
17052     /**
17053      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17054      */
17055     emptyText : null,
17056    
17057
17058     // private
17059     initEvents : function()
17060     {
17061         if (this.emptyText) {
17062             this.el.attr('placeholder', this.emptyText);
17063         }
17064         
17065         Roo.form.TextField.superclass.initEvents.call(this);
17066         if(this.validationEvent == 'keyup'){
17067             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17068             this.el.on('keyup', this.filterValidation, this);
17069         }
17070         else if(this.validationEvent !== false){
17071             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17072         }
17073         
17074         if(this.selectOnFocus){
17075             this.on("focus", this.preFocus, this);
17076         }
17077         if (!this.allowLeadingSpace) {
17078             this.on('blur', this.cleanLeadingSpace, this);
17079         }
17080         
17081         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17082             this.el.on("keypress", this.filterKeys, this);
17083         }
17084         if(this.grow){
17085             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17086             this.el.on("click", this.autoSize,  this);
17087         }
17088         if(this.el.is('input[type=password]') && Roo.isSafari){
17089             this.el.on('keydown', this.SafariOnKeyDown, this);
17090         }
17091     },
17092
17093     processValue : function(value){
17094         if(this.stripCharsRe){
17095             var newValue = value.replace(this.stripCharsRe, '');
17096             if(newValue !== value){
17097                 this.setRawValue(newValue);
17098                 return newValue;
17099             }
17100         }
17101         return value;
17102     },
17103
17104     filterValidation : function(e){
17105         if(!e.isNavKeyPress()){
17106             this.validationTask.delay(this.validationDelay);
17107         }
17108     },
17109
17110     // private
17111     onKeyUp : function(e){
17112         if(!e.isNavKeyPress()){
17113             this.autoSize();
17114         }
17115     },
17116     // private - clean the leading white space
17117     cleanLeadingSpace : function(e)
17118     {
17119         if ( this.inputType == 'file') {
17120             return;
17121         }
17122         
17123         this.setValue((this.getValue() + '').replace(/^\s+/,''));
17124     },
17125     /**
17126      * Resets the current field value to the originally-loaded value and clears any validation messages.
17127      *  
17128      */
17129     reset : function(){
17130         Roo.form.TextField.superclass.reset.call(this);
17131        
17132     }, 
17133     // private
17134     preFocus : function(){
17135         
17136         if(this.selectOnFocus){
17137             this.el.dom.select();
17138         }
17139     },
17140
17141     
17142     // private
17143     filterKeys : function(e){
17144         var k = e.getKey();
17145         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17146             return;
17147         }
17148         var c = e.getCharCode(), cc = String.fromCharCode(c);
17149         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17150             return;
17151         }
17152         if(!this.maskRe.test(cc)){
17153             e.stopEvent();
17154         }
17155     },
17156
17157     setValue : function(v){
17158         
17159         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17160         
17161         this.autoSize();
17162     },
17163
17164     /**
17165      * Validates a value according to the field's validation rules and marks the field as invalid
17166      * if the validation fails
17167      * @param {Mixed} value The value to validate
17168      * @return {Boolean} True if the value is valid, else false
17169      */
17170     validateValue : function(value){
17171         if(value.length < 1)  { // if it's blank
17172              if(this.allowBlank){
17173                 this.clearInvalid();
17174                 return true;
17175              }else{
17176                 this.markInvalid(this.blankText);
17177                 return false;
17178              }
17179         }
17180         if(value.length < this.minLength){
17181             this.markInvalid(String.format(this.minLengthText, this.minLength));
17182             return false;
17183         }
17184         if(value.length > this.maxLength){
17185             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17186             return false;
17187         }
17188         if(this.vtype){
17189             var vt = Roo.form.VTypes;
17190             if(!vt[this.vtype](value, this)){
17191                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17192                 return false;
17193             }
17194         }
17195         if(typeof this.validator == "function"){
17196             var msg = this.validator(value);
17197             if(msg !== true){
17198                 this.markInvalid(msg);
17199                 return false;
17200             }
17201         }
17202         if(this.regex && !this.regex.test(value)){
17203             this.markInvalid(this.regexText);
17204             return false;
17205         }
17206         return true;
17207     },
17208
17209     /**
17210      * Selects text in this field
17211      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17212      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17213      */
17214     selectText : function(start, end){
17215         var v = this.getRawValue();
17216         if(v.length > 0){
17217             start = start === undefined ? 0 : start;
17218             end = end === undefined ? v.length : end;
17219             var d = this.el.dom;
17220             if(d.setSelectionRange){
17221                 d.setSelectionRange(start, end);
17222             }else if(d.createTextRange){
17223                 var range = d.createTextRange();
17224                 range.moveStart("character", start);
17225                 range.moveEnd("character", v.length-end);
17226                 range.select();
17227             }
17228         }
17229     },
17230
17231     /**
17232      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17233      * This only takes effect if grow = true, and fires the autosize event.
17234      */
17235     autoSize : function(){
17236         if(!this.grow || !this.rendered){
17237             return;
17238         }
17239         if(!this.metrics){
17240             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17241         }
17242         var el = this.el;
17243         var v = el.dom.value;
17244         var d = document.createElement('div');
17245         d.appendChild(document.createTextNode(v));
17246         v = d.innerHTML;
17247         d = null;
17248         v += "&#160;";
17249         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17250         this.el.setWidth(w);
17251         this.fireEvent("autosize", this, w);
17252     },
17253     
17254     // private
17255     SafariOnKeyDown : function(event)
17256     {
17257         // this is a workaround for a password hang bug on chrome/ webkit.
17258         
17259         var isSelectAll = false;
17260         
17261         if(this.el.dom.selectionEnd > 0){
17262             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17263         }
17264         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17265             event.preventDefault();
17266             this.setValue('');
17267             return;
17268         }
17269         
17270         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17271             
17272             event.preventDefault();
17273             // this is very hacky as keydown always get's upper case.
17274             
17275             var cc = String.fromCharCode(event.getCharCode());
17276             
17277             
17278             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17279             
17280         }
17281         
17282         
17283     }
17284 });/*
17285  * Based on:
17286  * Ext JS Library 1.1.1
17287  * Copyright(c) 2006-2007, Ext JS, LLC.
17288  *
17289  * Originally Released Under LGPL - original licence link has changed is not relivant.
17290  *
17291  * Fork - LGPL
17292  * <script type="text/javascript">
17293  */
17294  
17295 /**
17296  * @class Roo.form.Hidden
17297  * @extends Roo.form.TextField
17298  * Simple Hidden element used on forms 
17299  * 
17300  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17301  * 
17302  * @constructor
17303  * Creates a new Hidden form element.
17304  * @param {Object} config Configuration options
17305  */
17306
17307
17308
17309 // easy hidden field...
17310 Roo.form.Hidden = function(config){
17311     Roo.form.Hidden.superclass.constructor.call(this, config);
17312 };
17313   
17314 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17315     fieldLabel:      '',
17316     inputType:      'hidden',
17317     width:          50,
17318     allowBlank:     true,
17319     labelSeparator: '',
17320     hidden:         true,
17321     itemCls :       'x-form-item-display-none'
17322
17323
17324 });
17325
17326
17327 /*
17328  * Based on:
17329  * Ext JS Library 1.1.1
17330  * Copyright(c) 2006-2007, Ext JS, LLC.
17331  *
17332  * Originally Released Under LGPL - original licence link has changed is not relivant.
17333  *
17334  * Fork - LGPL
17335  * <script type="text/javascript">
17336  */
17337  
17338 /**
17339  * @class Roo.form.TriggerField
17340  * @extends Roo.form.TextField
17341  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17342  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17343  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17344  * for which you can provide a custom implementation.  For example:
17345  * <pre><code>
17346 var trigger = new Roo.form.TriggerField();
17347 trigger.onTriggerClick = myTriggerFn;
17348 trigger.applyTo('my-field');
17349 </code></pre>
17350  *
17351  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17352  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17353  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17354  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17355  * @constructor
17356  * Create a new TriggerField.
17357  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17358  * to the base TextField)
17359  */
17360 Roo.form.TriggerField = function(config){
17361     this.mimicing = false;
17362     Roo.form.TriggerField.superclass.constructor.call(this, config);
17363 };
17364
17365 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17366     /**
17367      * @cfg {String} triggerClass A CSS class to apply to the trigger
17368      */
17369     /**
17370      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17371      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17372      */
17373     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17374     /**
17375      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17376      */
17377     hideTrigger:false,
17378
17379     /** @cfg {Boolean} grow @hide */
17380     /** @cfg {Number} growMin @hide */
17381     /** @cfg {Number} growMax @hide */
17382
17383     /**
17384      * @hide 
17385      * @method
17386      */
17387     autoSize: Roo.emptyFn,
17388     // private
17389     monitorTab : true,
17390     // private
17391     deferHeight : true,
17392
17393     
17394     actionMode : 'wrap',
17395     // private
17396     onResize : function(w, h){
17397         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17398         if(typeof w == 'number'){
17399             var x = w - this.trigger.getWidth();
17400             this.el.setWidth(this.adjustWidth('input', x));
17401             this.trigger.setStyle('left', x+'px');
17402         }
17403     },
17404
17405     // private
17406     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17407
17408     // private
17409     getResizeEl : function(){
17410         return this.wrap;
17411     },
17412
17413     // private
17414     getPositionEl : function(){
17415         return this.wrap;
17416     },
17417
17418     // private
17419     alignErrorIcon : function(){
17420         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17421     },
17422
17423     // private
17424     onRender : function(ct, position){
17425         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17426         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17427         this.trigger = this.wrap.createChild(this.triggerConfig ||
17428                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17429         if(this.hideTrigger){
17430             this.trigger.setDisplayed(false);
17431         }
17432         this.initTrigger();
17433         if(!this.width){
17434             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17435         }
17436     },
17437
17438     // private
17439     initTrigger : function(){
17440         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17441         this.trigger.addClassOnOver('x-form-trigger-over');
17442         this.trigger.addClassOnClick('x-form-trigger-click');
17443     },
17444
17445     // private
17446     onDestroy : function(){
17447         if(this.trigger){
17448             this.trigger.removeAllListeners();
17449             this.trigger.remove();
17450         }
17451         if(this.wrap){
17452             this.wrap.remove();
17453         }
17454         Roo.form.TriggerField.superclass.onDestroy.call(this);
17455     },
17456
17457     // private
17458     onFocus : function(){
17459         Roo.form.TriggerField.superclass.onFocus.call(this);
17460         if(!this.mimicing){
17461             this.wrap.addClass('x-trigger-wrap-focus');
17462             this.mimicing = true;
17463             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17464             if(this.monitorTab){
17465                 this.el.on("keydown", this.checkTab, this);
17466             }
17467         }
17468     },
17469
17470     // private
17471     checkTab : function(e){
17472         if(e.getKey() == e.TAB){
17473             this.triggerBlur();
17474         }
17475     },
17476
17477     // private
17478     onBlur : function(){
17479         // do nothing
17480     },
17481
17482     // private
17483     mimicBlur : function(e, t){
17484         if(!this.wrap.contains(t) && this.validateBlur()){
17485             this.triggerBlur();
17486         }
17487     },
17488
17489     // private
17490     triggerBlur : function(){
17491         this.mimicing = false;
17492         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17493         if(this.monitorTab){
17494             this.el.un("keydown", this.checkTab, this);
17495         }
17496         this.wrap.removeClass('x-trigger-wrap-focus');
17497         Roo.form.TriggerField.superclass.onBlur.call(this);
17498     },
17499
17500     // private
17501     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17502     validateBlur : function(e, t){
17503         return true;
17504     },
17505
17506     // private
17507     onDisable : function(){
17508         Roo.form.TriggerField.superclass.onDisable.call(this);
17509         if(this.wrap){
17510             this.wrap.addClass('x-item-disabled');
17511         }
17512     },
17513
17514     // private
17515     onEnable : function(){
17516         Roo.form.TriggerField.superclass.onEnable.call(this);
17517         if(this.wrap){
17518             this.wrap.removeClass('x-item-disabled');
17519         }
17520     },
17521
17522     // private
17523     onShow : function(){
17524         var ae = this.getActionEl();
17525         
17526         if(ae){
17527             ae.dom.style.display = '';
17528             ae.dom.style.visibility = 'visible';
17529         }
17530     },
17531
17532     // private
17533     
17534     onHide : function(){
17535         var ae = this.getActionEl();
17536         ae.dom.style.display = 'none';
17537     },
17538
17539     /**
17540      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17541      * by an implementing function.
17542      * @method
17543      * @param {EventObject} e
17544      */
17545     onTriggerClick : Roo.emptyFn
17546 });
17547
17548 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17549 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17550 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17551 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17552     initComponent : function(){
17553         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17554
17555         this.triggerConfig = {
17556             tag:'span', cls:'x-form-twin-triggers', cn:[
17557             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17558             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17559         ]};
17560     },
17561
17562     getTrigger : function(index){
17563         return this.triggers[index];
17564     },
17565
17566     initTrigger : function(){
17567         var ts = this.trigger.select('.x-form-trigger', true);
17568         this.wrap.setStyle('overflow', 'hidden');
17569         var triggerField = this;
17570         ts.each(function(t, all, index){
17571             t.hide = function(){
17572                 var w = triggerField.wrap.getWidth();
17573                 this.dom.style.display = 'none';
17574                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17575             };
17576             t.show = function(){
17577                 var w = triggerField.wrap.getWidth();
17578                 this.dom.style.display = '';
17579                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17580             };
17581             var triggerIndex = 'Trigger'+(index+1);
17582
17583             if(this['hide'+triggerIndex]){
17584                 t.dom.style.display = 'none';
17585             }
17586             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17587             t.addClassOnOver('x-form-trigger-over');
17588             t.addClassOnClick('x-form-trigger-click');
17589         }, this);
17590         this.triggers = ts.elements;
17591     },
17592
17593     onTrigger1Click : Roo.emptyFn,
17594     onTrigger2Click : Roo.emptyFn
17595 });/*
17596  * Based on:
17597  * Ext JS Library 1.1.1
17598  * Copyright(c) 2006-2007, Ext JS, LLC.
17599  *
17600  * Originally Released Under LGPL - original licence link has changed is not relivant.
17601  *
17602  * Fork - LGPL
17603  * <script type="text/javascript">
17604  */
17605  
17606 /**
17607  * @class Roo.form.TextArea
17608  * @extends Roo.form.TextField
17609  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17610  * support for auto-sizing.
17611  * @constructor
17612  * Creates a new TextArea
17613  * @param {Object} config Configuration options
17614  */
17615 Roo.form.TextArea = function(config){
17616     Roo.form.TextArea.superclass.constructor.call(this, config);
17617     // these are provided exchanges for backwards compat
17618     // minHeight/maxHeight were replaced by growMin/growMax to be
17619     // compatible with TextField growing config values
17620     if(this.minHeight !== undefined){
17621         this.growMin = this.minHeight;
17622     }
17623     if(this.maxHeight !== undefined){
17624         this.growMax = this.maxHeight;
17625     }
17626 };
17627
17628 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17629     /**
17630      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17631      */
17632     growMin : 60,
17633     /**
17634      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17635      */
17636     growMax: 1000,
17637     /**
17638      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17639      * in the field (equivalent to setting overflow: hidden, defaults to false)
17640      */
17641     preventScrollbars: false,
17642     /**
17643      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17644      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17645      */
17646
17647     // private
17648     onRender : function(ct, position){
17649         if(!this.el){
17650             this.defaultAutoCreate = {
17651                 tag: "textarea",
17652                 style:"width:300px;height:60px;",
17653                 autocomplete: "new-password"
17654             };
17655         }
17656         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17657         if(this.grow){
17658             this.textSizeEl = Roo.DomHelper.append(document.body, {
17659                 tag: "pre", cls: "x-form-grow-sizer"
17660             });
17661             if(this.preventScrollbars){
17662                 this.el.setStyle("overflow", "hidden");
17663             }
17664             this.el.setHeight(this.growMin);
17665         }
17666     },
17667
17668     onDestroy : function(){
17669         if(this.textSizeEl){
17670             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17671         }
17672         Roo.form.TextArea.superclass.onDestroy.call(this);
17673     },
17674
17675     // private
17676     onKeyUp : function(e){
17677         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17678             this.autoSize();
17679         }
17680     },
17681
17682     /**
17683      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17684      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17685      */
17686     autoSize : function(){
17687         if(!this.grow || !this.textSizeEl){
17688             return;
17689         }
17690         var el = this.el;
17691         var v = el.dom.value;
17692         var ts = this.textSizeEl;
17693
17694         ts.innerHTML = '';
17695         ts.appendChild(document.createTextNode(v));
17696         v = ts.innerHTML;
17697
17698         Roo.fly(ts).setWidth(this.el.getWidth());
17699         if(v.length < 1){
17700             v = "&#160;&#160;";
17701         }else{
17702             if(Roo.isIE){
17703                 v = v.replace(/\n/g, '<p>&#160;</p>');
17704             }
17705             v += "&#160;\n&#160;";
17706         }
17707         ts.innerHTML = v;
17708         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17709         if(h != this.lastHeight){
17710             this.lastHeight = h;
17711             this.el.setHeight(h);
17712             this.fireEvent("autosize", this, h);
17713         }
17714     }
17715 });/*
17716  * Based on:
17717  * Ext JS Library 1.1.1
17718  * Copyright(c) 2006-2007, Ext JS, LLC.
17719  *
17720  * Originally Released Under LGPL - original licence link has changed is not relivant.
17721  *
17722  * Fork - LGPL
17723  * <script type="text/javascript">
17724  */
17725  
17726
17727 /**
17728  * @class Roo.form.NumberField
17729  * @extends Roo.form.TextField
17730  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17731  * @constructor
17732  * Creates a new NumberField
17733  * @param {Object} config Configuration options
17734  */
17735 Roo.form.NumberField = function(config){
17736     Roo.form.NumberField.superclass.constructor.call(this, config);
17737 };
17738
17739 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17740     /**
17741      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17742      */
17743     fieldClass: "x-form-field x-form-num-field",
17744     /**
17745      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17746      */
17747     allowDecimals : true,
17748     /**
17749      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17750      */
17751     decimalSeparator : ".",
17752     /**
17753      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17754      */
17755     decimalPrecision : 2,
17756     /**
17757      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17758      */
17759     allowNegative : true,
17760     /**
17761      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17762      */
17763     minValue : Number.NEGATIVE_INFINITY,
17764     /**
17765      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17766      */
17767     maxValue : Number.MAX_VALUE,
17768     /**
17769      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17770      */
17771     minText : "The minimum value for this field is {0}",
17772     /**
17773      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17774      */
17775     maxText : "The maximum value for this field is {0}",
17776     /**
17777      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17778      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17779      */
17780     nanText : "{0} is not a valid number",
17781
17782     // private
17783     initEvents : function(){
17784         Roo.form.NumberField.superclass.initEvents.call(this);
17785         var allowed = "0123456789";
17786         if(this.allowDecimals){
17787             allowed += this.decimalSeparator;
17788         }
17789         if(this.allowNegative){
17790             allowed += "-";
17791         }
17792         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17793         var keyPress = function(e){
17794             var k = e.getKey();
17795             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17796                 return;
17797             }
17798             var c = e.getCharCode();
17799             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17800                 e.stopEvent();
17801             }
17802         };
17803         this.el.on("keypress", keyPress, this);
17804     },
17805
17806     // private
17807     validateValue : function(value){
17808         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17809             return false;
17810         }
17811         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17812              return true;
17813         }
17814         var num = this.parseValue(value);
17815         if(isNaN(num)){
17816             this.markInvalid(String.format(this.nanText, value));
17817             return false;
17818         }
17819         if(num < this.minValue){
17820             this.markInvalid(String.format(this.minText, this.minValue));
17821             return false;
17822         }
17823         if(num > this.maxValue){
17824             this.markInvalid(String.format(this.maxText, this.maxValue));
17825             return false;
17826         }
17827         return true;
17828     },
17829
17830     getValue : function(){
17831         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17832     },
17833
17834     // private
17835     parseValue : function(value){
17836         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17837         return isNaN(value) ? '' : value;
17838     },
17839
17840     // private
17841     fixPrecision : function(value){
17842         var nan = isNaN(value);
17843         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17844             return nan ? '' : value;
17845         }
17846         return parseFloat(value).toFixed(this.decimalPrecision);
17847     },
17848
17849     setValue : function(v){
17850         v = this.fixPrecision(v);
17851         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17852     },
17853
17854     // private
17855     decimalPrecisionFcn : function(v){
17856         return Math.floor(v);
17857     },
17858
17859     beforeBlur : function(){
17860         var v = this.parseValue(this.getRawValue());
17861         if(v){
17862             this.setValue(v);
17863         }
17864     }
17865 });/*
17866  * Based on:
17867  * Ext JS Library 1.1.1
17868  * Copyright(c) 2006-2007, Ext JS, LLC.
17869  *
17870  * Originally Released Under LGPL - original licence link has changed is not relivant.
17871  *
17872  * Fork - LGPL
17873  * <script type="text/javascript">
17874  */
17875  
17876 /**
17877  * @class Roo.form.DateField
17878  * @extends Roo.form.TriggerField
17879  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17880 * @constructor
17881 * Create a new DateField
17882 * @param {Object} config
17883  */
17884 Roo.form.DateField = function(config)
17885 {
17886     Roo.form.DateField.superclass.constructor.call(this, config);
17887     
17888       this.addEvents({
17889          
17890         /**
17891          * @event select
17892          * Fires when a date is selected
17893              * @param {Roo.form.DateField} combo This combo box
17894              * @param {Date} date The date selected
17895              */
17896         'select' : true
17897          
17898     });
17899     
17900     
17901     if(typeof this.minValue == "string") {
17902         this.minValue = this.parseDate(this.minValue);
17903     }
17904     if(typeof this.maxValue == "string") {
17905         this.maxValue = this.parseDate(this.maxValue);
17906     }
17907     this.ddMatch = null;
17908     if(this.disabledDates){
17909         var dd = this.disabledDates;
17910         var re = "(?:";
17911         for(var i = 0; i < dd.length; i++){
17912             re += dd[i];
17913             if(i != dd.length-1) {
17914                 re += "|";
17915             }
17916         }
17917         this.ddMatch = new RegExp(re + ")");
17918     }
17919 };
17920
17921 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17922     /**
17923      * @cfg {String} format
17924      * The default date format string which can be overriden for localization support.  The format must be
17925      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17926      */
17927     format : "m/d/y",
17928     /**
17929      * @cfg {String} altFormats
17930      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17931      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17932      */
17933     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17934     /**
17935      * @cfg {Array} disabledDays
17936      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17937      */
17938     disabledDays : null,
17939     /**
17940      * @cfg {String} disabledDaysText
17941      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17942      */
17943     disabledDaysText : "Disabled",
17944     /**
17945      * @cfg {Array} disabledDates
17946      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17947      * expression so they are very powerful. Some examples:
17948      * <ul>
17949      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17950      * <li>["03/08", "09/16"] would disable those days for every year</li>
17951      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17952      * <li>["03/../2006"] would disable every day in March 2006</li>
17953      * <li>["^03"] would disable every day in every March</li>
17954      * </ul>
17955      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17956      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17957      */
17958     disabledDates : null,
17959     /**
17960      * @cfg {String} disabledDatesText
17961      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17962      */
17963     disabledDatesText : "Disabled",
17964     /**
17965      * @cfg {Date/String} minValue
17966      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17967      * valid format (defaults to null).
17968      */
17969     minValue : null,
17970     /**
17971      * @cfg {Date/String} maxValue
17972      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17973      * valid format (defaults to null).
17974      */
17975     maxValue : null,
17976     /**
17977      * @cfg {String} minText
17978      * The error text to display when the date in the cell is before minValue (defaults to
17979      * 'The date in this field must be after {minValue}').
17980      */
17981     minText : "The date in this field must be equal to or after {0}",
17982     /**
17983      * @cfg {String} maxText
17984      * The error text to display when the date in the cell is after maxValue (defaults to
17985      * 'The date in this field must be before {maxValue}').
17986      */
17987     maxText : "The date in this field must be equal to or before {0}",
17988     /**
17989      * @cfg {String} invalidText
17990      * The error text to display when the date in the field is invalid (defaults to
17991      * '{value} is not a valid date - it must be in the format {format}').
17992      */
17993     invalidText : "{0} is not a valid date - it must be in the format {1}",
17994     /**
17995      * @cfg {String} triggerClass
17996      * An additional CSS class used to style the trigger button.  The trigger will always get the
17997      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17998      * which displays a calendar icon).
17999      */
18000     triggerClass : 'x-form-date-trigger',
18001     
18002
18003     /**
18004      * @cfg {Boolean} useIso
18005      * if enabled, then the date field will use a hidden field to store the 
18006      * real value as iso formated date. default (false)
18007      */ 
18008     useIso : false,
18009     /**
18010      * @cfg {String/Object} autoCreate
18011      * A DomHelper element spec, or true for a default element spec (defaults to
18012      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18013      */ 
18014     // private
18015     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
18016     
18017     // private
18018     hiddenField: false,
18019     
18020     onRender : function(ct, position)
18021     {
18022         Roo.form.DateField.superclass.onRender.call(this, ct, position);
18023         if (this.useIso) {
18024             //this.el.dom.removeAttribute('name'); 
18025             Roo.log("Changing name?");
18026             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
18027             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18028                     'before', true);
18029             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18030             // prevent input submission
18031             this.hiddenName = this.name;
18032         }
18033             
18034             
18035     },
18036     
18037     // private
18038     validateValue : function(value)
18039     {
18040         value = this.formatDate(value);
18041         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18042             Roo.log('super failed');
18043             return false;
18044         }
18045         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18046              return true;
18047         }
18048         var svalue = value;
18049         value = this.parseDate(value);
18050         if(!value){
18051             Roo.log('parse date failed' + svalue);
18052             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18053             return false;
18054         }
18055         var time = value.getTime();
18056         if(this.minValue && time < this.minValue.getTime()){
18057             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18058             return false;
18059         }
18060         if(this.maxValue && time > this.maxValue.getTime()){
18061             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18062             return false;
18063         }
18064         if(this.disabledDays){
18065             var day = value.getDay();
18066             for(var i = 0; i < this.disabledDays.length; i++) {
18067                 if(day === this.disabledDays[i]){
18068                     this.markInvalid(this.disabledDaysText);
18069                     return false;
18070                 }
18071             }
18072         }
18073         var fvalue = this.formatDate(value);
18074         if(this.ddMatch && this.ddMatch.test(fvalue)){
18075             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18076             return false;
18077         }
18078         return true;
18079     },
18080
18081     // private
18082     // Provides logic to override the default TriggerField.validateBlur which just returns true
18083     validateBlur : function(){
18084         return !this.menu || !this.menu.isVisible();
18085     },
18086     
18087     getName: function()
18088     {
18089         // returns hidden if it's set..
18090         if (!this.rendered) {return ''};
18091         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18092         
18093     },
18094
18095     /**
18096      * Returns the current date value of the date field.
18097      * @return {Date} The date value
18098      */
18099     getValue : function(){
18100         
18101         return  this.hiddenField ?
18102                 this.hiddenField.value :
18103                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18104     },
18105
18106     /**
18107      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18108      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18109      * (the default format used is "m/d/y").
18110      * <br />Usage:
18111      * <pre><code>
18112 //All of these calls set the same date value (May 4, 2006)
18113
18114 //Pass a date object:
18115 var dt = new Date('5/4/06');
18116 dateField.setValue(dt);
18117
18118 //Pass a date string (default format):
18119 dateField.setValue('5/4/06');
18120
18121 //Pass a date string (custom format):
18122 dateField.format = 'Y-m-d';
18123 dateField.setValue('2006-5-4');
18124 </code></pre>
18125      * @param {String/Date} date The date or valid date string
18126      */
18127     setValue : function(date){
18128         if (this.hiddenField) {
18129             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18130         }
18131         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18132         // make sure the value field is always stored as a date..
18133         this.value = this.parseDate(date);
18134         
18135         
18136     },
18137
18138     // private
18139     parseDate : function(value){
18140         if(!value || value instanceof Date){
18141             return value;
18142         }
18143         var v = Date.parseDate(value, this.format);
18144          if (!v && this.useIso) {
18145             v = Date.parseDate(value, 'Y-m-d');
18146         }
18147         if(!v && this.altFormats){
18148             if(!this.altFormatsArray){
18149                 this.altFormatsArray = this.altFormats.split("|");
18150             }
18151             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18152                 v = Date.parseDate(value, this.altFormatsArray[i]);
18153             }
18154         }
18155         return v;
18156     },
18157
18158     // private
18159     formatDate : function(date, fmt){
18160         return (!date || !(date instanceof Date)) ?
18161                date : date.dateFormat(fmt || this.format);
18162     },
18163
18164     // private
18165     menuListeners : {
18166         select: function(m, d){
18167             
18168             this.setValue(d);
18169             this.fireEvent('select', this, d);
18170         },
18171         show : function(){ // retain focus styling
18172             this.onFocus();
18173         },
18174         hide : function(){
18175             this.focus.defer(10, this);
18176             var ml = this.menuListeners;
18177             this.menu.un("select", ml.select,  this);
18178             this.menu.un("show", ml.show,  this);
18179             this.menu.un("hide", ml.hide,  this);
18180         }
18181     },
18182
18183     // private
18184     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18185     onTriggerClick : function(){
18186         if(this.disabled){
18187             return;
18188         }
18189         if(this.menu == null){
18190             this.menu = new Roo.menu.DateMenu();
18191         }
18192         Roo.apply(this.menu.picker,  {
18193             showClear: this.allowBlank,
18194             minDate : this.minValue,
18195             maxDate : this.maxValue,
18196             disabledDatesRE : this.ddMatch,
18197             disabledDatesText : this.disabledDatesText,
18198             disabledDays : this.disabledDays,
18199             disabledDaysText : this.disabledDaysText,
18200             format : this.useIso ? 'Y-m-d' : this.format,
18201             minText : String.format(this.minText, this.formatDate(this.minValue)),
18202             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18203         });
18204         this.menu.on(Roo.apply({}, this.menuListeners, {
18205             scope:this
18206         }));
18207         this.menu.picker.setValue(this.getValue() || new Date());
18208         this.menu.show(this.el, "tl-bl?");
18209     },
18210
18211     beforeBlur : function(){
18212         var v = this.parseDate(this.getRawValue());
18213         if(v){
18214             this.setValue(v);
18215         }
18216     },
18217
18218     /*@
18219      * overide
18220      * 
18221      */
18222     isDirty : function() {
18223         if(this.disabled) {
18224             return false;
18225         }
18226         
18227         if(typeof(this.startValue) === 'undefined'){
18228             return false;
18229         }
18230         
18231         return String(this.getValue()) !== String(this.startValue);
18232         
18233     },
18234     // @overide
18235     cleanLeadingSpace : function(e)
18236     {
18237        return;
18238     }
18239     
18240 });/*
18241  * Based on:
18242  * Ext JS Library 1.1.1
18243  * Copyright(c) 2006-2007, Ext JS, LLC.
18244  *
18245  * Originally Released Under LGPL - original licence link has changed is not relivant.
18246  *
18247  * Fork - LGPL
18248  * <script type="text/javascript">
18249  */
18250  
18251 /**
18252  * @class Roo.form.MonthField
18253  * @extends Roo.form.TriggerField
18254  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18255 * @constructor
18256 * Create a new MonthField
18257 * @param {Object} config
18258  */
18259 Roo.form.MonthField = function(config){
18260     
18261     Roo.form.MonthField.superclass.constructor.call(this, config);
18262     
18263       this.addEvents({
18264          
18265         /**
18266          * @event select
18267          * Fires when a date is selected
18268              * @param {Roo.form.MonthFieeld} combo This combo box
18269              * @param {Date} date The date selected
18270              */
18271         'select' : true
18272          
18273     });
18274     
18275     
18276     if(typeof this.minValue == "string") {
18277         this.minValue = this.parseDate(this.minValue);
18278     }
18279     if(typeof this.maxValue == "string") {
18280         this.maxValue = this.parseDate(this.maxValue);
18281     }
18282     this.ddMatch = null;
18283     if(this.disabledDates){
18284         var dd = this.disabledDates;
18285         var re = "(?:";
18286         for(var i = 0; i < dd.length; i++){
18287             re += dd[i];
18288             if(i != dd.length-1) {
18289                 re += "|";
18290             }
18291         }
18292         this.ddMatch = new RegExp(re + ")");
18293     }
18294 };
18295
18296 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18297     /**
18298      * @cfg {String} format
18299      * The default date format string which can be overriden for localization support.  The format must be
18300      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18301      */
18302     format : "M Y",
18303     /**
18304      * @cfg {String} altFormats
18305      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18306      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18307      */
18308     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18309     /**
18310      * @cfg {Array} disabledDays
18311      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18312      */
18313     disabledDays : [0,1,2,3,4,5,6],
18314     /**
18315      * @cfg {String} disabledDaysText
18316      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18317      */
18318     disabledDaysText : "Disabled",
18319     /**
18320      * @cfg {Array} disabledDates
18321      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18322      * expression so they are very powerful. Some examples:
18323      * <ul>
18324      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18325      * <li>["03/08", "09/16"] would disable those days for every year</li>
18326      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18327      * <li>["03/../2006"] would disable every day in March 2006</li>
18328      * <li>["^03"] would disable every day in every March</li>
18329      * </ul>
18330      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18331      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18332      */
18333     disabledDates : null,
18334     /**
18335      * @cfg {String} disabledDatesText
18336      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18337      */
18338     disabledDatesText : "Disabled",
18339     /**
18340      * @cfg {Date/String} minValue
18341      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18342      * valid format (defaults to null).
18343      */
18344     minValue : null,
18345     /**
18346      * @cfg {Date/String} maxValue
18347      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18348      * valid format (defaults to null).
18349      */
18350     maxValue : null,
18351     /**
18352      * @cfg {String} minText
18353      * The error text to display when the date in the cell is before minValue (defaults to
18354      * 'The date in this field must be after {minValue}').
18355      */
18356     minText : "The date in this field must be equal to or after {0}",
18357     /**
18358      * @cfg {String} maxTextf
18359      * The error text to display when the date in the cell is after maxValue (defaults to
18360      * 'The date in this field must be before {maxValue}').
18361      */
18362     maxText : "The date in this field must be equal to or before {0}",
18363     /**
18364      * @cfg {String} invalidText
18365      * The error text to display when the date in the field is invalid (defaults to
18366      * '{value} is not a valid date - it must be in the format {format}').
18367      */
18368     invalidText : "{0} is not a valid date - it must be in the format {1}",
18369     /**
18370      * @cfg {String} triggerClass
18371      * An additional CSS class used to style the trigger button.  The trigger will always get the
18372      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18373      * which displays a calendar icon).
18374      */
18375     triggerClass : 'x-form-date-trigger',
18376     
18377
18378     /**
18379      * @cfg {Boolean} useIso
18380      * if enabled, then the date field will use a hidden field to store the 
18381      * real value as iso formated date. default (true)
18382      */ 
18383     useIso : true,
18384     /**
18385      * @cfg {String/Object} autoCreate
18386      * A DomHelper element spec, or true for a default element spec (defaults to
18387      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18388      */ 
18389     // private
18390     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18391     
18392     // private
18393     hiddenField: false,
18394     
18395     hideMonthPicker : false,
18396     
18397     onRender : function(ct, position)
18398     {
18399         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18400         if (this.useIso) {
18401             this.el.dom.removeAttribute('name'); 
18402             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18403                     'before', true);
18404             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18405             // prevent input submission
18406             this.hiddenName = this.name;
18407         }
18408             
18409             
18410     },
18411     
18412     // private
18413     validateValue : function(value)
18414     {
18415         value = this.formatDate(value);
18416         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18417             return false;
18418         }
18419         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18420              return true;
18421         }
18422         var svalue = value;
18423         value = this.parseDate(value);
18424         if(!value){
18425             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18426             return false;
18427         }
18428         var time = value.getTime();
18429         if(this.minValue && time < this.minValue.getTime()){
18430             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18431             return false;
18432         }
18433         if(this.maxValue && time > this.maxValue.getTime()){
18434             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18435             return false;
18436         }
18437         /*if(this.disabledDays){
18438             var day = value.getDay();
18439             for(var i = 0; i < this.disabledDays.length; i++) {
18440                 if(day === this.disabledDays[i]){
18441                     this.markInvalid(this.disabledDaysText);
18442                     return false;
18443                 }
18444             }
18445         }
18446         */
18447         var fvalue = this.formatDate(value);
18448         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18449             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18450             return false;
18451         }
18452         */
18453         return true;
18454     },
18455
18456     // private
18457     // Provides logic to override the default TriggerField.validateBlur which just returns true
18458     validateBlur : function(){
18459         return !this.menu || !this.menu.isVisible();
18460     },
18461
18462     /**
18463      * Returns the current date value of the date field.
18464      * @return {Date} The date value
18465      */
18466     getValue : function(){
18467         
18468         
18469         
18470         return  this.hiddenField ?
18471                 this.hiddenField.value :
18472                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18473     },
18474
18475     /**
18476      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18477      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18478      * (the default format used is "m/d/y").
18479      * <br />Usage:
18480      * <pre><code>
18481 //All of these calls set the same date value (May 4, 2006)
18482
18483 //Pass a date object:
18484 var dt = new Date('5/4/06');
18485 monthField.setValue(dt);
18486
18487 //Pass a date string (default format):
18488 monthField.setValue('5/4/06');
18489
18490 //Pass a date string (custom format):
18491 monthField.format = 'Y-m-d';
18492 monthField.setValue('2006-5-4');
18493 </code></pre>
18494      * @param {String/Date} date The date or valid date string
18495      */
18496     setValue : function(date){
18497         Roo.log('month setValue' + date);
18498         // can only be first of month..
18499         
18500         var val = this.parseDate(date);
18501         
18502         if (this.hiddenField) {
18503             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18504         }
18505         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18506         this.value = this.parseDate(date);
18507     },
18508
18509     // private
18510     parseDate : function(value){
18511         if(!value || value instanceof Date){
18512             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18513             return value;
18514         }
18515         var v = Date.parseDate(value, this.format);
18516         if (!v && this.useIso) {
18517             v = Date.parseDate(value, 'Y-m-d');
18518         }
18519         if (v) {
18520             // 
18521             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18522         }
18523         
18524         
18525         if(!v && this.altFormats){
18526             if(!this.altFormatsArray){
18527                 this.altFormatsArray = this.altFormats.split("|");
18528             }
18529             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18530                 v = Date.parseDate(value, this.altFormatsArray[i]);
18531             }
18532         }
18533         return v;
18534     },
18535
18536     // private
18537     formatDate : function(date, fmt){
18538         return (!date || !(date instanceof Date)) ?
18539                date : date.dateFormat(fmt || this.format);
18540     },
18541
18542     // private
18543     menuListeners : {
18544         select: function(m, d){
18545             this.setValue(d);
18546             this.fireEvent('select', this, d);
18547         },
18548         show : function(){ // retain focus styling
18549             this.onFocus();
18550         },
18551         hide : function(){
18552             this.focus.defer(10, this);
18553             var ml = this.menuListeners;
18554             this.menu.un("select", ml.select,  this);
18555             this.menu.un("show", ml.show,  this);
18556             this.menu.un("hide", ml.hide,  this);
18557         }
18558     },
18559     // private
18560     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18561     onTriggerClick : function(){
18562         if(this.disabled){
18563             return;
18564         }
18565         if(this.menu == null){
18566             this.menu = new Roo.menu.DateMenu();
18567            
18568         }
18569         
18570         Roo.apply(this.menu.picker,  {
18571             
18572             showClear: this.allowBlank,
18573             minDate : this.minValue,
18574             maxDate : this.maxValue,
18575             disabledDatesRE : this.ddMatch,
18576             disabledDatesText : this.disabledDatesText,
18577             
18578             format : this.useIso ? 'Y-m-d' : this.format,
18579             minText : String.format(this.minText, this.formatDate(this.minValue)),
18580             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18581             
18582         });
18583          this.menu.on(Roo.apply({}, this.menuListeners, {
18584             scope:this
18585         }));
18586        
18587         
18588         var m = this.menu;
18589         var p = m.picker;
18590         
18591         // hide month picker get's called when we called by 'before hide';
18592         
18593         var ignorehide = true;
18594         p.hideMonthPicker  = function(disableAnim){
18595             if (ignorehide) {
18596                 return;
18597             }
18598              if(this.monthPicker){
18599                 Roo.log("hideMonthPicker called");
18600                 if(disableAnim === true){
18601                     this.monthPicker.hide();
18602                 }else{
18603                     this.monthPicker.slideOut('t', {duration:.2});
18604                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18605                     p.fireEvent("select", this, this.value);
18606                     m.hide();
18607                 }
18608             }
18609         }
18610         
18611         Roo.log('picker set value');
18612         Roo.log(this.getValue());
18613         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18614         m.show(this.el, 'tl-bl?');
18615         ignorehide  = false;
18616         // this will trigger hideMonthPicker..
18617         
18618         
18619         // hidden the day picker
18620         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18621         
18622         
18623         
18624       
18625         
18626         p.showMonthPicker.defer(100, p);
18627     
18628         
18629        
18630     },
18631
18632     beforeBlur : function(){
18633         var v = this.parseDate(this.getRawValue());
18634         if(v){
18635             this.setValue(v);
18636         }
18637     }
18638
18639     /** @cfg {Boolean} grow @hide */
18640     /** @cfg {Number} growMin @hide */
18641     /** @cfg {Number} growMax @hide */
18642     /**
18643      * @hide
18644      * @method autoSize
18645      */
18646 });/*
18647  * Based on:
18648  * Ext JS Library 1.1.1
18649  * Copyright(c) 2006-2007, Ext JS, LLC.
18650  *
18651  * Originally Released Under LGPL - original licence link has changed is not relivant.
18652  *
18653  * Fork - LGPL
18654  * <script type="text/javascript">
18655  */
18656  
18657
18658 /**
18659  * @class Roo.form.ComboBox
18660  * @extends Roo.form.TriggerField
18661  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18662  * @constructor
18663  * Create a new ComboBox.
18664  * @param {Object} config Configuration options
18665  */
18666 Roo.form.ComboBox = function(config){
18667     Roo.form.ComboBox.superclass.constructor.call(this, config);
18668     this.addEvents({
18669         /**
18670          * @event expand
18671          * Fires when the dropdown list is expanded
18672              * @param {Roo.form.ComboBox} combo This combo box
18673              */
18674         'expand' : true,
18675         /**
18676          * @event collapse
18677          * Fires when the dropdown list is collapsed
18678              * @param {Roo.form.ComboBox} combo This combo box
18679              */
18680         'collapse' : true,
18681         /**
18682          * @event beforeselect
18683          * Fires before a list item is selected. Return false to cancel the selection.
18684              * @param {Roo.form.ComboBox} combo This combo box
18685              * @param {Roo.data.Record} record The data record returned from the underlying store
18686              * @param {Number} index The index of the selected item in the dropdown list
18687              */
18688         'beforeselect' : true,
18689         /**
18690          * @event select
18691          * Fires when a list item is selected
18692              * @param {Roo.form.ComboBox} combo This combo box
18693              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18694              * @param {Number} index The index of the selected item in the dropdown list
18695              */
18696         'select' : true,
18697         /**
18698          * @event beforequery
18699          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18700          * The event object passed has these properties:
18701              * @param {Roo.form.ComboBox} combo This combo box
18702              * @param {String} query The query
18703              * @param {Boolean} forceAll true to force "all" query
18704              * @param {Boolean} cancel true to cancel the query
18705              * @param {Object} e The query event object
18706              */
18707         'beforequery': true,
18708          /**
18709          * @event add
18710          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18711              * @param {Roo.form.ComboBox} combo This combo box
18712              */
18713         'add' : true,
18714         /**
18715          * @event edit
18716          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18717              * @param {Roo.form.ComboBox} combo This combo box
18718              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18719              */
18720         'edit' : true
18721         
18722         
18723     });
18724     if(this.transform){
18725         this.allowDomMove = false;
18726         var s = Roo.getDom(this.transform);
18727         if(!this.hiddenName){
18728             this.hiddenName = s.name;
18729         }
18730         if(!this.store){
18731             this.mode = 'local';
18732             var d = [], opts = s.options;
18733             for(var i = 0, len = opts.length;i < len; i++){
18734                 var o = opts[i];
18735                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18736                 if(o.selected) {
18737                     this.value = value;
18738                 }
18739                 d.push([value, o.text]);
18740             }
18741             this.store = new Roo.data.SimpleStore({
18742                 'id': 0,
18743                 fields: ['value', 'text'],
18744                 data : d
18745             });
18746             this.valueField = 'value';
18747             this.displayField = 'text';
18748         }
18749         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18750         if(!this.lazyRender){
18751             this.target = true;
18752             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18753             s.parentNode.removeChild(s); // remove it
18754             this.render(this.el.parentNode);
18755         }else{
18756             s.parentNode.removeChild(s); // remove it
18757         }
18758
18759     }
18760     if (this.store) {
18761         this.store = Roo.factory(this.store, Roo.data);
18762     }
18763     
18764     this.selectedIndex = -1;
18765     if(this.mode == 'local'){
18766         if(config.queryDelay === undefined){
18767             this.queryDelay = 10;
18768         }
18769         if(config.minChars === undefined){
18770             this.minChars = 0;
18771         }
18772     }
18773 };
18774
18775 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18776     /**
18777      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18778      */
18779     /**
18780      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18781      * rendering into an Roo.Editor, defaults to false)
18782      */
18783     /**
18784      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18785      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18786      */
18787     /**
18788      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18789      */
18790     /**
18791      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18792      * the dropdown list (defaults to undefined, with no header element)
18793      */
18794
18795      /**
18796      * @cfg {String/Roo.Template} tpl The template to use to render the output
18797      */
18798      
18799     // private
18800     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18801     /**
18802      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18803      */
18804     listWidth: undefined,
18805     /**
18806      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18807      * mode = 'remote' or 'text' if mode = 'local')
18808      */
18809     displayField: undefined,
18810     /**
18811      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18812      * mode = 'remote' or 'value' if mode = 'local'). 
18813      * Note: use of a valueField requires the user make a selection
18814      * in order for a value to be mapped.
18815      */
18816     valueField: undefined,
18817     
18818     
18819     /**
18820      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18821      * field's data value (defaults to the underlying DOM element's name)
18822      */
18823     hiddenName: undefined,
18824     /**
18825      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18826      */
18827     listClass: '',
18828     /**
18829      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18830      */
18831     selectedClass: 'x-combo-selected',
18832     /**
18833      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18834      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18835      * which displays a downward arrow icon).
18836      */
18837     triggerClass : 'x-form-arrow-trigger',
18838     /**
18839      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18840      */
18841     shadow:'sides',
18842     /**
18843      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18844      * anchor positions (defaults to 'tl-bl')
18845      */
18846     listAlign: 'tl-bl?',
18847     /**
18848      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18849      */
18850     maxHeight: 300,
18851     /**
18852      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18853      * query specified by the allQuery config option (defaults to 'query')
18854      */
18855     triggerAction: 'query',
18856     /**
18857      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18858      * (defaults to 4, does not apply if editable = false)
18859      */
18860     minChars : 4,
18861     /**
18862      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18863      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18864      */
18865     typeAhead: false,
18866     /**
18867      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18868      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18869      */
18870     queryDelay: 500,
18871     /**
18872      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18873      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18874      */
18875     pageSize: 0,
18876     /**
18877      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18878      * when editable = true (defaults to false)
18879      */
18880     selectOnFocus:false,
18881     /**
18882      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18883      */
18884     queryParam: 'query',
18885     /**
18886      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18887      * when mode = 'remote' (defaults to 'Loading...')
18888      */
18889     loadingText: 'Loading...',
18890     /**
18891      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18892      */
18893     resizable: false,
18894     /**
18895      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18896      */
18897     handleHeight : 8,
18898     /**
18899      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18900      * traditional select (defaults to true)
18901      */
18902     editable: true,
18903     /**
18904      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18905      */
18906     allQuery: '',
18907     /**
18908      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18909      */
18910     mode: 'remote',
18911     /**
18912      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18913      * listWidth has a higher value)
18914      */
18915     minListWidth : 70,
18916     /**
18917      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18918      * allow the user to set arbitrary text into the field (defaults to false)
18919      */
18920     forceSelection:false,
18921     /**
18922      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18923      * if typeAhead = true (defaults to 250)
18924      */
18925     typeAheadDelay : 250,
18926     /**
18927      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18928      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18929      */
18930     valueNotFoundText : undefined,
18931     /**
18932      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18933      */
18934     blockFocus : false,
18935     
18936     /**
18937      * @cfg {Boolean} disableClear Disable showing of clear button.
18938      */
18939     disableClear : false,
18940     /**
18941      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18942      */
18943     alwaysQuery : false,
18944     
18945     //private
18946     addicon : false,
18947     editicon: false,
18948     
18949     // element that contains real text value.. (when hidden is used..)
18950      
18951     // private
18952     onRender : function(ct, position)
18953     {
18954         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18955         
18956         if(this.hiddenName){
18957             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18958                     'before', true);
18959             this.hiddenField.value =
18960                 this.hiddenValue !== undefined ? this.hiddenValue :
18961                 this.value !== undefined ? this.value : '';
18962
18963             // prevent input submission
18964             this.el.dom.removeAttribute('name');
18965              
18966              
18967         }
18968         
18969         if(Roo.isGecko){
18970             this.el.dom.setAttribute('autocomplete', 'off');
18971         }
18972
18973         var cls = 'x-combo-list';
18974
18975         this.list = new Roo.Layer({
18976             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18977         });
18978
18979         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18980         this.list.setWidth(lw);
18981         this.list.swallowEvent('mousewheel');
18982         this.assetHeight = 0;
18983
18984         if(this.title){
18985             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18986             this.assetHeight += this.header.getHeight();
18987         }
18988
18989         this.innerList = this.list.createChild({cls:cls+'-inner'});
18990         this.innerList.on('mouseover', this.onViewOver, this);
18991         this.innerList.on('mousemove', this.onViewMove, this);
18992         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18993         
18994         if(this.allowBlank && !this.pageSize && !this.disableClear){
18995             this.footer = this.list.createChild({cls:cls+'-ft'});
18996             this.pageTb = new Roo.Toolbar(this.footer);
18997            
18998         }
18999         if(this.pageSize){
19000             this.footer = this.list.createChild({cls:cls+'-ft'});
19001             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
19002                     {pageSize: this.pageSize});
19003             
19004         }
19005         
19006         if (this.pageTb && this.allowBlank && !this.disableClear) {
19007             var _this = this;
19008             this.pageTb.add(new Roo.Toolbar.Fill(), {
19009                 cls: 'x-btn-icon x-btn-clear',
19010                 text: '&#160;',
19011                 handler: function()
19012                 {
19013                     _this.collapse();
19014                     _this.clearValue();
19015                     _this.onSelect(false, -1);
19016                 }
19017             });
19018         }
19019         if (this.footer) {
19020             this.assetHeight += this.footer.getHeight();
19021         }
19022         
19023
19024         if(!this.tpl){
19025             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19026         }
19027
19028         this.view = new Roo.View(this.innerList, this.tpl, {
19029             singleSelect:true,
19030             store: this.store,
19031             selectedClass: this.selectedClass
19032         });
19033
19034         this.view.on('click', this.onViewClick, this);
19035
19036         this.store.on('beforeload', this.onBeforeLoad, this);
19037         this.store.on('load', this.onLoad, this);
19038         this.store.on('loadexception', this.onLoadException, this);
19039
19040         if(this.resizable){
19041             this.resizer = new Roo.Resizable(this.list,  {
19042                pinned:true, handles:'se'
19043             });
19044             this.resizer.on('resize', function(r, w, h){
19045                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19046                 this.listWidth = w;
19047                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19048                 this.restrictHeight();
19049             }, this);
19050             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19051         }
19052         if(!this.editable){
19053             this.editable = true;
19054             this.setEditable(false);
19055         }  
19056         
19057         
19058         if (typeof(this.events.add.listeners) != 'undefined') {
19059             
19060             this.addicon = this.wrap.createChild(
19061                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19062        
19063             this.addicon.on('click', function(e) {
19064                 this.fireEvent('add', this);
19065             }, this);
19066         }
19067         if (typeof(this.events.edit.listeners) != 'undefined') {
19068             
19069             this.editicon = this.wrap.createChild(
19070                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19071             if (this.addicon) {
19072                 this.editicon.setStyle('margin-left', '40px');
19073             }
19074             this.editicon.on('click', function(e) {
19075                 
19076                 // we fire even  if inothing is selected..
19077                 this.fireEvent('edit', this, this.lastData );
19078                 
19079             }, this);
19080         }
19081         
19082         
19083         
19084     },
19085
19086     // private
19087     initEvents : function(){
19088         Roo.form.ComboBox.superclass.initEvents.call(this);
19089
19090         this.keyNav = new Roo.KeyNav(this.el, {
19091             "up" : function(e){
19092                 this.inKeyMode = true;
19093                 this.selectPrev();
19094             },
19095
19096             "down" : function(e){
19097                 if(!this.isExpanded()){
19098                     this.onTriggerClick();
19099                 }else{
19100                     this.inKeyMode = true;
19101                     this.selectNext();
19102                 }
19103             },
19104
19105             "enter" : function(e){
19106                 this.onViewClick();
19107                 //return true;
19108             },
19109
19110             "esc" : function(e){
19111                 this.collapse();
19112             },
19113
19114             "tab" : function(e){
19115                 this.onViewClick(false);
19116                 this.fireEvent("specialkey", this, e);
19117                 return true;
19118             },
19119
19120             scope : this,
19121
19122             doRelay : function(foo, bar, hname){
19123                 if(hname == 'down' || this.scope.isExpanded()){
19124                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19125                 }
19126                 return true;
19127             },
19128
19129             forceKeyDown: true
19130         });
19131         this.queryDelay = Math.max(this.queryDelay || 10,
19132                 this.mode == 'local' ? 10 : 250);
19133         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19134         if(this.typeAhead){
19135             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19136         }
19137         if(this.editable !== false){
19138             this.el.on("keyup", this.onKeyUp, this);
19139         }
19140         if(this.forceSelection){
19141             this.on('blur', this.doForce, this);
19142         }
19143     },
19144
19145     onDestroy : function(){
19146         if(this.view){
19147             this.view.setStore(null);
19148             this.view.el.removeAllListeners();
19149             this.view.el.remove();
19150             this.view.purgeListeners();
19151         }
19152         if(this.list){
19153             this.list.destroy();
19154         }
19155         if(this.store){
19156             this.store.un('beforeload', this.onBeforeLoad, this);
19157             this.store.un('load', this.onLoad, this);
19158             this.store.un('loadexception', this.onLoadException, this);
19159         }
19160         Roo.form.ComboBox.superclass.onDestroy.call(this);
19161     },
19162
19163     // private
19164     fireKey : function(e){
19165         if(e.isNavKeyPress() && !this.list.isVisible()){
19166             this.fireEvent("specialkey", this, e);
19167         }
19168     },
19169
19170     // private
19171     onResize: function(w, h){
19172         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19173         
19174         if(typeof w != 'number'){
19175             // we do not handle it!?!?
19176             return;
19177         }
19178         var tw = this.trigger.getWidth();
19179         tw += this.addicon ? this.addicon.getWidth() : 0;
19180         tw += this.editicon ? this.editicon.getWidth() : 0;
19181         var x = w - tw;
19182         this.el.setWidth( this.adjustWidth('input', x));
19183             
19184         this.trigger.setStyle('left', x+'px');
19185         
19186         if(this.list && this.listWidth === undefined){
19187             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19188             this.list.setWidth(lw);
19189             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19190         }
19191         
19192     
19193         
19194     },
19195
19196     /**
19197      * Allow or prevent the user from directly editing the field text.  If false is passed,
19198      * the user will only be able to select from the items defined in the dropdown list.  This method
19199      * is the runtime equivalent of setting the 'editable' config option at config time.
19200      * @param {Boolean} value True to allow the user to directly edit the field text
19201      */
19202     setEditable : function(value){
19203         if(value == this.editable){
19204             return;
19205         }
19206         this.editable = value;
19207         if(!value){
19208             this.el.dom.setAttribute('readOnly', true);
19209             this.el.on('mousedown', this.onTriggerClick,  this);
19210             this.el.addClass('x-combo-noedit');
19211         }else{
19212             this.el.dom.setAttribute('readOnly', false);
19213             this.el.un('mousedown', this.onTriggerClick,  this);
19214             this.el.removeClass('x-combo-noedit');
19215         }
19216     },
19217
19218     // private
19219     onBeforeLoad : function(){
19220         if(!this.hasFocus){
19221             return;
19222         }
19223         this.innerList.update(this.loadingText ?
19224                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19225         this.restrictHeight();
19226         this.selectedIndex = -1;
19227     },
19228
19229     // private
19230     onLoad : function(){
19231         if(!this.hasFocus){
19232             return;
19233         }
19234         if(this.store.getCount() > 0){
19235             this.expand();
19236             this.restrictHeight();
19237             if(this.lastQuery == this.allQuery){
19238                 if(this.editable){
19239                     this.el.dom.select();
19240                 }
19241                 if(!this.selectByValue(this.value, true)){
19242                     this.select(0, true);
19243                 }
19244             }else{
19245                 this.selectNext();
19246                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19247                     this.taTask.delay(this.typeAheadDelay);
19248                 }
19249             }
19250         }else{
19251             this.onEmptyResults();
19252         }
19253         //this.el.focus();
19254     },
19255     // private
19256     onLoadException : function()
19257     {
19258         this.collapse();
19259         Roo.log(this.store.reader.jsonData);
19260         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19261             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19262         }
19263         
19264         
19265     },
19266     // private
19267     onTypeAhead : function(){
19268         if(this.store.getCount() > 0){
19269             var r = this.store.getAt(0);
19270             var newValue = r.data[this.displayField];
19271             var len = newValue.length;
19272             var selStart = this.getRawValue().length;
19273             if(selStart != len){
19274                 this.setRawValue(newValue);
19275                 this.selectText(selStart, newValue.length);
19276             }
19277         }
19278     },
19279
19280     // private
19281     onSelect : function(record, index){
19282         if(this.fireEvent('beforeselect', this, record, index) !== false){
19283             this.setFromData(index > -1 ? record.data : false);
19284             this.collapse();
19285             this.fireEvent('select', this, record, index);
19286         }
19287     },
19288
19289     /**
19290      * Returns the currently selected field value or empty string if no value is set.
19291      * @return {String} value The selected value
19292      */
19293     getValue : function(){
19294         if(this.valueField){
19295             return typeof this.value != 'undefined' ? this.value : '';
19296         }
19297         return Roo.form.ComboBox.superclass.getValue.call(this);
19298     },
19299
19300     /**
19301      * Clears any text/value currently set in the field
19302      */
19303     clearValue : function(){
19304         if(this.hiddenField){
19305             this.hiddenField.value = '';
19306         }
19307         this.value = '';
19308         this.setRawValue('');
19309         this.lastSelectionText = '';
19310         
19311     },
19312
19313     /**
19314      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19315      * will be displayed in the field.  If the value does not match the data value of an existing item,
19316      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19317      * Otherwise the field will be blank (although the value will still be set).
19318      * @param {String} value The value to match
19319      */
19320     setValue : function(v){
19321         var text = v;
19322         if(this.valueField){
19323             var r = this.findRecord(this.valueField, v);
19324             if(r){
19325                 text = r.data[this.displayField];
19326             }else if(this.valueNotFoundText !== undefined){
19327                 text = this.valueNotFoundText;
19328             }
19329         }
19330         this.lastSelectionText = text;
19331         if(this.hiddenField){
19332             this.hiddenField.value = v;
19333         }
19334         Roo.form.ComboBox.superclass.setValue.call(this, text);
19335         this.value = v;
19336     },
19337     /**
19338      * @property {Object} the last set data for the element
19339      */
19340     
19341     lastData : false,
19342     /**
19343      * Sets the value of the field based on a object which is related to the record format for the store.
19344      * @param {Object} value the value to set as. or false on reset?
19345      */
19346     setFromData : function(o){
19347         var dv = ''; // display value
19348         var vv = ''; // value value..
19349         this.lastData = o;
19350         if (this.displayField) {
19351             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19352         } else {
19353             // this is an error condition!!!
19354             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19355         }
19356         
19357         if(this.valueField){
19358             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19359         }
19360         if(this.hiddenField){
19361             this.hiddenField.value = vv;
19362             
19363             this.lastSelectionText = dv;
19364             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19365             this.value = vv;
19366             return;
19367         }
19368         // no hidden field.. - we store the value in 'value', but still display
19369         // display field!!!!
19370         this.lastSelectionText = dv;
19371         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19372         this.value = vv;
19373         
19374         
19375     },
19376     // private
19377     reset : function(){
19378         // overridden so that last data is reset..
19379         this.setValue(this.resetValue);
19380         this.originalValue = this.getValue();
19381         this.clearInvalid();
19382         this.lastData = false;
19383         if (this.view) {
19384             this.view.clearSelections();
19385         }
19386     },
19387     // private
19388     findRecord : function(prop, value){
19389         var record;
19390         if(this.store.getCount() > 0){
19391             this.store.each(function(r){
19392                 if(r.data[prop] == value){
19393                     record = r;
19394                     return false;
19395                 }
19396                 return true;
19397             });
19398         }
19399         return record;
19400     },
19401     
19402     getName: function()
19403     {
19404         // returns hidden if it's set..
19405         if (!this.rendered) {return ''};
19406         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19407         
19408     },
19409     // private
19410     onViewMove : function(e, t){
19411         this.inKeyMode = false;
19412     },
19413
19414     // private
19415     onViewOver : function(e, t){
19416         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19417             return;
19418         }
19419         var item = this.view.findItemFromChild(t);
19420         if(item){
19421             var index = this.view.indexOf(item);
19422             this.select(index, false);
19423         }
19424     },
19425
19426     // private
19427     onViewClick : function(doFocus)
19428     {
19429         var index = this.view.getSelectedIndexes()[0];
19430         var r = this.store.getAt(index);
19431         if(r){
19432             this.onSelect(r, index);
19433         }
19434         if(doFocus !== false && !this.blockFocus){
19435             this.el.focus();
19436         }
19437     },
19438
19439     // private
19440     restrictHeight : function(){
19441         this.innerList.dom.style.height = '';
19442         var inner = this.innerList.dom;
19443         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19444         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19445         this.list.beginUpdate();
19446         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19447         this.list.alignTo(this.el, this.listAlign);
19448         this.list.endUpdate();
19449     },
19450
19451     // private
19452     onEmptyResults : function(){
19453         this.collapse();
19454     },
19455
19456     /**
19457      * Returns true if the dropdown list is expanded, else false.
19458      */
19459     isExpanded : function(){
19460         return this.list.isVisible();
19461     },
19462
19463     /**
19464      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19465      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19466      * @param {String} value The data value of the item to select
19467      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19468      * selected item if it is not currently in view (defaults to true)
19469      * @return {Boolean} True if the value matched an item in the list, else false
19470      */
19471     selectByValue : function(v, scrollIntoView){
19472         if(v !== undefined && v !== null){
19473             var r = this.findRecord(this.valueField || this.displayField, v);
19474             if(r){
19475                 this.select(this.store.indexOf(r), scrollIntoView);
19476                 return true;
19477             }
19478         }
19479         return false;
19480     },
19481
19482     /**
19483      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19484      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19485      * @param {Number} index The zero-based index of the list item to select
19486      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19487      * selected item if it is not currently in view (defaults to true)
19488      */
19489     select : function(index, scrollIntoView){
19490         this.selectedIndex = index;
19491         this.view.select(index);
19492         if(scrollIntoView !== false){
19493             var el = this.view.getNode(index);
19494             if(el){
19495                 this.innerList.scrollChildIntoView(el, false);
19496             }
19497         }
19498     },
19499
19500     // private
19501     selectNext : function(){
19502         var ct = this.store.getCount();
19503         if(ct > 0){
19504             if(this.selectedIndex == -1){
19505                 this.select(0);
19506             }else if(this.selectedIndex < ct-1){
19507                 this.select(this.selectedIndex+1);
19508             }
19509         }
19510     },
19511
19512     // private
19513     selectPrev : function(){
19514         var ct = this.store.getCount();
19515         if(ct > 0){
19516             if(this.selectedIndex == -1){
19517                 this.select(0);
19518             }else if(this.selectedIndex != 0){
19519                 this.select(this.selectedIndex-1);
19520             }
19521         }
19522     },
19523
19524     // private
19525     onKeyUp : function(e){
19526         if(this.editable !== false && !e.isSpecialKey()){
19527             this.lastKey = e.getKey();
19528             this.dqTask.delay(this.queryDelay);
19529         }
19530     },
19531
19532     // private
19533     validateBlur : function(){
19534         return !this.list || !this.list.isVisible();   
19535     },
19536
19537     // private
19538     initQuery : function(){
19539         this.doQuery(this.getRawValue());
19540     },
19541
19542     // private
19543     doForce : function(){
19544         if(this.el.dom.value.length > 0){
19545             this.el.dom.value =
19546                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19547              
19548         }
19549     },
19550
19551     /**
19552      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19553      * query allowing the query action to be canceled if needed.
19554      * @param {String} query The SQL query to execute
19555      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19556      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19557      * saved in the current store (defaults to false)
19558      */
19559     doQuery : function(q, forceAll){
19560         if(q === undefined || q === null){
19561             q = '';
19562         }
19563         var qe = {
19564             query: q,
19565             forceAll: forceAll,
19566             combo: this,
19567             cancel:false
19568         };
19569         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19570             return false;
19571         }
19572         q = qe.query;
19573         forceAll = qe.forceAll;
19574         if(forceAll === true || (q.length >= this.minChars)){
19575             if(this.lastQuery != q || this.alwaysQuery){
19576                 this.lastQuery = q;
19577                 if(this.mode == 'local'){
19578                     this.selectedIndex = -1;
19579                     if(forceAll){
19580                         this.store.clearFilter();
19581                     }else{
19582                         this.store.filter(this.displayField, q);
19583                     }
19584                     this.onLoad();
19585                 }else{
19586                     this.store.baseParams[this.queryParam] = q;
19587                     this.store.load({
19588                         params: this.getParams(q)
19589                     });
19590                     this.expand();
19591                 }
19592             }else{
19593                 this.selectedIndex = -1;
19594                 this.onLoad();   
19595             }
19596         }
19597     },
19598
19599     // private
19600     getParams : function(q){
19601         var p = {};
19602         //p[this.queryParam] = q;
19603         if(this.pageSize){
19604             p.start = 0;
19605             p.limit = this.pageSize;
19606         }
19607         return p;
19608     },
19609
19610     /**
19611      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19612      */
19613     collapse : function(){
19614         if(!this.isExpanded()){
19615             return;
19616         }
19617         this.list.hide();
19618         Roo.get(document).un('mousedown', this.collapseIf, this);
19619         Roo.get(document).un('mousewheel', this.collapseIf, this);
19620         if (!this.editable) {
19621             Roo.get(document).un('keydown', this.listKeyPress, this);
19622         }
19623         this.fireEvent('collapse', this);
19624     },
19625
19626     // private
19627     collapseIf : function(e){
19628         if(!e.within(this.wrap) && !e.within(this.list)){
19629             this.collapse();
19630         }
19631     },
19632
19633     /**
19634      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19635      */
19636     expand : function(){
19637         if(this.isExpanded() || !this.hasFocus){
19638             return;
19639         }
19640         this.list.alignTo(this.el, this.listAlign);
19641         this.list.show();
19642         Roo.get(document).on('mousedown', this.collapseIf, this);
19643         Roo.get(document).on('mousewheel', this.collapseIf, this);
19644         if (!this.editable) {
19645             Roo.get(document).on('keydown', this.listKeyPress, this);
19646         }
19647         
19648         this.fireEvent('expand', this);
19649     },
19650
19651     // private
19652     // Implements the default empty TriggerField.onTriggerClick function
19653     onTriggerClick : function(){
19654         if(this.disabled){
19655             return;
19656         }
19657         if(this.isExpanded()){
19658             this.collapse();
19659             if (!this.blockFocus) {
19660                 this.el.focus();
19661             }
19662             
19663         }else {
19664             this.hasFocus = true;
19665             if(this.triggerAction == 'all') {
19666                 this.doQuery(this.allQuery, true);
19667             } else {
19668                 this.doQuery(this.getRawValue());
19669             }
19670             if (!this.blockFocus) {
19671                 this.el.focus();
19672             }
19673         }
19674     },
19675     listKeyPress : function(e)
19676     {
19677         //Roo.log('listkeypress');
19678         // scroll to first matching element based on key pres..
19679         if (e.isSpecialKey()) {
19680             return false;
19681         }
19682         var k = String.fromCharCode(e.getKey()).toUpperCase();
19683         //Roo.log(k);
19684         var match  = false;
19685         var csel = this.view.getSelectedNodes();
19686         var cselitem = false;
19687         if (csel.length) {
19688             var ix = this.view.indexOf(csel[0]);
19689             cselitem  = this.store.getAt(ix);
19690             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19691                 cselitem = false;
19692             }
19693             
19694         }
19695         
19696         this.store.each(function(v) { 
19697             if (cselitem) {
19698                 // start at existing selection.
19699                 if (cselitem.id == v.id) {
19700                     cselitem = false;
19701                 }
19702                 return;
19703             }
19704                 
19705             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19706                 match = this.store.indexOf(v);
19707                 return false;
19708             }
19709         }, this);
19710         
19711         if (match === false) {
19712             return true; // no more action?
19713         }
19714         // scroll to?
19715         this.view.select(match);
19716         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19717         sn.scrollIntoView(sn.dom.parentNode, false);
19718     } 
19719
19720     /** 
19721     * @cfg {Boolean} grow 
19722     * @hide 
19723     */
19724     /** 
19725     * @cfg {Number} growMin 
19726     * @hide 
19727     */
19728     /** 
19729     * @cfg {Number} growMax 
19730     * @hide 
19731     */
19732     /**
19733      * @hide
19734      * @method autoSize
19735      */
19736 });/*
19737  * Copyright(c) 2010-2012, Roo J Solutions Limited
19738  *
19739  * Licence LGPL
19740  *
19741  */
19742
19743 /**
19744  * @class Roo.form.ComboBoxArray
19745  * @extends Roo.form.TextField
19746  * A facebook style adder... for lists of email / people / countries  etc...
19747  * pick multiple items from a combo box, and shows each one.
19748  *
19749  *  Fred [x]  Brian [x]  [Pick another |v]
19750  *
19751  *
19752  *  For this to work: it needs various extra information
19753  *    - normal combo problay has
19754  *      name, hiddenName
19755  *    + displayField, valueField
19756  *
19757  *    For our purpose...
19758  *
19759  *
19760  *   If we change from 'extends' to wrapping...
19761  *   
19762  *  
19763  *
19764  
19765  
19766  * @constructor
19767  * Create a new ComboBoxArray.
19768  * @param {Object} config Configuration options
19769  */
19770  
19771
19772 Roo.form.ComboBoxArray = function(config)
19773 {
19774     this.addEvents({
19775         /**
19776          * @event beforeremove
19777          * Fires before remove the value from the list
19778              * @param {Roo.form.ComboBoxArray} _self This combo box array
19779              * @param {Roo.form.ComboBoxArray.Item} item removed item
19780              */
19781         'beforeremove' : true,
19782         /**
19783          * @event remove
19784          * Fires when remove the value from the list
19785              * @param {Roo.form.ComboBoxArray} _self This combo box array
19786              * @param {Roo.form.ComboBoxArray.Item} item removed item
19787              */
19788         'remove' : true
19789         
19790         
19791     });
19792     
19793     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19794     
19795     this.items = new Roo.util.MixedCollection(false);
19796     
19797     // construct the child combo...
19798     
19799     
19800     
19801     
19802    
19803     
19804 }
19805
19806  
19807 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19808
19809     /**
19810      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19811      */
19812     
19813     lastData : false,
19814     
19815     // behavies liek a hiddne field
19816     inputType:      'hidden',
19817     /**
19818      * @cfg {Number} width The width of the box that displays the selected element
19819      */ 
19820     width:          300,
19821
19822     
19823     
19824     /**
19825      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19826      */
19827     name : false,
19828     /**
19829      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19830      */
19831     hiddenName : false,
19832     
19833     
19834     // private the array of items that are displayed..
19835     items  : false,
19836     // private - the hidden field el.
19837     hiddenEl : false,
19838     // private - the filed el..
19839     el : false,
19840     
19841     //validateValue : function() { return true; }, // all values are ok!
19842     //onAddClick: function() { },
19843     
19844     onRender : function(ct, position) 
19845     {
19846         
19847         // create the standard hidden element
19848         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19849         
19850         
19851         // give fake names to child combo;
19852         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19853         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19854         
19855         this.combo = Roo.factory(this.combo, Roo.form);
19856         this.combo.onRender(ct, position);
19857         if (typeof(this.combo.width) != 'undefined') {
19858             this.combo.onResize(this.combo.width,0);
19859         }
19860         
19861         this.combo.initEvents();
19862         
19863         // assigned so form know we need to do this..
19864         this.store          = this.combo.store;
19865         this.valueField     = this.combo.valueField;
19866         this.displayField   = this.combo.displayField ;
19867         
19868         
19869         this.combo.wrap.addClass('x-cbarray-grp');
19870         
19871         var cbwrap = this.combo.wrap.createChild(
19872             {tag: 'div', cls: 'x-cbarray-cb'},
19873             this.combo.el.dom
19874         );
19875         
19876              
19877         this.hiddenEl = this.combo.wrap.createChild({
19878             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19879         });
19880         this.el = this.combo.wrap.createChild({
19881             tag: 'input',  type:'hidden' , name: this.name, value : ''
19882         });
19883          //   this.el.dom.removeAttribute("name");
19884         
19885         
19886         this.outerWrap = this.combo.wrap;
19887         this.wrap = cbwrap;
19888         
19889         this.outerWrap.setWidth(this.width);
19890         this.outerWrap.dom.removeChild(this.el.dom);
19891         
19892         this.wrap.dom.appendChild(this.el.dom);
19893         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19894         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19895         
19896         this.combo.trigger.setStyle('position','relative');
19897         this.combo.trigger.setStyle('left', '0px');
19898         this.combo.trigger.setStyle('top', '2px');
19899         
19900         this.combo.el.setStyle('vertical-align', 'text-bottom');
19901         
19902         //this.trigger.setStyle('vertical-align', 'top');
19903         
19904         // this should use the code from combo really... on('add' ....)
19905         if (this.adder) {
19906             
19907         
19908             this.adder = this.outerWrap.createChild(
19909                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19910             var _t = this;
19911             this.adder.on('click', function(e) {
19912                 _t.fireEvent('adderclick', this, e);
19913             }, _t);
19914         }
19915         //var _t = this;
19916         //this.adder.on('click', this.onAddClick, _t);
19917         
19918         
19919         this.combo.on('select', function(cb, rec, ix) {
19920             this.addItem(rec.data);
19921             
19922             cb.setValue('');
19923             cb.el.dom.value = '';
19924             //cb.lastData = rec.data;
19925             // add to list
19926             
19927         }, this);
19928         
19929         
19930     },
19931     
19932     
19933     getName: function()
19934     {
19935         // returns hidden if it's set..
19936         if (!this.rendered) {return ''};
19937         return  this.hiddenName ? this.hiddenName : this.name;
19938         
19939     },
19940     
19941     
19942     onResize: function(w, h){
19943         
19944         return;
19945         // not sure if this is needed..
19946         //this.combo.onResize(w,h);
19947         
19948         if(typeof w != 'number'){
19949             // we do not handle it!?!?
19950             return;
19951         }
19952         var tw = this.combo.trigger.getWidth();
19953         tw += this.addicon ? this.addicon.getWidth() : 0;
19954         tw += this.editicon ? this.editicon.getWidth() : 0;
19955         var x = w - tw;
19956         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19957             
19958         this.combo.trigger.setStyle('left', '0px');
19959         
19960         if(this.list && this.listWidth === undefined){
19961             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19962             this.list.setWidth(lw);
19963             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19964         }
19965         
19966     
19967         
19968     },
19969     
19970     addItem: function(rec)
19971     {
19972         var valueField = this.combo.valueField;
19973         var displayField = this.combo.displayField;
19974         
19975         if (this.items.indexOfKey(rec[valueField]) > -1) {
19976             //console.log("GOT " + rec.data.id);
19977             return;
19978         }
19979         
19980         var x = new Roo.form.ComboBoxArray.Item({
19981             //id : rec[this.idField],
19982             data : rec,
19983             displayField : displayField ,
19984             tipField : displayField ,
19985             cb : this
19986         });
19987         // use the 
19988         this.items.add(rec[valueField],x);
19989         // add it before the element..
19990         this.updateHiddenEl();
19991         x.render(this.outerWrap, this.wrap.dom);
19992         // add the image handler..
19993     },
19994     
19995     updateHiddenEl : function()
19996     {
19997         this.validate();
19998         if (!this.hiddenEl) {
19999             return;
20000         }
20001         var ar = [];
20002         var idField = this.combo.valueField;
20003         
20004         this.items.each(function(f) {
20005             ar.push(f.data[idField]);
20006         });
20007         this.hiddenEl.dom.value = ar.join(',');
20008         this.validate();
20009     },
20010     
20011     reset : function()
20012     {
20013         this.items.clear();
20014         
20015         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
20016            el.remove();
20017         });
20018         
20019         this.el.dom.value = '';
20020         if (this.hiddenEl) {
20021             this.hiddenEl.dom.value = '';
20022         }
20023         
20024     },
20025     getValue: function()
20026     {
20027         return this.hiddenEl ? this.hiddenEl.dom.value : '';
20028     },
20029     setValue: function(v) // not a valid action - must use addItems..
20030     {
20031         
20032         this.reset();
20033          
20034         if (this.store.isLocal && (typeof(v) == 'string')) {
20035             // then we can use the store to find the values..
20036             // comma seperated at present.. this needs to allow JSON based encoding..
20037             this.hiddenEl.value  = v;
20038             var v_ar = [];
20039             Roo.each(v.split(','), function(k) {
20040                 Roo.log("CHECK " + this.valueField + ',' + k);
20041                 var li = this.store.query(this.valueField, k);
20042                 if (!li.length) {
20043                     return;
20044                 }
20045                 var add = {};
20046                 add[this.valueField] = k;
20047                 add[this.displayField] = li.item(0).data[this.displayField];
20048                 
20049                 this.addItem(add);
20050             }, this) 
20051              
20052         }
20053         if (typeof(v) == 'object' ) {
20054             // then let's assume it's an array of objects..
20055             Roo.each(v, function(l) {
20056                 this.addItem(l);
20057             }, this);
20058              
20059         }
20060         
20061         
20062     },
20063     setFromData: function(v)
20064     {
20065         // this recieves an object, if setValues is called.
20066         this.reset();
20067         this.el.dom.value = v[this.displayField];
20068         this.hiddenEl.dom.value = v[this.valueField];
20069         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20070             return;
20071         }
20072         var kv = v[this.valueField];
20073         var dv = v[this.displayField];
20074         kv = typeof(kv) != 'string' ? '' : kv;
20075         dv = typeof(dv) != 'string' ? '' : dv;
20076         
20077         
20078         var keys = kv.split(',');
20079         var display = dv.split(',');
20080         for (var i = 0 ; i < keys.length; i++) {
20081             
20082             add = {};
20083             add[this.valueField] = keys[i];
20084             add[this.displayField] = display[i];
20085             this.addItem(add);
20086         }
20087       
20088         
20089     },
20090     
20091     /**
20092      * Validates the combox array value
20093      * @return {Boolean} True if the value is valid, else false
20094      */
20095     validate : function(){
20096         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20097             this.clearInvalid();
20098             return true;
20099         }
20100         return false;
20101     },
20102     
20103     validateValue : function(value){
20104         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20105         
20106     },
20107     
20108     /*@
20109      * overide
20110      * 
20111      */
20112     isDirty : function() {
20113         if(this.disabled) {
20114             return false;
20115         }
20116         
20117         try {
20118             var d = Roo.decode(String(this.originalValue));
20119         } catch (e) {
20120             return String(this.getValue()) !== String(this.originalValue);
20121         }
20122         
20123         var originalValue = [];
20124         
20125         for (var i = 0; i < d.length; i++){
20126             originalValue.push(d[i][this.valueField]);
20127         }
20128         
20129         return String(this.getValue()) !== String(originalValue.join(','));
20130         
20131     }
20132     
20133 });
20134
20135
20136
20137 /**
20138  * @class Roo.form.ComboBoxArray.Item
20139  * @extends Roo.BoxComponent
20140  * A selected item in the list
20141  *  Fred [x]  Brian [x]  [Pick another |v]
20142  * 
20143  * @constructor
20144  * Create a new item.
20145  * @param {Object} config Configuration options
20146  */
20147  
20148 Roo.form.ComboBoxArray.Item = function(config) {
20149     config.id = Roo.id();
20150     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20151 }
20152
20153 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20154     data : {},
20155     cb: false,
20156     displayField : false,
20157     tipField : false,
20158     
20159     
20160     defaultAutoCreate : {
20161         tag: 'div',
20162         cls: 'x-cbarray-item',
20163         cn : [ 
20164             { tag: 'div' },
20165             {
20166                 tag: 'img',
20167                 width:16,
20168                 height : 16,
20169                 src : Roo.BLANK_IMAGE_URL ,
20170                 align: 'center'
20171             }
20172         ]
20173         
20174     },
20175     
20176  
20177     onRender : function(ct, position)
20178     {
20179         Roo.form.Field.superclass.onRender.call(this, ct, position);
20180         
20181         if(!this.el){
20182             var cfg = this.getAutoCreate();
20183             this.el = ct.createChild(cfg, position);
20184         }
20185         
20186         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20187         
20188         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20189             this.cb.renderer(this.data) :
20190             String.format('{0}',this.data[this.displayField]);
20191         
20192             
20193         this.el.child('div').dom.setAttribute('qtip',
20194                         String.format('{0}',this.data[this.tipField])
20195         );
20196         
20197         this.el.child('img').on('click', this.remove, this);
20198         
20199     },
20200    
20201     remove : function()
20202     {
20203         if(this.cb.disabled){
20204             return;
20205         }
20206         
20207         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20208             this.cb.items.remove(this);
20209             this.el.child('img').un('click', this.remove, this);
20210             this.el.remove();
20211             this.cb.updateHiddenEl();
20212
20213             this.cb.fireEvent('remove', this.cb, this);
20214         }
20215         
20216     }
20217 });/*
20218  * RooJS Library 1.1.1
20219  * Copyright(c) 2008-2011  Alan Knowles
20220  *
20221  * License - LGPL
20222  */
20223  
20224
20225 /**
20226  * @class Roo.form.ComboNested
20227  * @extends Roo.form.ComboBox
20228  * A combobox for that allows selection of nested items in a list,
20229  * eg.
20230  *
20231  *  Book
20232  *    -> red
20233  *    -> green
20234  *  Table
20235  *    -> square
20236  *      ->red
20237  *      ->green
20238  *    -> rectangle
20239  *      ->green
20240  *      
20241  * 
20242  * @constructor
20243  * Create a new ComboNested
20244  * @param {Object} config Configuration options
20245  */
20246 Roo.form.ComboNested = function(config){
20247     Roo.form.ComboCheck.superclass.constructor.call(this, config);
20248     // should verify some data...
20249     // like
20250     // hiddenName = required..
20251     // displayField = required
20252     // valudField == required
20253     var req= [ 'hiddenName', 'displayField', 'valueField' ];
20254     var _t = this;
20255     Roo.each(req, function(e) {
20256         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20257             throw "Roo.form.ComboNested : missing value for: " + e;
20258         }
20259     });
20260      
20261     
20262 };
20263
20264 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20265    
20266     /*
20267      * @config {Number} max Number of columns to show
20268      */
20269     
20270     maxColumns : 3,
20271    
20272     list : null, // the outermost div..
20273     innerLists : null, // the
20274     views : null,
20275     stores : null,
20276     // private
20277     onRender : function(ct, position)
20278     {
20279         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20280         
20281         if(this.hiddenName){
20282             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
20283                     'before', true);
20284             this.hiddenField.value =
20285                 this.hiddenValue !== undefined ? this.hiddenValue :
20286                 this.value !== undefined ? this.value : '';
20287
20288             // prevent input submission
20289             this.el.dom.removeAttribute('name');
20290              
20291              
20292         }
20293         
20294         if(Roo.isGecko){
20295             this.el.dom.setAttribute('autocomplete', 'off');
20296         }
20297
20298         var cls = 'x-combo-list';
20299
20300         this.list = new Roo.Layer({
20301             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20302         });
20303
20304         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20305         this.list.setWidth(lw);
20306         this.list.swallowEvent('mousewheel');
20307         this.assetHeight = 0;
20308
20309         if(this.title){
20310             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20311             this.assetHeight += this.header.getHeight();
20312         }
20313         this.innerLists = [];
20314         this.views = [];
20315         this.stores = [];
20316         for (var i =0 ; i < this.maxColumns; i++) {
20317             this.onRenderList( cls, i);
20318         }
20319         
20320         // always needs footer, as we are going to have an 'OK' button.
20321         this.footer = this.list.createChild({cls:cls+'-ft'});
20322         this.pageTb = new Roo.Toolbar(this.footer);  
20323         var _this = this;
20324         this.pageTb.add(  {
20325             
20326             text: 'Done',
20327             handler: function()
20328             {
20329                 _this.collapse();
20330             }
20331         });
20332         
20333         if ( this.allowBlank && !this.disableClear) {
20334             
20335             this.pageTb.add(new Roo.Toolbar.Fill(), {
20336                 cls: 'x-btn-icon x-btn-clear',
20337                 text: '&#160;',
20338                 handler: function()
20339                 {
20340                     _this.collapse();
20341                     _this.clearValue();
20342                     _this.onSelect(false, -1);
20343                 }
20344             });
20345         }
20346         if (this.footer) {
20347             this.assetHeight += this.footer.getHeight();
20348         }
20349         
20350     },
20351     onRenderList : function (  cls, i)
20352     {
20353         
20354         var lw = Math.floor(
20355                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20356         );
20357         
20358         this.list.setWidth(lw); // default to '1'
20359
20360         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20361         //il.on('mouseover', this.onViewOver, this, { list:  i });
20362         //il.on('mousemove', this.onViewMove, this, { list:  i });
20363         il.setWidth(lw);
20364         il.setStyle({ 'overflow-x' : 'hidden'});
20365
20366         if(!this.tpl){
20367             this.tpl = new Roo.Template({
20368                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20369                 isEmpty: function (value, allValues) {
20370                     //Roo.log(value);
20371                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20372                     return dl ? 'has-children' : 'no-children'
20373                 }
20374             });
20375         }
20376         
20377         var store  = this.store;
20378         if (i > 0) {
20379             store  = new Roo.data.SimpleStore({
20380                 //fields : this.store.reader.meta.fields,
20381                 reader : this.store.reader,
20382                 data : [ ]
20383             });
20384         }
20385         this.stores[i]  = store;
20386                 
20387         
20388         
20389         var view = this.views[i] = new Roo.View(
20390             il,
20391             this.tpl,
20392             {
20393                 singleSelect:true,
20394                 store: store,
20395                 selectedClass: this.selectedClass
20396             }
20397         );
20398         view.getEl().setWidth(lw);
20399         view.getEl().setStyle({
20400             position: i < 1 ? 'relative' : 'absolute',
20401             top: 0,
20402             left: (i * lw ) + 'px',
20403             display : i > 0 ? 'none' : 'block'
20404         });
20405         view.on('selectionchange', this.onSelectChange, this, {list : i });
20406         view.on('dblclick', this.onDoubleClick, this, {list : i });
20407         //view.on('click', this.onViewClick, this, { list : i });
20408
20409         store.on('beforeload', this.onBeforeLoad, this);
20410         store.on('load',  this.onLoad, this, { list  : i});
20411         store.on('loadexception', this.onLoadException, this);
20412
20413         // hide the other vies..
20414         
20415         
20416         
20417     },
20418     onResize : function()  {},
20419     
20420     restrictHeight : function()
20421     {
20422         var mh = 0;
20423         Roo.each(this.innerLists, function(il,i) {
20424             var el = this.views[i].getEl();
20425             el.dom.style.height = '';
20426             var inner = el.dom;
20427             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20428             // only adjust heights on other ones..
20429             if (i < 1) {
20430                 
20431                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20432                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20433                 mh = Math.max(el.getHeight(), mh);
20434             }
20435             
20436             
20437         }, this);
20438         
20439         this.list.beginUpdate();
20440         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20441         this.list.alignTo(this.el, this.listAlign);
20442         this.list.endUpdate();
20443         
20444     },
20445      
20446     
20447     // -- store handlers..
20448     // private
20449     onBeforeLoad : function()
20450     {
20451         if(!this.hasFocus){
20452             return;
20453         }
20454         this.innerLists[0].update(this.loadingText ?
20455                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20456         this.restrictHeight();
20457         this.selectedIndex = -1;
20458     },
20459     // private
20460     onLoad : function(a,b,c,d)
20461     {
20462         
20463         if(!this.hasFocus){
20464             return;
20465         }
20466         
20467         if(this.store.getCount() > 0) {
20468             this.expand();
20469             this.restrictHeight();   
20470         } else {
20471             this.onEmptyResults();
20472         }
20473         /*
20474         this.stores[1].loadData([]);
20475         this.stores[2].loadData([]);
20476         this.views
20477         */    
20478     
20479         //this.el.focus();
20480     },
20481     
20482     
20483     // private
20484     onLoadException : function()
20485     {
20486         this.collapse();
20487         Roo.log(this.store.reader.jsonData);
20488         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20489             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20490         }
20491         
20492         
20493     } ,
20494      
20495      
20496
20497     onSelectChange : function (view, sels, opts )
20498     {
20499         var ix = view.getSelectedIndexes();
20500         
20501         
20502         if (opts.list > this.maxColumns - 2) {
20503              
20504             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20505             return;
20506         }
20507         
20508         if (!ix.length) {
20509             this.setFromData({});
20510             var str = this.stores[opts.list+1];
20511             str.removeAll();
20512             return;
20513         }
20514         
20515         var rec = view.store.getAt(ix[0]);
20516         this.setFromData(rec.data);
20517         
20518         var lw = Math.floor(
20519                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20520         );
20521         var data =  typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
20522         var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
20523         this.stores[opts.list+1].loadData( data );
20524         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20525         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20526         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20527         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
20528     },
20529     onDoubleClick : function()
20530     {
20531         this.collapse(); //??
20532     },
20533     
20534      
20535     
20536     findRecord : function (prop,value)
20537     {
20538         return this.findRecordInStore(this.store, prop,value);
20539     },
20540     
20541      // private
20542     findRecordInStore : function(store, prop, value)
20543     {
20544         var cstore = new Roo.data.SimpleStore({
20545             //fields : this.store.reader.meta.fields, // we need array reader.. for
20546             reader : this.store.reader,
20547             data : [ ]
20548         });
20549         var _this = this;
20550         var record  = false;
20551         if(store.getCount() > 0){
20552            store.each(function(r){
20553                 if(r.data[prop] == value){
20554                     record = r;
20555                     return false;
20556                 }
20557                 if (r.data.cn && r.data.cn.length) {
20558                     cstore.loadData( r.data.cn);
20559                     var cret = _this.findRecordInStore(cstore, prop, value);
20560                     if (cret !== false) {
20561                         record = cret;
20562                         return false;
20563                     }
20564                 }
20565                 
20566                 return true;
20567             });
20568         }
20569         return record;
20570     }
20571     
20572     
20573     
20574     
20575 });/*
20576  * Based on:
20577  * Ext JS Library 1.1.1
20578  * Copyright(c) 2006-2007, Ext JS, LLC.
20579  *
20580  * Originally Released Under LGPL - original licence link has changed is not relivant.
20581  *
20582  * Fork - LGPL
20583  * <script type="text/javascript">
20584  */
20585 /**
20586  * @class Roo.form.Checkbox
20587  * @extends Roo.form.Field
20588  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20589  * @constructor
20590  * Creates a new Checkbox
20591  * @param {Object} config Configuration options
20592  */
20593 Roo.form.Checkbox = function(config){
20594     Roo.form.Checkbox.superclass.constructor.call(this, config);
20595     this.addEvents({
20596         /**
20597          * @event check
20598          * Fires when the checkbox is checked or unchecked.
20599              * @param {Roo.form.Checkbox} this This checkbox
20600              * @param {Boolean} checked The new checked value
20601              */
20602         check : true
20603     });
20604 };
20605
20606 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20607     /**
20608      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20609      */
20610     focusClass : undefined,
20611     /**
20612      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20613      */
20614     fieldClass: "x-form-field",
20615     /**
20616      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20617      */
20618     checked: false,
20619     /**
20620      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20621      * {tag: "input", type: "checkbox", autocomplete: "off"})
20622      */
20623     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20624     /**
20625      * @cfg {String} boxLabel The text that appears beside the checkbox
20626      */
20627     boxLabel : "",
20628     /**
20629      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20630      */  
20631     inputValue : '1',
20632     /**
20633      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20634      */
20635      valueOff: '0', // value when not checked..
20636
20637     actionMode : 'viewEl', 
20638     //
20639     // private
20640     itemCls : 'x-menu-check-item x-form-item',
20641     groupClass : 'x-menu-group-item',
20642     inputType : 'hidden',
20643     
20644     
20645     inSetChecked: false, // check that we are not calling self...
20646     
20647     inputElement: false, // real input element?
20648     basedOn: false, // ????
20649     
20650     isFormField: true, // not sure where this is needed!!!!
20651
20652     onResize : function(){
20653         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20654         if(!this.boxLabel){
20655             this.el.alignTo(this.wrap, 'c-c');
20656         }
20657     },
20658
20659     initEvents : function(){
20660         Roo.form.Checkbox.superclass.initEvents.call(this);
20661         this.el.on("click", this.onClick,  this);
20662         this.el.on("change", this.onClick,  this);
20663     },
20664
20665
20666     getResizeEl : function(){
20667         return this.wrap;
20668     },
20669
20670     getPositionEl : function(){
20671         return this.wrap;
20672     },
20673
20674     // private
20675     onRender : function(ct, position){
20676         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20677         /*
20678         if(this.inputValue !== undefined){
20679             this.el.dom.value = this.inputValue;
20680         }
20681         */
20682         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20683         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20684         var viewEl = this.wrap.createChild({ 
20685             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20686         this.viewEl = viewEl;   
20687         this.wrap.on('click', this.onClick,  this); 
20688         
20689         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20690         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20691         
20692         
20693         
20694         if(this.boxLabel){
20695             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20696         //    viewEl.on('click', this.onClick,  this); 
20697         }
20698         //if(this.checked){
20699             this.setChecked(this.checked);
20700         //}else{
20701             //this.checked = this.el.dom;
20702         //}
20703
20704     },
20705
20706     // private
20707     initValue : Roo.emptyFn,
20708
20709     /**
20710      * Returns the checked state of the checkbox.
20711      * @return {Boolean} True if checked, else false
20712      */
20713     getValue : function(){
20714         if(this.el){
20715             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20716         }
20717         return this.valueOff;
20718         
20719     },
20720
20721         // private
20722     onClick : function(){ 
20723         if (this.disabled) {
20724             return;
20725         }
20726         this.setChecked(!this.checked);
20727
20728         //if(this.el.dom.checked != this.checked){
20729         //    this.setValue(this.el.dom.checked);
20730        // }
20731     },
20732
20733     /**
20734      * Sets the checked state of the checkbox.
20735      * On is always based on a string comparison between inputValue and the param.
20736      * @param {Boolean/String} value - the value to set 
20737      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20738      */
20739     setValue : function(v,suppressEvent){
20740         
20741         
20742         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20743         //if(this.el && this.el.dom){
20744         //    this.el.dom.checked = this.checked;
20745         //    this.el.dom.defaultChecked = this.checked;
20746         //}
20747         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20748         //this.fireEvent("check", this, this.checked);
20749     },
20750     // private..
20751     setChecked : function(state,suppressEvent)
20752     {
20753         if (this.inSetChecked) {
20754             this.checked = state;
20755             return;
20756         }
20757         
20758     
20759         if(this.wrap){
20760             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20761         }
20762         this.checked = state;
20763         if(suppressEvent !== true){
20764             this.fireEvent('check', this, state);
20765         }
20766         this.inSetChecked = true;
20767         this.el.dom.value = state ? this.inputValue : this.valueOff;
20768         this.inSetChecked = false;
20769         
20770     },
20771     // handle setting of hidden value by some other method!!?!?
20772     setFromHidden: function()
20773     {
20774         if(!this.el){
20775             return;
20776         }
20777         //console.log("SET FROM HIDDEN");
20778         //alert('setFrom hidden');
20779         this.setValue(this.el.dom.value);
20780     },
20781     
20782     onDestroy : function()
20783     {
20784         if(this.viewEl){
20785             Roo.get(this.viewEl).remove();
20786         }
20787          
20788         Roo.form.Checkbox.superclass.onDestroy.call(this);
20789     },
20790     
20791     setBoxLabel : function(str)
20792     {
20793         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20794     }
20795
20796 });/*
20797  * Based on:
20798  * Ext JS Library 1.1.1
20799  * Copyright(c) 2006-2007, Ext JS, LLC.
20800  *
20801  * Originally Released Under LGPL - original licence link has changed is not relivant.
20802  *
20803  * Fork - LGPL
20804  * <script type="text/javascript">
20805  */
20806  
20807 /**
20808  * @class Roo.form.Radio
20809  * @extends Roo.form.Checkbox
20810  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20811  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20812  * @constructor
20813  * Creates a new Radio
20814  * @param {Object} config Configuration options
20815  */
20816 Roo.form.Radio = function(){
20817     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20818 };
20819 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20820     inputType: 'radio',
20821
20822     /**
20823      * If this radio is part of a group, it will return the selected value
20824      * @return {String}
20825      */
20826     getGroupValue : function(){
20827         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20828     },
20829     
20830     
20831     onRender : function(ct, position){
20832         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20833         
20834         if(this.inputValue !== undefined){
20835             this.el.dom.value = this.inputValue;
20836         }
20837          
20838         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20839         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20840         //var viewEl = this.wrap.createChild({ 
20841         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20842         //this.viewEl = viewEl;   
20843         //this.wrap.on('click', this.onClick,  this); 
20844         
20845         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20846         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20847         
20848         
20849         
20850         if(this.boxLabel){
20851             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20852         //    viewEl.on('click', this.onClick,  this); 
20853         }
20854          if(this.checked){
20855             this.el.dom.checked =   'checked' ;
20856         }
20857          
20858     } 
20859     
20860     
20861 });//<script type="text/javascript">
20862
20863 /*
20864  * Based  Ext JS Library 1.1.1
20865  * Copyright(c) 2006-2007, Ext JS, LLC.
20866  * LGPL
20867  *
20868  */
20869  
20870 /**
20871  * @class Roo.HtmlEditorCore
20872  * @extends Roo.Component
20873  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20874  *
20875  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20876  */
20877
20878 Roo.HtmlEditorCore = function(config){
20879     
20880     
20881     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20882     
20883     
20884     this.addEvents({
20885         /**
20886          * @event initialize
20887          * Fires when the editor is fully initialized (including the iframe)
20888          * @param {Roo.HtmlEditorCore} this
20889          */
20890         initialize: true,
20891         /**
20892          * @event activate
20893          * Fires when the editor is first receives the focus. Any insertion must wait
20894          * until after this event.
20895          * @param {Roo.HtmlEditorCore} this
20896          */
20897         activate: true,
20898          /**
20899          * @event beforesync
20900          * Fires before the textarea is updated with content from the editor iframe. Return false
20901          * to cancel the sync.
20902          * @param {Roo.HtmlEditorCore} this
20903          * @param {String} html
20904          */
20905         beforesync: true,
20906          /**
20907          * @event beforepush
20908          * Fires before the iframe editor is updated with content from the textarea. Return false
20909          * to cancel the push.
20910          * @param {Roo.HtmlEditorCore} this
20911          * @param {String} html
20912          */
20913         beforepush: true,
20914          /**
20915          * @event sync
20916          * Fires when the textarea is updated with content from the editor iframe.
20917          * @param {Roo.HtmlEditorCore} this
20918          * @param {String} html
20919          */
20920         sync: true,
20921          /**
20922          * @event push
20923          * Fires when the iframe editor is updated with content from the textarea.
20924          * @param {Roo.HtmlEditorCore} this
20925          * @param {String} html
20926          */
20927         push: true,
20928         
20929         /**
20930          * @event editorevent
20931          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20932          * @param {Roo.HtmlEditorCore} this
20933          */
20934         editorevent: true
20935         
20936     });
20937     
20938     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20939     
20940     // defaults : white / black...
20941     this.applyBlacklists();
20942     
20943     
20944     
20945 };
20946
20947
20948 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20949
20950
20951      /**
20952      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20953      */
20954     
20955     owner : false,
20956     
20957      /**
20958      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20959      *                        Roo.resizable.
20960      */
20961     resizable : false,
20962      /**
20963      * @cfg {Number} height (in pixels)
20964      */   
20965     height: 300,
20966    /**
20967      * @cfg {Number} width (in pixels)
20968      */   
20969     width: 500,
20970     
20971     /**
20972      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20973      * 
20974      */
20975     stylesheets: false,
20976     
20977     // id of frame..
20978     frameId: false,
20979     
20980     // private properties
20981     validationEvent : false,
20982     deferHeight: true,
20983     initialized : false,
20984     activated : false,
20985     sourceEditMode : false,
20986     onFocus : Roo.emptyFn,
20987     iframePad:3,
20988     hideMode:'offsets',
20989     
20990     clearUp: true,
20991     
20992     // blacklist + whitelisted elements..
20993     black: false,
20994     white: false,
20995      
20996     bodyCls : '',
20997
20998     /**
20999      * Protected method that will not generally be called directly. It
21000      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21001      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21002      */
21003     getDocMarkup : function(){
21004         // body styles..
21005         var st = '';
21006         
21007         // inherit styels from page...?? 
21008         if (this.stylesheets === false) {
21009             
21010             Roo.get(document.head).select('style').each(function(node) {
21011                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21012             });
21013             
21014             Roo.get(document.head).select('link').each(function(node) { 
21015                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21016             });
21017             
21018         } else if (!this.stylesheets.length) {
21019                 // simple..
21020                 st = '<style type="text/css">' +
21021                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21022                    '</style>';
21023         } else { 
21024             st = '<style type="text/css">' +
21025                     this.stylesheets +
21026                 '</style>';
21027         }
21028         
21029         st +=  '<style type="text/css">' +
21030             'IMG { cursor: pointer } ' +
21031         '</style>';
21032
21033         var cls = 'roo-htmleditor-body';
21034         
21035         if(this.bodyCls.length){
21036             cls += ' ' + this.bodyCls;
21037         }
21038         
21039         return '<html><head>' + st  +
21040             //<style type="text/css">' +
21041             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21042             //'</style>' +
21043             ' </head><body class="' +  cls + '"></body></html>';
21044     },
21045
21046     // private
21047     onRender : function(ct, position)
21048     {
21049         var _t = this;
21050         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21051         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21052         
21053         
21054         this.el.dom.style.border = '0 none';
21055         this.el.dom.setAttribute('tabIndex', -1);
21056         this.el.addClass('x-hidden hide');
21057         
21058         
21059         
21060         if(Roo.isIE){ // fix IE 1px bogus margin
21061             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21062         }
21063        
21064         
21065         this.frameId = Roo.id();
21066         
21067          
21068         
21069         var iframe = this.owner.wrap.createChild({
21070             tag: 'iframe',
21071             cls: 'form-control', // bootstrap..
21072             id: this.frameId,
21073             name: this.frameId,
21074             frameBorder : 'no',
21075             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21076         }, this.el
21077         );
21078         
21079         
21080         this.iframe = iframe.dom;
21081
21082          this.assignDocWin();
21083         
21084         this.doc.designMode = 'on';
21085        
21086         this.doc.open();
21087         this.doc.write(this.getDocMarkup());
21088         this.doc.close();
21089
21090         
21091         var task = { // must defer to wait for browser to be ready
21092             run : function(){
21093                 //console.log("run task?" + this.doc.readyState);
21094                 this.assignDocWin();
21095                 if(this.doc.body || this.doc.readyState == 'complete'){
21096                     try {
21097                         this.doc.designMode="on";
21098                     } catch (e) {
21099                         return;
21100                     }
21101                     Roo.TaskMgr.stop(task);
21102                     this.initEditor.defer(10, this);
21103                 }
21104             },
21105             interval : 10,
21106             duration: 10000,
21107             scope: this
21108         };
21109         Roo.TaskMgr.start(task);
21110
21111     },
21112
21113     // private
21114     onResize : function(w, h)
21115     {
21116          Roo.log('resize: ' +w + ',' + h );
21117         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21118         if(!this.iframe){
21119             return;
21120         }
21121         if(typeof w == 'number'){
21122             
21123             this.iframe.style.width = w + 'px';
21124         }
21125         if(typeof h == 'number'){
21126             
21127             this.iframe.style.height = h + 'px';
21128             if(this.doc){
21129                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21130             }
21131         }
21132         
21133     },
21134
21135     /**
21136      * Toggles the editor between standard and source edit mode.
21137      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21138      */
21139     toggleSourceEdit : function(sourceEditMode){
21140         
21141         this.sourceEditMode = sourceEditMode === true;
21142         
21143         if(this.sourceEditMode){
21144  
21145             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21146             
21147         }else{
21148             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21149             //this.iframe.className = '';
21150             this.deferFocus();
21151         }
21152         //this.setSize(this.owner.wrap.getSize());
21153         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21154     },
21155
21156     
21157   
21158
21159     /**
21160      * Protected method that will not generally be called directly. If you need/want
21161      * custom HTML cleanup, this is the method you should override.
21162      * @param {String} html The HTML to be cleaned
21163      * return {String} The cleaned HTML
21164      */
21165     cleanHtml : function(html){
21166         html = String(html);
21167         if(html.length > 5){
21168             if(Roo.isSafari){ // strip safari nonsense
21169                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21170             }
21171         }
21172         if(html == '&nbsp;'){
21173             html = '';
21174         }
21175         return html;
21176     },
21177
21178     /**
21179      * HTML Editor -> Textarea
21180      * Protected method that will not generally be called directly. Syncs the contents
21181      * of the editor iframe with the textarea.
21182      */
21183     syncValue : function(){
21184         if(this.initialized){
21185             var bd = (this.doc.body || this.doc.documentElement);
21186             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21187             var html = bd.innerHTML;
21188             if(Roo.isSafari){
21189                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21190                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21191                 if(m && m[1]){
21192                     html = '<div style="'+m[0]+'">' + html + '</div>';
21193                 }
21194             }
21195             html = this.cleanHtml(html);
21196             // fix up the special chars.. normaly like back quotes in word...
21197             // however we do not want to do this with chinese..
21198             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21199                 
21200                 var cc = match.charCodeAt();
21201
21202                 // Get the character value, handling surrogate pairs
21203                 if (match.length == 2) {
21204                     // It's a surrogate pair, calculate the Unicode code point
21205                     var high = match.charCodeAt(0) - 0xD800;
21206                     var low  = match.charCodeAt(1) - 0xDC00;
21207                     cc = (high * 0x400) + low + 0x10000;
21208                 }  else if (
21209                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21210                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21211                     (cc >= 0xf900 && cc < 0xfb00 )
21212                 ) {
21213                         return match;
21214                 }  
21215          
21216                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21217                 return "&#" + cc + ";";
21218                 
21219                 
21220             });
21221             
21222             
21223              
21224             if(this.owner.fireEvent('beforesync', this, html) !== false){
21225                 this.el.dom.value = html;
21226                 this.owner.fireEvent('sync', this, html);
21227             }
21228         }
21229     },
21230
21231     /**
21232      * Protected method that will not generally be called directly. Pushes the value of the textarea
21233      * into the iframe editor.
21234      */
21235     pushValue : function(){
21236         if(this.initialized){
21237             var v = this.el.dom.value.trim();
21238             
21239 //            if(v.length < 1){
21240 //                v = '&#160;';
21241 //            }
21242             
21243             if(this.owner.fireEvent('beforepush', this, v) !== false){
21244                 var d = (this.doc.body || this.doc.documentElement);
21245                 d.innerHTML = v;
21246                 this.cleanUpPaste();
21247                 this.el.dom.value = d.innerHTML;
21248                 this.owner.fireEvent('push', this, v);
21249             }
21250         }
21251     },
21252
21253     // private
21254     deferFocus : function(){
21255         this.focus.defer(10, this);
21256     },
21257
21258     // doc'ed in Field
21259     focus : function(){
21260         if(this.win && !this.sourceEditMode){
21261             this.win.focus();
21262         }else{
21263             this.el.focus();
21264         }
21265     },
21266     
21267     assignDocWin: function()
21268     {
21269         var iframe = this.iframe;
21270         
21271          if(Roo.isIE){
21272             this.doc = iframe.contentWindow.document;
21273             this.win = iframe.contentWindow;
21274         } else {
21275 //            if (!Roo.get(this.frameId)) {
21276 //                return;
21277 //            }
21278 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21279 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21280             
21281             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21282                 return;
21283             }
21284             
21285             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21286             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21287         }
21288     },
21289     
21290     // private
21291     initEditor : function(){
21292         //console.log("INIT EDITOR");
21293         this.assignDocWin();
21294         
21295         
21296         
21297         this.doc.designMode="on";
21298         this.doc.open();
21299         this.doc.write(this.getDocMarkup());
21300         this.doc.close();
21301         
21302         var dbody = (this.doc.body || this.doc.documentElement);
21303         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21304         // this copies styles from the containing element into thsi one..
21305         // not sure why we need all of this..
21306         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21307         
21308         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21309         //ss['background-attachment'] = 'fixed'; // w3c
21310         dbody.bgProperties = 'fixed'; // ie
21311         //Roo.DomHelper.applyStyles(dbody, ss);
21312         Roo.EventManager.on(this.doc, {
21313             //'mousedown': this.onEditorEvent,
21314             'mouseup': this.onEditorEvent,
21315             'dblclick': this.onEditorEvent,
21316             'click': this.onEditorEvent,
21317             'keyup': this.onEditorEvent,
21318             buffer:100,
21319             scope: this
21320         });
21321         if(Roo.isGecko){
21322             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21323         }
21324         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21325             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21326         }
21327         this.initialized = true;
21328
21329         this.owner.fireEvent('initialize', this);
21330         this.pushValue();
21331     },
21332
21333     // private
21334     onDestroy : function(){
21335         
21336         
21337         
21338         if(this.rendered){
21339             
21340             //for (var i =0; i < this.toolbars.length;i++) {
21341             //    // fixme - ask toolbars for heights?
21342             //    this.toolbars[i].onDestroy();
21343            // }
21344             
21345             //this.wrap.dom.innerHTML = '';
21346             //this.wrap.remove();
21347         }
21348     },
21349
21350     // private
21351     onFirstFocus : function(){
21352         
21353         this.assignDocWin();
21354         
21355         
21356         this.activated = true;
21357          
21358     
21359         if(Roo.isGecko){ // prevent silly gecko errors
21360             this.win.focus();
21361             var s = this.win.getSelection();
21362             if(!s.focusNode || s.focusNode.nodeType != 3){
21363                 var r = s.getRangeAt(0);
21364                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21365                 r.collapse(true);
21366                 this.deferFocus();
21367             }
21368             try{
21369                 this.execCmd('useCSS', true);
21370                 this.execCmd('styleWithCSS', false);
21371             }catch(e){}
21372         }
21373         this.owner.fireEvent('activate', this);
21374     },
21375
21376     // private
21377     adjustFont: function(btn){
21378         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21379         //if(Roo.isSafari){ // safari
21380         //    adjust *= 2;
21381        // }
21382         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21383         if(Roo.isSafari){ // safari
21384             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21385             v =  (v < 10) ? 10 : v;
21386             v =  (v > 48) ? 48 : v;
21387             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21388             
21389         }
21390         
21391         
21392         v = Math.max(1, v+adjust);
21393         
21394         this.execCmd('FontSize', v  );
21395     },
21396
21397     onEditorEvent : function(e)
21398     {
21399         this.owner.fireEvent('editorevent', this, e);
21400       //  this.updateToolbar();
21401         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21402     },
21403
21404     insertTag : function(tg)
21405     {
21406         // could be a bit smarter... -> wrap the current selected tRoo..
21407         if (tg.toLowerCase() == 'span' ||
21408             tg.toLowerCase() == 'code' ||
21409             tg.toLowerCase() == 'sup' ||
21410             tg.toLowerCase() == 'sub' 
21411             ) {
21412             
21413             range = this.createRange(this.getSelection());
21414             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21415             wrappingNode.appendChild(range.extractContents());
21416             range.insertNode(wrappingNode);
21417
21418             return;
21419             
21420             
21421             
21422         }
21423         this.execCmd("formatblock",   tg);
21424         
21425     },
21426     
21427     insertText : function(txt)
21428     {
21429         
21430         
21431         var range = this.createRange();
21432         range.deleteContents();
21433                //alert(Sender.getAttribute('label'));
21434                
21435         range.insertNode(this.doc.createTextNode(txt));
21436     } ,
21437     
21438      
21439
21440     /**
21441      * Executes a Midas editor command on the editor document and performs necessary focus and
21442      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21443      * @param {String} cmd The Midas command
21444      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21445      */
21446     relayCmd : function(cmd, value){
21447         this.win.focus();
21448         this.execCmd(cmd, value);
21449         this.owner.fireEvent('editorevent', this);
21450         //this.updateToolbar();
21451         this.owner.deferFocus();
21452     },
21453
21454     /**
21455      * Executes a Midas editor command directly on the editor document.
21456      * For visual commands, you should use {@link #relayCmd} instead.
21457      * <b>This should only be called after the editor is initialized.</b>
21458      * @param {String} cmd The Midas command
21459      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21460      */
21461     execCmd : function(cmd, value){
21462         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21463         this.syncValue();
21464     },
21465  
21466  
21467    
21468     /**
21469      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21470      * to insert tRoo.
21471      * @param {String} text | dom node.. 
21472      */
21473     insertAtCursor : function(text)
21474     {
21475         
21476         if(!this.activated){
21477             return;
21478         }
21479         /*
21480         if(Roo.isIE){
21481             this.win.focus();
21482             var r = this.doc.selection.createRange();
21483             if(r){
21484                 r.collapse(true);
21485                 r.pasteHTML(text);
21486                 this.syncValue();
21487                 this.deferFocus();
21488             
21489             }
21490             return;
21491         }
21492         */
21493         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21494             this.win.focus();
21495             
21496             
21497             // from jquery ui (MIT licenced)
21498             var range, node;
21499             var win = this.win;
21500             
21501             if (win.getSelection && win.getSelection().getRangeAt) {
21502                 range = win.getSelection().getRangeAt(0);
21503                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21504                 range.insertNode(node);
21505             } else if (win.document.selection && win.document.selection.createRange) {
21506                 // no firefox support
21507                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21508                 win.document.selection.createRange().pasteHTML(txt);
21509             } else {
21510                 // no firefox support
21511                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21512                 this.execCmd('InsertHTML', txt);
21513             } 
21514             
21515             this.syncValue();
21516             
21517             this.deferFocus();
21518         }
21519     },
21520  // private
21521     mozKeyPress : function(e){
21522         if(e.ctrlKey){
21523             var c = e.getCharCode(), cmd;
21524           
21525             if(c > 0){
21526                 c = String.fromCharCode(c).toLowerCase();
21527                 switch(c){
21528                     case 'b':
21529                         cmd = 'bold';
21530                         break;
21531                     case 'i':
21532                         cmd = 'italic';
21533                         break;
21534                     
21535                     case 'u':
21536                         cmd = 'underline';
21537                         break;
21538                     
21539                     case 'v':
21540                         this.cleanUpPaste.defer(100, this);
21541                         return;
21542                         
21543                 }
21544                 if(cmd){
21545                     this.win.focus();
21546                     this.execCmd(cmd);
21547                     this.deferFocus();
21548                     e.preventDefault();
21549                 }
21550                 
21551             }
21552         }
21553     },
21554
21555     // private
21556     fixKeys : function(){ // load time branching for fastest keydown performance
21557         if(Roo.isIE){
21558             return function(e){
21559                 var k = e.getKey(), r;
21560                 if(k == e.TAB){
21561                     e.stopEvent();
21562                     r = this.doc.selection.createRange();
21563                     if(r){
21564                         r.collapse(true);
21565                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21566                         this.deferFocus();
21567                     }
21568                     return;
21569                 }
21570                 
21571                 if(k == e.ENTER){
21572                     r = this.doc.selection.createRange();
21573                     if(r){
21574                         var target = r.parentElement();
21575                         if(!target || target.tagName.toLowerCase() != 'li'){
21576                             e.stopEvent();
21577                             r.pasteHTML('<br />');
21578                             r.collapse(false);
21579                             r.select();
21580                         }
21581                     }
21582                 }
21583                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21584                     this.cleanUpPaste.defer(100, this);
21585                     return;
21586                 }
21587                 
21588                 
21589             };
21590         }else if(Roo.isOpera){
21591             return function(e){
21592                 var k = e.getKey();
21593                 if(k == e.TAB){
21594                     e.stopEvent();
21595                     this.win.focus();
21596                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21597                     this.deferFocus();
21598                 }
21599                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21600                     this.cleanUpPaste.defer(100, this);
21601                     return;
21602                 }
21603                 
21604             };
21605         }else if(Roo.isSafari){
21606             return function(e){
21607                 var k = e.getKey();
21608                 
21609                 if(k == e.TAB){
21610                     e.stopEvent();
21611                     this.execCmd('InsertText','\t');
21612                     this.deferFocus();
21613                     return;
21614                 }
21615                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21616                     this.cleanUpPaste.defer(100, this);
21617                     return;
21618                 }
21619                 
21620              };
21621         }
21622     }(),
21623     
21624     getAllAncestors: function()
21625     {
21626         var p = this.getSelectedNode();
21627         var a = [];
21628         if (!p) {
21629             a.push(p); // push blank onto stack..
21630             p = this.getParentElement();
21631         }
21632         
21633         
21634         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21635             a.push(p);
21636             p = p.parentNode;
21637         }
21638         a.push(this.doc.body);
21639         return a;
21640     },
21641     lastSel : false,
21642     lastSelNode : false,
21643     
21644     
21645     getSelection : function() 
21646     {
21647         this.assignDocWin();
21648         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21649     },
21650     
21651     getSelectedNode: function() 
21652     {
21653         // this may only work on Gecko!!!
21654         
21655         // should we cache this!!!!
21656         
21657         
21658         
21659          
21660         var range = this.createRange(this.getSelection()).cloneRange();
21661         
21662         if (Roo.isIE) {
21663             var parent = range.parentElement();
21664             while (true) {
21665                 var testRange = range.duplicate();
21666                 testRange.moveToElementText(parent);
21667                 if (testRange.inRange(range)) {
21668                     break;
21669                 }
21670                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21671                     break;
21672                 }
21673                 parent = parent.parentElement;
21674             }
21675             return parent;
21676         }
21677         
21678         // is ancestor a text element.
21679         var ac =  range.commonAncestorContainer;
21680         if (ac.nodeType == 3) {
21681             ac = ac.parentNode;
21682         }
21683         
21684         var ar = ac.childNodes;
21685          
21686         var nodes = [];
21687         var other_nodes = [];
21688         var has_other_nodes = false;
21689         for (var i=0;i<ar.length;i++) {
21690             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21691                 continue;
21692             }
21693             // fullly contained node.
21694             
21695             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21696                 nodes.push(ar[i]);
21697                 continue;
21698             }
21699             
21700             // probably selected..
21701             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21702                 other_nodes.push(ar[i]);
21703                 continue;
21704             }
21705             // outer..
21706             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21707                 continue;
21708             }
21709             
21710             
21711             has_other_nodes = true;
21712         }
21713         if (!nodes.length && other_nodes.length) {
21714             nodes= other_nodes;
21715         }
21716         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21717             return false;
21718         }
21719         
21720         return nodes[0];
21721     },
21722     createRange: function(sel)
21723     {
21724         // this has strange effects when using with 
21725         // top toolbar - not sure if it's a great idea.
21726         //this.editor.contentWindow.focus();
21727         if (typeof sel != "undefined") {
21728             try {
21729                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21730             } catch(e) {
21731                 return this.doc.createRange();
21732             }
21733         } else {
21734             return this.doc.createRange();
21735         }
21736     },
21737     getParentElement: function()
21738     {
21739         
21740         this.assignDocWin();
21741         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21742         
21743         var range = this.createRange(sel);
21744          
21745         try {
21746             var p = range.commonAncestorContainer;
21747             while (p.nodeType == 3) { // text node
21748                 p = p.parentNode;
21749             }
21750             return p;
21751         } catch (e) {
21752             return null;
21753         }
21754     
21755     },
21756     /***
21757      *
21758      * Range intersection.. the hard stuff...
21759      *  '-1' = before
21760      *  '0' = hits..
21761      *  '1' = after.
21762      *         [ -- selected range --- ]
21763      *   [fail]                        [fail]
21764      *
21765      *    basically..
21766      *      if end is before start or  hits it. fail.
21767      *      if start is after end or hits it fail.
21768      *
21769      *   if either hits (but other is outside. - then it's not 
21770      *   
21771      *    
21772      **/
21773     
21774     
21775     // @see http://www.thismuchiknow.co.uk/?p=64.
21776     rangeIntersectsNode : function(range, node)
21777     {
21778         var nodeRange = node.ownerDocument.createRange();
21779         try {
21780             nodeRange.selectNode(node);
21781         } catch (e) {
21782             nodeRange.selectNodeContents(node);
21783         }
21784     
21785         var rangeStartRange = range.cloneRange();
21786         rangeStartRange.collapse(true);
21787     
21788         var rangeEndRange = range.cloneRange();
21789         rangeEndRange.collapse(false);
21790     
21791         var nodeStartRange = nodeRange.cloneRange();
21792         nodeStartRange.collapse(true);
21793     
21794         var nodeEndRange = nodeRange.cloneRange();
21795         nodeEndRange.collapse(false);
21796     
21797         return rangeStartRange.compareBoundaryPoints(
21798                  Range.START_TO_START, nodeEndRange) == -1 &&
21799                rangeEndRange.compareBoundaryPoints(
21800                  Range.START_TO_START, nodeStartRange) == 1;
21801         
21802          
21803     },
21804     rangeCompareNode : function(range, node)
21805     {
21806         var nodeRange = node.ownerDocument.createRange();
21807         try {
21808             nodeRange.selectNode(node);
21809         } catch (e) {
21810             nodeRange.selectNodeContents(node);
21811         }
21812         
21813         
21814         range.collapse(true);
21815     
21816         nodeRange.collapse(true);
21817      
21818         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21819         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21820          
21821         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21822         
21823         var nodeIsBefore   =  ss == 1;
21824         var nodeIsAfter    = ee == -1;
21825         
21826         if (nodeIsBefore && nodeIsAfter) {
21827             return 0; // outer
21828         }
21829         if (!nodeIsBefore && nodeIsAfter) {
21830             return 1; //right trailed.
21831         }
21832         
21833         if (nodeIsBefore && !nodeIsAfter) {
21834             return 2;  // left trailed.
21835         }
21836         // fully contined.
21837         return 3;
21838     },
21839
21840     // private? - in a new class?
21841     cleanUpPaste :  function()
21842     {
21843         // cleans up the whole document..
21844         Roo.log('cleanuppaste');
21845         
21846         this.cleanUpChildren(this.doc.body);
21847         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21848         if (clean != this.doc.body.innerHTML) {
21849             this.doc.body.innerHTML = clean;
21850         }
21851         
21852     },
21853     
21854     cleanWordChars : function(input) {// change the chars to hex code
21855         var he = Roo.HtmlEditorCore;
21856         
21857         var output = input;
21858         Roo.each(he.swapCodes, function(sw) { 
21859             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21860             
21861             output = output.replace(swapper, sw[1]);
21862         });
21863         
21864         return output;
21865     },
21866     
21867     
21868     cleanUpChildren : function (n)
21869     {
21870         if (!n.childNodes.length) {
21871             return;
21872         }
21873         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21874            this.cleanUpChild(n.childNodes[i]);
21875         }
21876     },
21877     
21878     
21879         
21880     
21881     cleanUpChild : function (node)
21882     {
21883         var ed = this;
21884         //console.log(node);
21885         if (node.nodeName == "#text") {
21886             // clean up silly Windows -- stuff?
21887             return; 
21888         }
21889         if (node.nodeName == "#comment") {
21890             node.parentNode.removeChild(node);
21891             // clean up silly Windows -- stuff?
21892             return; 
21893         }
21894         var lcname = node.tagName.toLowerCase();
21895         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21896         // whitelist of tags..
21897         
21898         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21899             // remove node.
21900             node.parentNode.removeChild(node);
21901             return;
21902             
21903         }
21904         
21905         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21906         
21907         // spans with no attributes - just remove them..
21908         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21909             remove_keep_children = true;
21910         }
21911         
21912         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21913         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21914         
21915         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21916         //    remove_keep_children = true;
21917         //}
21918         
21919         if (remove_keep_children) {
21920             this.cleanUpChildren(node);
21921             // inserts everything just before this node...
21922             while (node.childNodes.length) {
21923                 var cn = node.childNodes[0];
21924                 node.removeChild(cn);
21925                 node.parentNode.insertBefore(cn, node);
21926             }
21927             node.parentNode.removeChild(node);
21928             return;
21929         }
21930         
21931         if (!node.attributes || !node.attributes.length) {
21932             
21933           
21934             
21935             
21936             this.cleanUpChildren(node);
21937             return;
21938         }
21939         
21940         function cleanAttr(n,v)
21941         {
21942             
21943             if (v.match(/^\./) || v.match(/^\//)) {
21944                 return;
21945             }
21946             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21947                 return;
21948             }
21949             if (v.match(/^#/)) {
21950                 return;
21951             }
21952 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21953             node.removeAttribute(n);
21954             
21955         }
21956         
21957         var cwhite = this.cwhite;
21958         var cblack = this.cblack;
21959             
21960         function cleanStyle(n,v)
21961         {
21962             if (v.match(/expression/)) { //XSS?? should we even bother..
21963                 node.removeAttribute(n);
21964                 return;
21965             }
21966             
21967             var parts = v.split(/;/);
21968             var clean = [];
21969             
21970             Roo.each(parts, function(p) {
21971                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21972                 if (!p.length) {
21973                     return true;
21974                 }
21975                 var l = p.split(':').shift().replace(/\s+/g,'');
21976                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21977                 
21978                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21979 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21980                     //node.removeAttribute(n);
21981                     return true;
21982                 }
21983                 //Roo.log()
21984                 // only allow 'c whitelisted system attributes'
21985                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21986 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21987                     //node.removeAttribute(n);
21988                     return true;
21989                 }
21990                 
21991                 
21992                  
21993                 
21994                 clean.push(p);
21995                 return true;
21996             });
21997             if (clean.length) { 
21998                 node.setAttribute(n, clean.join(';'));
21999             } else {
22000                 node.removeAttribute(n);
22001             }
22002             
22003         }
22004         
22005         
22006         for (var i = node.attributes.length-1; i > -1 ; i--) {
22007             var a = node.attributes[i];
22008             //console.log(a);
22009             
22010             if (a.name.toLowerCase().substr(0,2)=='on')  {
22011                 node.removeAttribute(a.name);
22012                 continue;
22013             }
22014             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22015                 node.removeAttribute(a.name);
22016                 continue;
22017             }
22018             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22019                 cleanAttr(a.name,a.value); // fixme..
22020                 continue;
22021             }
22022             if (a.name == 'style') {
22023                 cleanStyle(a.name,a.value);
22024                 continue;
22025             }
22026             /// clean up MS crap..
22027             // tecnically this should be a list of valid class'es..
22028             
22029             
22030             if (a.name == 'class') {
22031                 if (a.value.match(/^Mso/)) {
22032                     node.removeAttribute('class');
22033                 }
22034                 
22035                 if (a.value.match(/^body$/)) {
22036                     node.removeAttribute('class');
22037                 }
22038                 continue;
22039             }
22040             
22041             // style cleanup!?
22042             // class cleanup?
22043             
22044         }
22045         
22046         
22047         this.cleanUpChildren(node);
22048         
22049         
22050     },
22051     
22052     /**
22053      * Clean up MS wordisms...
22054      */
22055     cleanWord : function(node)
22056     {
22057         if (!node) {
22058             this.cleanWord(this.doc.body);
22059             return;
22060         }
22061         
22062         if(
22063                 node.nodeName == 'SPAN' &&
22064                 !node.hasAttributes() &&
22065                 node.childNodes.length == 1 &&
22066                 node.firstChild.nodeName == "#text"  
22067         ) {
22068             var textNode = node.firstChild;
22069             node.removeChild(textNode);
22070             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22071                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22072             }
22073             node.parentNode.insertBefore(textNode, node);
22074             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22075                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22076             }
22077             node.parentNode.removeChild(node);
22078         }
22079         
22080         if (node.nodeName == "#text") {
22081             // clean up silly Windows -- stuff?
22082             return; 
22083         }
22084         if (node.nodeName == "#comment") {
22085             node.parentNode.removeChild(node);
22086             // clean up silly Windows -- stuff?
22087             return; 
22088         }
22089         
22090         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22091             node.parentNode.removeChild(node);
22092             return;
22093         }
22094         //Roo.log(node.tagName);
22095         // remove - but keep children..
22096         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22097             //Roo.log('-- removed');
22098             while (node.childNodes.length) {
22099                 var cn = node.childNodes[0];
22100                 node.removeChild(cn);
22101                 node.parentNode.insertBefore(cn, node);
22102                 // move node to parent - and clean it..
22103                 this.cleanWord(cn);
22104             }
22105             node.parentNode.removeChild(node);
22106             /// no need to iterate chidlren = it's got none..
22107             //this.iterateChildren(node, this.cleanWord);
22108             return;
22109         }
22110         // clean styles
22111         if (node.className.length) {
22112             
22113             var cn = node.className.split(/\W+/);
22114             var cna = [];
22115             Roo.each(cn, function(cls) {
22116                 if (cls.match(/Mso[a-zA-Z]+/)) {
22117                     return;
22118                 }
22119                 cna.push(cls);
22120             });
22121             node.className = cna.length ? cna.join(' ') : '';
22122             if (!cna.length) {
22123                 node.removeAttribute("class");
22124             }
22125         }
22126         
22127         if (node.hasAttribute("lang")) {
22128             node.removeAttribute("lang");
22129         }
22130         
22131         if (node.hasAttribute("style")) {
22132             
22133             var styles = node.getAttribute("style").split(";");
22134             var nstyle = [];
22135             Roo.each(styles, function(s) {
22136                 if (!s.match(/:/)) {
22137                     return;
22138                 }
22139                 var kv = s.split(":");
22140                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22141                     return;
22142                 }
22143                 // what ever is left... we allow.
22144                 nstyle.push(s);
22145             });
22146             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22147             if (!nstyle.length) {
22148                 node.removeAttribute('style');
22149             }
22150         }
22151         this.iterateChildren(node, this.cleanWord);
22152         
22153         
22154         
22155     },
22156     /**
22157      * iterateChildren of a Node, calling fn each time, using this as the scole..
22158      * @param {DomNode} node node to iterate children of.
22159      * @param {Function} fn method of this class to call on each item.
22160      */
22161     iterateChildren : function(node, fn)
22162     {
22163         if (!node.childNodes.length) {
22164                 return;
22165         }
22166         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22167            fn.call(this, node.childNodes[i])
22168         }
22169     },
22170     
22171     
22172     /**
22173      * cleanTableWidths.
22174      *
22175      * Quite often pasting from word etc.. results in tables with column and widths.
22176      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22177      *
22178      */
22179     cleanTableWidths : function(node)
22180     {
22181          
22182          
22183         if (!node) {
22184             this.cleanTableWidths(this.doc.body);
22185             return;
22186         }
22187         
22188         // ignore list...
22189         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22190             return; 
22191         }
22192         Roo.log(node.tagName);
22193         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22194             this.iterateChildren(node, this.cleanTableWidths);
22195             return;
22196         }
22197         if (node.hasAttribute('width')) {
22198             node.removeAttribute('width');
22199         }
22200         
22201          
22202         if (node.hasAttribute("style")) {
22203             // pretty basic...
22204             
22205             var styles = node.getAttribute("style").split(";");
22206             var nstyle = [];
22207             Roo.each(styles, function(s) {
22208                 if (!s.match(/:/)) {
22209                     return;
22210                 }
22211                 var kv = s.split(":");
22212                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22213                     return;
22214                 }
22215                 // what ever is left... we allow.
22216                 nstyle.push(s);
22217             });
22218             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22219             if (!nstyle.length) {
22220                 node.removeAttribute('style');
22221             }
22222         }
22223         
22224         this.iterateChildren(node, this.cleanTableWidths);
22225         
22226         
22227     },
22228     
22229     
22230     
22231     
22232     domToHTML : function(currentElement, depth, nopadtext) {
22233         
22234         depth = depth || 0;
22235         nopadtext = nopadtext || false;
22236     
22237         if (!currentElement) {
22238             return this.domToHTML(this.doc.body);
22239         }
22240         
22241         //Roo.log(currentElement);
22242         var j;
22243         var allText = false;
22244         var nodeName = currentElement.nodeName;
22245         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22246         
22247         if  (nodeName == '#text') {
22248             
22249             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22250         }
22251         
22252         
22253         var ret = '';
22254         if (nodeName != 'BODY') {
22255              
22256             var i = 0;
22257             // Prints the node tagName, such as <A>, <IMG>, etc
22258             if (tagName) {
22259                 var attr = [];
22260                 for(i = 0; i < currentElement.attributes.length;i++) {
22261                     // quoting?
22262                     var aname = currentElement.attributes.item(i).name;
22263                     if (!currentElement.attributes.item(i).value.length) {
22264                         continue;
22265                     }
22266                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22267                 }
22268                 
22269                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22270             } 
22271             else {
22272                 
22273                 // eack
22274             }
22275         } else {
22276             tagName = false;
22277         }
22278         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22279             return ret;
22280         }
22281         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22282             nopadtext = true;
22283         }
22284         
22285         
22286         // Traverse the tree
22287         i = 0;
22288         var currentElementChild = currentElement.childNodes.item(i);
22289         var allText = true;
22290         var innerHTML  = '';
22291         lastnode = '';
22292         while (currentElementChild) {
22293             // Formatting code (indent the tree so it looks nice on the screen)
22294             var nopad = nopadtext;
22295             if (lastnode == 'SPAN') {
22296                 nopad  = true;
22297             }
22298             // text
22299             if  (currentElementChild.nodeName == '#text') {
22300                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22301                 toadd = nopadtext ? toadd : toadd.trim();
22302                 if (!nopad && toadd.length > 80) {
22303                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22304                 }
22305                 innerHTML  += toadd;
22306                 
22307                 i++;
22308                 currentElementChild = currentElement.childNodes.item(i);
22309                 lastNode = '';
22310                 continue;
22311             }
22312             allText = false;
22313             
22314             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22315                 
22316             // Recursively traverse the tree structure of the child node
22317             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22318             lastnode = currentElementChild.nodeName;
22319             i++;
22320             currentElementChild=currentElement.childNodes.item(i);
22321         }
22322         
22323         ret += innerHTML;
22324         
22325         if (!allText) {
22326                 // The remaining code is mostly for formatting the tree
22327             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22328         }
22329         
22330         
22331         if (tagName) {
22332             ret+= "</"+tagName+">";
22333         }
22334         return ret;
22335         
22336     },
22337         
22338     applyBlacklists : function()
22339     {
22340         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22341         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22342         
22343         this.white = [];
22344         this.black = [];
22345         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22346             if (b.indexOf(tag) > -1) {
22347                 return;
22348             }
22349             this.white.push(tag);
22350             
22351         }, this);
22352         
22353         Roo.each(w, function(tag) {
22354             if (b.indexOf(tag) > -1) {
22355                 return;
22356             }
22357             if (this.white.indexOf(tag) > -1) {
22358                 return;
22359             }
22360             this.white.push(tag);
22361             
22362         }, this);
22363         
22364         
22365         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22366             if (w.indexOf(tag) > -1) {
22367                 return;
22368             }
22369             this.black.push(tag);
22370             
22371         }, this);
22372         
22373         Roo.each(b, function(tag) {
22374             if (w.indexOf(tag) > -1) {
22375                 return;
22376             }
22377             if (this.black.indexOf(tag) > -1) {
22378                 return;
22379             }
22380             this.black.push(tag);
22381             
22382         }, this);
22383         
22384         
22385         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22386         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22387         
22388         this.cwhite = [];
22389         this.cblack = [];
22390         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22391             if (b.indexOf(tag) > -1) {
22392                 return;
22393             }
22394             this.cwhite.push(tag);
22395             
22396         }, this);
22397         
22398         Roo.each(w, function(tag) {
22399             if (b.indexOf(tag) > -1) {
22400                 return;
22401             }
22402             if (this.cwhite.indexOf(tag) > -1) {
22403                 return;
22404             }
22405             this.cwhite.push(tag);
22406             
22407         }, this);
22408         
22409         
22410         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22411             if (w.indexOf(tag) > -1) {
22412                 return;
22413             }
22414             this.cblack.push(tag);
22415             
22416         }, this);
22417         
22418         Roo.each(b, function(tag) {
22419             if (w.indexOf(tag) > -1) {
22420                 return;
22421             }
22422             if (this.cblack.indexOf(tag) > -1) {
22423                 return;
22424             }
22425             this.cblack.push(tag);
22426             
22427         }, this);
22428     },
22429     
22430     setStylesheets : function(stylesheets)
22431     {
22432         if(typeof(stylesheets) == 'string'){
22433             Roo.get(this.iframe.contentDocument.head).createChild({
22434                 tag : 'link',
22435                 rel : 'stylesheet',
22436                 type : 'text/css',
22437                 href : stylesheets
22438             });
22439             
22440             return;
22441         }
22442         var _this = this;
22443      
22444         Roo.each(stylesheets, function(s) {
22445             if(!s.length){
22446                 return;
22447             }
22448             
22449             Roo.get(_this.iframe.contentDocument.head).createChild({
22450                 tag : 'link',
22451                 rel : 'stylesheet',
22452                 type : 'text/css',
22453                 href : s
22454             });
22455         });
22456
22457         
22458     },
22459     
22460     removeStylesheets : function()
22461     {
22462         var _this = this;
22463         
22464         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22465             s.remove();
22466         });
22467     },
22468     
22469     setStyle : function(style)
22470     {
22471         Roo.get(this.iframe.contentDocument.head).createChild({
22472             tag : 'style',
22473             type : 'text/css',
22474             html : style
22475         });
22476
22477         return;
22478     }
22479     
22480     // hide stuff that is not compatible
22481     /**
22482      * @event blur
22483      * @hide
22484      */
22485     /**
22486      * @event change
22487      * @hide
22488      */
22489     /**
22490      * @event focus
22491      * @hide
22492      */
22493     /**
22494      * @event specialkey
22495      * @hide
22496      */
22497     /**
22498      * @cfg {String} fieldClass @hide
22499      */
22500     /**
22501      * @cfg {String} focusClass @hide
22502      */
22503     /**
22504      * @cfg {String} autoCreate @hide
22505      */
22506     /**
22507      * @cfg {String} inputType @hide
22508      */
22509     /**
22510      * @cfg {String} invalidClass @hide
22511      */
22512     /**
22513      * @cfg {String} invalidText @hide
22514      */
22515     /**
22516      * @cfg {String} msgFx @hide
22517      */
22518     /**
22519      * @cfg {String} validateOnBlur @hide
22520      */
22521 });
22522
22523 Roo.HtmlEditorCore.white = [
22524         'area', 'br', 'img', 'input', 'hr', 'wbr',
22525         
22526        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22527        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22528        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22529        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22530        'table',   'ul',         'xmp', 
22531        
22532        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22533       'thead',   'tr', 
22534      
22535       'dir', 'menu', 'ol', 'ul', 'dl',
22536        
22537       'embed',  'object'
22538 ];
22539
22540
22541 Roo.HtmlEditorCore.black = [
22542     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22543         'applet', // 
22544         'base',   'basefont', 'bgsound', 'blink',  'body', 
22545         'frame',  'frameset', 'head',    'html',   'ilayer', 
22546         'iframe', 'layer',  'link',     'meta',    'object',   
22547         'script', 'style' ,'title',  'xml' // clean later..
22548 ];
22549 Roo.HtmlEditorCore.clean = [
22550     'script', 'style', 'title', 'xml'
22551 ];
22552 Roo.HtmlEditorCore.remove = [
22553     'font'
22554 ];
22555 // attributes..
22556
22557 Roo.HtmlEditorCore.ablack = [
22558     'on'
22559 ];
22560     
22561 Roo.HtmlEditorCore.aclean = [ 
22562     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22563 ];
22564
22565 // protocols..
22566 Roo.HtmlEditorCore.pwhite= [
22567         'http',  'https',  'mailto'
22568 ];
22569
22570 // white listed style attributes.
22571 Roo.HtmlEditorCore.cwhite= [
22572       //  'text-align', /// default is to allow most things..
22573       
22574          
22575 //        'font-size'//??
22576 ];
22577
22578 // black listed style attributes.
22579 Roo.HtmlEditorCore.cblack= [
22580       //  'font-size' -- this can be set by the project 
22581 ];
22582
22583
22584 Roo.HtmlEditorCore.swapCodes   =[ 
22585     [    8211, "--" ], 
22586     [    8212, "--" ], 
22587     [    8216,  "'" ],  
22588     [    8217, "'" ],  
22589     [    8220, '"' ],  
22590     [    8221, '"' ],  
22591     [    8226, "*" ],  
22592     [    8230, "..." ]
22593 ]; 
22594
22595     //<script type="text/javascript">
22596
22597 /*
22598  * Ext JS Library 1.1.1
22599  * Copyright(c) 2006-2007, Ext JS, LLC.
22600  * Licence LGPL
22601  * 
22602  */
22603  
22604  
22605 Roo.form.HtmlEditor = function(config){
22606     
22607     
22608     
22609     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22610     
22611     if (!this.toolbars) {
22612         this.toolbars = [];
22613     }
22614     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22615     
22616     
22617 };
22618
22619 /**
22620  * @class Roo.form.HtmlEditor
22621  * @extends Roo.form.Field
22622  * Provides a lightweight HTML Editor component.
22623  *
22624  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22625  * 
22626  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22627  * supported by this editor.</b><br/><br/>
22628  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22629  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22630  */
22631 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22632     /**
22633      * @cfg {Boolean} clearUp
22634      */
22635     clearUp : true,
22636       /**
22637      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22638      */
22639     toolbars : false,
22640    
22641      /**
22642      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22643      *                        Roo.resizable.
22644      */
22645     resizable : false,
22646      /**
22647      * @cfg {Number} height (in pixels)
22648      */   
22649     height: 300,
22650    /**
22651      * @cfg {Number} width (in pixels)
22652      */   
22653     width: 500,
22654     
22655     /**
22656      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22657      * 
22658      */
22659     stylesheets: false,
22660     
22661     
22662      /**
22663      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22664      * 
22665      */
22666     cblack: false,
22667     /**
22668      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22669      * 
22670      */
22671     cwhite: false,
22672     
22673      /**
22674      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22675      * 
22676      */
22677     black: false,
22678     /**
22679      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22680      * 
22681      */
22682     white: false,
22683     
22684     // id of frame..
22685     frameId: false,
22686     
22687     // private properties
22688     validationEvent : false,
22689     deferHeight: true,
22690     initialized : false,
22691     activated : false,
22692     
22693     onFocus : Roo.emptyFn,
22694     iframePad:3,
22695     hideMode:'offsets',
22696     
22697     actionMode : 'container', // defaults to hiding it...
22698     
22699     defaultAutoCreate : { // modified by initCompnoent..
22700         tag: "textarea",
22701         style:"width:500px;height:300px;",
22702         autocomplete: "new-password"
22703     },
22704
22705     // private
22706     initComponent : function(){
22707         this.addEvents({
22708             /**
22709              * @event initialize
22710              * Fires when the editor is fully initialized (including the iframe)
22711              * @param {HtmlEditor} this
22712              */
22713             initialize: true,
22714             /**
22715              * @event activate
22716              * Fires when the editor is first receives the focus. Any insertion must wait
22717              * until after this event.
22718              * @param {HtmlEditor} this
22719              */
22720             activate: true,
22721              /**
22722              * @event beforesync
22723              * Fires before the textarea is updated with content from the editor iframe. Return false
22724              * to cancel the sync.
22725              * @param {HtmlEditor} this
22726              * @param {String} html
22727              */
22728             beforesync: true,
22729              /**
22730              * @event beforepush
22731              * Fires before the iframe editor is updated with content from the textarea. Return false
22732              * to cancel the push.
22733              * @param {HtmlEditor} this
22734              * @param {String} html
22735              */
22736             beforepush: true,
22737              /**
22738              * @event sync
22739              * Fires when the textarea is updated with content from the editor iframe.
22740              * @param {HtmlEditor} this
22741              * @param {String} html
22742              */
22743             sync: true,
22744              /**
22745              * @event push
22746              * Fires when the iframe editor is updated with content from the textarea.
22747              * @param {HtmlEditor} this
22748              * @param {String} html
22749              */
22750             push: true,
22751              /**
22752              * @event editmodechange
22753              * Fires when the editor switches edit modes
22754              * @param {HtmlEditor} this
22755              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22756              */
22757             editmodechange: true,
22758             /**
22759              * @event editorevent
22760              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22761              * @param {HtmlEditor} this
22762              */
22763             editorevent: true,
22764             /**
22765              * @event firstfocus
22766              * Fires when on first focus - needed by toolbars..
22767              * @param {HtmlEditor} this
22768              */
22769             firstfocus: true,
22770             /**
22771              * @event autosave
22772              * Auto save the htmlEditor value as a file into Events
22773              * @param {HtmlEditor} this
22774              */
22775             autosave: true,
22776             /**
22777              * @event savedpreview
22778              * preview the saved version of htmlEditor
22779              * @param {HtmlEditor} this
22780              */
22781             savedpreview: true,
22782             
22783             /**
22784             * @event stylesheetsclick
22785             * Fires when press the Sytlesheets button
22786             * @param {Roo.HtmlEditorCore} this
22787             */
22788             stylesheetsclick: true
22789         });
22790         this.defaultAutoCreate =  {
22791             tag: "textarea",
22792             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22793             autocomplete: "new-password"
22794         };
22795     },
22796
22797     /**
22798      * Protected method that will not generally be called directly. It
22799      * is called when the editor creates its toolbar. Override this method if you need to
22800      * add custom toolbar buttons.
22801      * @param {HtmlEditor} editor
22802      */
22803     createToolbar : function(editor){
22804         Roo.log("create toolbars");
22805         if (!editor.toolbars || !editor.toolbars.length) {
22806             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22807         }
22808         
22809         for (var i =0 ; i < editor.toolbars.length;i++) {
22810             editor.toolbars[i] = Roo.factory(
22811                     typeof(editor.toolbars[i]) == 'string' ?
22812                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22813                 Roo.form.HtmlEditor);
22814             editor.toolbars[i].init(editor);
22815         }
22816          
22817         
22818     },
22819
22820      
22821     // private
22822     onRender : function(ct, position)
22823     {
22824         var _t = this;
22825         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22826         
22827         this.wrap = this.el.wrap({
22828             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22829         });
22830         
22831         this.editorcore.onRender(ct, position);
22832          
22833         if (this.resizable) {
22834             this.resizeEl = new Roo.Resizable(this.wrap, {
22835                 pinned : true,
22836                 wrap: true,
22837                 dynamic : true,
22838                 minHeight : this.height,
22839                 height: this.height,
22840                 handles : this.resizable,
22841                 width: this.width,
22842                 listeners : {
22843                     resize : function(r, w, h) {
22844                         _t.onResize(w,h); // -something
22845                     }
22846                 }
22847             });
22848             
22849         }
22850         this.createToolbar(this);
22851        
22852         
22853         if(!this.width){
22854             this.setSize(this.wrap.getSize());
22855         }
22856         if (this.resizeEl) {
22857             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22858             // should trigger onReize..
22859         }
22860         
22861         this.keyNav = new Roo.KeyNav(this.el, {
22862             
22863             "tab" : function(e){
22864                 e.preventDefault();
22865                 
22866                 var value = this.getValue();
22867                 
22868                 var start = this.el.dom.selectionStart;
22869                 var end = this.el.dom.selectionEnd;
22870                 
22871                 if(!e.shiftKey){
22872                     
22873                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22874                     this.el.dom.setSelectionRange(end + 1, end + 1);
22875                     return;
22876                 }
22877                 
22878                 var f = value.substring(0, start).split("\t");
22879                 
22880                 if(f.pop().length != 0){
22881                     return;
22882                 }
22883                 
22884                 this.setValue(f.join("\t") + value.substring(end));
22885                 this.el.dom.setSelectionRange(start - 1, start - 1);
22886                 
22887             },
22888             
22889             "home" : function(e){
22890                 e.preventDefault();
22891                 
22892                 var curr = this.el.dom.selectionStart;
22893                 var lines = this.getValue().split("\n");
22894                 
22895                 if(!lines.length){
22896                     return;
22897                 }
22898                 
22899                 if(e.ctrlKey){
22900                     this.el.dom.setSelectionRange(0, 0);
22901                     return;
22902                 }
22903                 
22904                 var pos = 0;
22905                 
22906                 for (var i = 0; i < lines.length;i++) {
22907                     pos += lines[i].length;
22908                     
22909                     if(i != 0){
22910                         pos += 1;
22911                     }
22912                     
22913                     if(pos < curr){
22914                         continue;
22915                     }
22916                     
22917                     pos -= lines[i].length;
22918                     
22919                     break;
22920                 }
22921                 
22922                 if(!e.shiftKey){
22923                     this.el.dom.setSelectionRange(pos, pos);
22924                     return;
22925                 }
22926                 
22927                 this.el.dom.selectionStart = pos;
22928                 this.el.dom.selectionEnd = curr;
22929             },
22930             
22931             "end" : function(e){
22932                 e.preventDefault();
22933                 
22934                 var curr = this.el.dom.selectionStart;
22935                 var lines = this.getValue().split("\n");
22936                 
22937                 if(!lines.length){
22938                     return;
22939                 }
22940                 
22941                 if(e.ctrlKey){
22942                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22943                     return;
22944                 }
22945                 
22946                 var pos = 0;
22947                 
22948                 for (var i = 0; i < lines.length;i++) {
22949                     
22950                     pos += lines[i].length;
22951                     
22952                     if(i != 0){
22953                         pos += 1;
22954                     }
22955                     
22956                     if(pos < curr){
22957                         continue;
22958                     }
22959                     
22960                     break;
22961                 }
22962                 
22963                 if(!e.shiftKey){
22964                     this.el.dom.setSelectionRange(pos, pos);
22965                     return;
22966                 }
22967                 
22968                 this.el.dom.selectionStart = curr;
22969                 this.el.dom.selectionEnd = pos;
22970             },
22971
22972             scope : this,
22973
22974             doRelay : function(foo, bar, hname){
22975                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22976             },
22977
22978             forceKeyDown: true
22979         });
22980         
22981 //        if(this.autosave && this.w){
22982 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22983 //        }
22984     },
22985
22986     // private
22987     onResize : function(w, h)
22988     {
22989         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22990         var ew = false;
22991         var eh = false;
22992         
22993         if(this.el ){
22994             if(typeof w == 'number'){
22995                 var aw = w - this.wrap.getFrameWidth('lr');
22996                 this.el.setWidth(this.adjustWidth('textarea', aw));
22997                 ew = aw;
22998             }
22999             if(typeof h == 'number'){
23000                 var tbh = 0;
23001                 for (var i =0; i < this.toolbars.length;i++) {
23002                     // fixme - ask toolbars for heights?
23003                     tbh += this.toolbars[i].tb.el.getHeight();
23004                     if (this.toolbars[i].footer) {
23005                         tbh += this.toolbars[i].footer.el.getHeight();
23006                     }
23007                 }
23008                 
23009                 
23010                 
23011                 
23012                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23013                 ah -= 5; // knock a few pixes off for look..
23014 //                Roo.log(ah);
23015                 this.el.setHeight(this.adjustWidth('textarea', ah));
23016                 var eh = ah;
23017             }
23018         }
23019         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23020         this.editorcore.onResize(ew,eh);
23021         
23022     },
23023
23024     /**
23025      * Toggles the editor between standard and source edit mode.
23026      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23027      */
23028     toggleSourceEdit : function(sourceEditMode)
23029     {
23030         this.editorcore.toggleSourceEdit(sourceEditMode);
23031         
23032         if(this.editorcore.sourceEditMode){
23033             Roo.log('editor - showing textarea');
23034             
23035 //            Roo.log('in');
23036 //            Roo.log(this.syncValue());
23037             this.editorcore.syncValue();
23038             this.el.removeClass('x-hidden');
23039             this.el.dom.removeAttribute('tabIndex');
23040             this.el.focus();
23041             
23042             for (var i = 0; i < this.toolbars.length; i++) {
23043                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23044                     this.toolbars[i].tb.hide();
23045                     this.toolbars[i].footer.hide();
23046                 }
23047             }
23048             
23049         }else{
23050             Roo.log('editor - hiding textarea');
23051 //            Roo.log('out')
23052 //            Roo.log(this.pushValue()); 
23053             this.editorcore.pushValue();
23054             
23055             this.el.addClass('x-hidden');
23056             this.el.dom.setAttribute('tabIndex', -1);
23057             
23058             for (var i = 0; i < this.toolbars.length; i++) {
23059                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23060                     this.toolbars[i].tb.show();
23061                     this.toolbars[i].footer.show();
23062                 }
23063             }
23064             
23065             //this.deferFocus();
23066         }
23067         
23068         this.setSize(this.wrap.getSize());
23069         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23070         
23071         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23072     },
23073  
23074     // private (for BoxComponent)
23075     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23076
23077     // private (for BoxComponent)
23078     getResizeEl : function(){
23079         return this.wrap;
23080     },
23081
23082     // private (for BoxComponent)
23083     getPositionEl : function(){
23084         return this.wrap;
23085     },
23086
23087     // private
23088     initEvents : function(){
23089         this.originalValue = this.getValue();
23090     },
23091
23092     /**
23093      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23094      * @method
23095      */
23096     markInvalid : Roo.emptyFn,
23097     /**
23098      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23099      * @method
23100      */
23101     clearInvalid : Roo.emptyFn,
23102
23103     setValue : function(v){
23104         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23105         this.editorcore.pushValue();
23106     },
23107
23108      
23109     // private
23110     deferFocus : function(){
23111         this.focus.defer(10, this);
23112     },
23113
23114     // doc'ed in Field
23115     focus : function(){
23116         this.editorcore.focus();
23117         
23118     },
23119       
23120
23121     // private
23122     onDestroy : function(){
23123         
23124         
23125         
23126         if(this.rendered){
23127             
23128             for (var i =0; i < this.toolbars.length;i++) {
23129                 // fixme - ask toolbars for heights?
23130                 this.toolbars[i].onDestroy();
23131             }
23132             
23133             this.wrap.dom.innerHTML = '';
23134             this.wrap.remove();
23135         }
23136     },
23137
23138     // private
23139     onFirstFocus : function(){
23140         //Roo.log("onFirstFocus");
23141         this.editorcore.onFirstFocus();
23142          for (var i =0; i < this.toolbars.length;i++) {
23143             this.toolbars[i].onFirstFocus();
23144         }
23145         
23146     },
23147     
23148     // private
23149     syncValue : function()
23150     {
23151         this.editorcore.syncValue();
23152     },
23153     
23154     pushValue : function()
23155     {
23156         this.editorcore.pushValue();
23157     },
23158     
23159     setStylesheets : function(stylesheets)
23160     {
23161         this.editorcore.setStylesheets(stylesheets);
23162     },
23163     
23164     removeStylesheets : function()
23165     {
23166         this.editorcore.removeStylesheets();
23167     }
23168      
23169     
23170     // hide stuff that is not compatible
23171     /**
23172      * @event blur
23173      * @hide
23174      */
23175     /**
23176      * @event change
23177      * @hide
23178      */
23179     /**
23180      * @event focus
23181      * @hide
23182      */
23183     /**
23184      * @event specialkey
23185      * @hide
23186      */
23187     /**
23188      * @cfg {String} fieldClass @hide
23189      */
23190     /**
23191      * @cfg {String} focusClass @hide
23192      */
23193     /**
23194      * @cfg {String} autoCreate @hide
23195      */
23196     /**
23197      * @cfg {String} inputType @hide
23198      */
23199     /**
23200      * @cfg {String} invalidClass @hide
23201      */
23202     /**
23203      * @cfg {String} invalidText @hide
23204      */
23205     /**
23206      * @cfg {String} msgFx @hide
23207      */
23208     /**
23209      * @cfg {String} validateOnBlur @hide
23210      */
23211 });
23212  
23213     // <script type="text/javascript">
23214 /*
23215  * Based on
23216  * Ext JS Library 1.1.1
23217  * Copyright(c) 2006-2007, Ext JS, LLC.
23218  *  
23219  
23220  */
23221
23222 /**
23223  * @class Roo.form.HtmlEditorToolbar1
23224  * Basic Toolbar
23225  * 
23226  * Usage:
23227  *
23228  new Roo.form.HtmlEditor({
23229     ....
23230     toolbars : [
23231         new Roo.form.HtmlEditorToolbar1({
23232             disable : { fonts: 1 , format: 1, ..., ... , ...],
23233             btns : [ .... ]
23234         })
23235     }
23236      
23237  * 
23238  * @cfg {Object} disable List of elements to disable..
23239  * @cfg {Array} btns List of additional buttons.
23240  * 
23241  * 
23242  * NEEDS Extra CSS? 
23243  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23244  */
23245  
23246 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23247 {
23248     
23249     Roo.apply(this, config);
23250     
23251     // default disabled, based on 'good practice'..
23252     this.disable = this.disable || {};
23253     Roo.applyIf(this.disable, {
23254         fontSize : true,
23255         colors : true,
23256         specialElements : true
23257     });
23258     
23259     
23260     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23261     // dont call parent... till later.
23262 }
23263
23264 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
23265     
23266     tb: false,
23267     
23268     rendered: false,
23269     
23270     editor : false,
23271     editorcore : false,
23272     /**
23273      * @cfg {Object} disable  List of toolbar elements to disable
23274          
23275      */
23276     disable : false,
23277     
23278     
23279      /**
23280      * @cfg {String} createLinkText The default text for the create link prompt
23281      */
23282     createLinkText : 'Please enter the URL for the link:',
23283     /**
23284      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23285      */
23286     defaultLinkValue : 'http:/'+'/',
23287    
23288     
23289       /**
23290      * @cfg {Array} fontFamilies An array of available font families
23291      */
23292     fontFamilies : [
23293         'Arial',
23294         'Courier New',
23295         'Tahoma',
23296         'Times New Roman',
23297         'Verdana'
23298     ],
23299     
23300     specialChars : [
23301            "&#169;",
23302           "&#174;",     
23303           "&#8482;",    
23304           "&#163;" ,    
23305          // "&#8212;",    
23306           "&#8230;",    
23307           "&#247;" ,    
23308         //  "&#225;" ,     ?? a acute?
23309            "&#8364;"    , //Euro
23310        //   "&#8220;"    ,
23311         //  "&#8221;"    ,
23312         //  "&#8226;"    ,
23313           "&#176;"  //   , // degrees
23314
23315          // "&#233;"     , // e ecute
23316          // "&#250;"     , // u ecute?
23317     ],
23318     
23319     specialElements : [
23320         {
23321             text: "Insert Table",
23322             xtype: 'MenuItem',
23323             xns : Roo.Menu,
23324             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23325                 
23326         },
23327         {    
23328             text: "Insert Image",
23329             xtype: 'MenuItem',
23330             xns : Roo.Menu,
23331             ihtml : '<img src="about:blank"/>'
23332             
23333         }
23334         
23335          
23336     ],
23337     
23338     
23339     inputElements : [ 
23340             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23341             "input:submit", "input:button", "select", "textarea", "label" ],
23342     formats : [
23343         ["p"] ,  
23344         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23345         ["pre"],[ "code"], 
23346         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23347         ['div'],['span'],
23348         ['sup'],['sub']
23349     ],
23350     
23351     cleanStyles : [
23352         "font-size"
23353     ],
23354      /**
23355      * @cfg {String} defaultFont default font to use.
23356      */
23357     defaultFont: 'tahoma',
23358    
23359     fontSelect : false,
23360     
23361     
23362     formatCombo : false,
23363     
23364     init : function(editor)
23365     {
23366         this.editor = editor;
23367         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23368         var editorcore = this.editorcore;
23369         
23370         var _t = this;
23371         
23372         var fid = editorcore.frameId;
23373         var etb = this;
23374         function btn(id, toggle, handler){
23375             var xid = fid + '-'+ id ;
23376             return {
23377                 id : xid,
23378                 cmd : id,
23379                 cls : 'x-btn-icon x-edit-'+id,
23380                 enableToggle:toggle !== false,
23381                 scope: _t, // was editor...
23382                 handler:handler||_t.relayBtnCmd,
23383                 clickEvent:'mousedown',
23384                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23385                 tabIndex:-1
23386             };
23387         }
23388         
23389         
23390         
23391         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23392         this.tb = tb;
23393          // stop form submits
23394         tb.el.on('click', function(e){
23395             e.preventDefault(); // what does this do?
23396         });
23397
23398         if(!this.disable.font) { // && !Roo.isSafari){
23399             /* why no safari for fonts 
23400             editor.fontSelect = tb.el.createChild({
23401                 tag:'select',
23402                 tabIndex: -1,
23403                 cls:'x-font-select',
23404                 html: this.createFontOptions()
23405             });
23406             
23407             editor.fontSelect.on('change', function(){
23408                 var font = editor.fontSelect.dom.value;
23409                 editor.relayCmd('fontname', font);
23410                 editor.deferFocus();
23411             }, editor);
23412             
23413             tb.add(
23414                 editor.fontSelect.dom,
23415                 '-'
23416             );
23417             */
23418             
23419         };
23420         if(!this.disable.formats){
23421             this.formatCombo = new Roo.form.ComboBox({
23422                 store: new Roo.data.SimpleStore({
23423                     id : 'tag',
23424                     fields: ['tag'],
23425                     data : this.formats // from states.js
23426                 }),
23427                 blockFocus : true,
23428                 name : '',
23429                 //autoCreate : {tag: "div",  size: "20"},
23430                 displayField:'tag',
23431                 typeAhead: false,
23432                 mode: 'local',
23433                 editable : false,
23434                 triggerAction: 'all',
23435                 emptyText:'Add tag',
23436                 selectOnFocus:true,
23437                 width:135,
23438                 listeners : {
23439                     'select': function(c, r, i) {
23440                         editorcore.insertTag(r.get('tag'));
23441                         editor.focus();
23442                     }
23443                 }
23444
23445             });
23446             tb.addField(this.formatCombo);
23447             
23448         }
23449         
23450         if(!this.disable.format){
23451             tb.add(
23452                 btn('bold'),
23453                 btn('italic'),
23454                 btn('underline'),
23455                 btn('strikethrough')
23456             );
23457         };
23458         if(!this.disable.fontSize){
23459             tb.add(
23460                 '-',
23461                 
23462                 
23463                 btn('increasefontsize', false, editorcore.adjustFont),
23464                 btn('decreasefontsize', false, editorcore.adjustFont)
23465             );
23466         };
23467         
23468         
23469         if(!this.disable.colors){
23470             tb.add(
23471                 '-', {
23472                     id:editorcore.frameId +'-forecolor',
23473                     cls:'x-btn-icon x-edit-forecolor',
23474                     clickEvent:'mousedown',
23475                     tooltip: this.buttonTips['forecolor'] || undefined,
23476                     tabIndex:-1,
23477                     menu : new Roo.menu.ColorMenu({
23478                         allowReselect: true,
23479                         focus: Roo.emptyFn,
23480                         value:'000000',
23481                         plain:true,
23482                         selectHandler: function(cp, color){
23483                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23484                             editor.deferFocus();
23485                         },
23486                         scope: editorcore,
23487                         clickEvent:'mousedown'
23488                     })
23489                 }, {
23490                     id:editorcore.frameId +'backcolor',
23491                     cls:'x-btn-icon x-edit-backcolor',
23492                     clickEvent:'mousedown',
23493                     tooltip: this.buttonTips['backcolor'] || undefined,
23494                     tabIndex:-1,
23495                     menu : new Roo.menu.ColorMenu({
23496                         focus: Roo.emptyFn,
23497                         value:'FFFFFF',
23498                         plain:true,
23499                         allowReselect: true,
23500                         selectHandler: function(cp, color){
23501                             if(Roo.isGecko){
23502                                 editorcore.execCmd('useCSS', false);
23503                                 editorcore.execCmd('hilitecolor', color);
23504                                 editorcore.execCmd('useCSS', true);
23505                                 editor.deferFocus();
23506                             }else{
23507                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23508                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23509                                 editor.deferFocus();
23510                             }
23511                         },
23512                         scope:editorcore,
23513                         clickEvent:'mousedown'
23514                     })
23515                 }
23516             );
23517         };
23518         // now add all the items...
23519         
23520
23521         if(!this.disable.alignments){
23522             tb.add(
23523                 '-',
23524                 btn('justifyleft'),
23525                 btn('justifycenter'),
23526                 btn('justifyright')
23527             );
23528         };
23529
23530         //if(!Roo.isSafari){
23531             if(!this.disable.links){
23532                 tb.add(
23533                     '-',
23534                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23535                 );
23536             };
23537
23538             if(!this.disable.lists){
23539                 tb.add(
23540                     '-',
23541                     btn('insertorderedlist'),
23542                     btn('insertunorderedlist')
23543                 );
23544             }
23545             if(!this.disable.sourceEdit){
23546                 tb.add(
23547                     '-',
23548                     btn('sourceedit', true, function(btn){
23549                         this.toggleSourceEdit(btn.pressed);
23550                     })
23551                 );
23552             }
23553         //}
23554         
23555         var smenu = { };
23556         // special menu.. - needs to be tidied up..
23557         if (!this.disable.special) {
23558             smenu = {
23559                 text: "&#169;",
23560                 cls: 'x-edit-none',
23561                 
23562                 menu : {
23563                     items : []
23564                 }
23565             };
23566             for (var i =0; i < this.specialChars.length; i++) {
23567                 smenu.menu.items.push({
23568                     
23569                     html: this.specialChars[i],
23570                     handler: function(a,b) {
23571                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23572                         //editor.insertAtCursor(a.html);
23573                         
23574                     },
23575                     tabIndex:-1
23576                 });
23577             }
23578             
23579             
23580             tb.add(smenu);
23581             
23582             
23583         }
23584         
23585         var cmenu = { };
23586         if (!this.disable.cleanStyles) {
23587             cmenu = {
23588                 cls: 'x-btn-icon x-btn-clear',
23589                 
23590                 menu : {
23591                     items : []
23592                 }
23593             };
23594             for (var i =0; i < this.cleanStyles.length; i++) {
23595                 cmenu.menu.items.push({
23596                     actiontype : this.cleanStyles[i],
23597                     html: 'Remove ' + this.cleanStyles[i],
23598                     handler: function(a,b) {
23599 //                        Roo.log(a);
23600 //                        Roo.log(b);
23601                         var c = Roo.get(editorcore.doc.body);
23602                         c.select('[style]').each(function(s) {
23603                             s.dom.style.removeProperty(a.actiontype);
23604                         });
23605                         editorcore.syncValue();
23606                     },
23607                     tabIndex:-1
23608                 });
23609             }
23610              cmenu.menu.items.push({
23611                 actiontype : 'tablewidths',
23612                 html: 'Remove Table Widths',
23613                 handler: function(a,b) {
23614                     editorcore.cleanTableWidths();
23615                     editorcore.syncValue();
23616                 },
23617                 tabIndex:-1
23618             });
23619             cmenu.menu.items.push({
23620                 actiontype : 'word',
23621                 html: 'Remove MS Word Formating',
23622                 handler: function(a,b) {
23623                     editorcore.cleanWord();
23624                     editorcore.syncValue();
23625                 },
23626                 tabIndex:-1
23627             });
23628             
23629             cmenu.menu.items.push({
23630                 actiontype : 'all',
23631                 html: 'Remove All Styles',
23632                 handler: function(a,b) {
23633                     
23634                     var c = Roo.get(editorcore.doc.body);
23635                     c.select('[style]').each(function(s) {
23636                         s.dom.removeAttribute('style');
23637                     });
23638                     editorcore.syncValue();
23639                 },
23640                 tabIndex:-1
23641             });
23642             
23643             cmenu.menu.items.push({
23644                 actiontype : 'all',
23645                 html: 'Remove All CSS Classes',
23646                 handler: function(a,b) {
23647                     
23648                     var c = Roo.get(editorcore.doc.body);
23649                     c.select('[class]').each(function(s) {
23650                         s.dom.removeAttribute('class');
23651                     });
23652                     editorcore.cleanWord();
23653                     editorcore.syncValue();
23654                 },
23655                 tabIndex:-1
23656             });
23657             
23658              cmenu.menu.items.push({
23659                 actiontype : 'tidy',
23660                 html: 'Tidy HTML Source',
23661                 handler: function(a,b) {
23662                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23663                     editorcore.syncValue();
23664                 },
23665                 tabIndex:-1
23666             });
23667             
23668             
23669             tb.add(cmenu);
23670         }
23671          
23672         if (!this.disable.specialElements) {
23673             var semenu = {
23674                 text: "Other;",
23675                 cls: 'x-edit-none',
23676                 menu : {
23677                     items : []
23678                 }
23679             };
23680             for (var i =0; i < this.specialElements.length; i++) {
23681                 semenu.menu.items.push(
23682                     Roo.apply({ 
23683                         handler: function(a,b) {
23684                             editor.insertAtCursor(this.ihtml);
23685                         }
23686                     }, this.specialElements[i])
23687                 );
23688                     
23689             }
23690             
23691             tb.add(semenu);
23692             
23693             
23694         }
23695          
23696         
23697         if (this.btns) {
23698             for(var i =0; i< this.btns.length;i++) {
23699                 var b = Roo.factory(this.btns[i],Roo.form);
23700                 b.cls =  'x-edit-none';
23701                 
23702                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23703                     b.cls += ' x-init-enable';
23704                 }
23705                 
23706                 b.scope = editorcore;
23707                 tb.add(b);
23708             }
23709         
23710         }
23711         
23712         
23713         
23714         // disable everything...
23715         
23716         this.tb.items.each(function(item){
23717             
23718            if(
23719                 item.id != editorcore.frameId+ '-sourceedit' && 
23720                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23721             ){
23722                 
23723                 item.disable();
23724             }
23725         });
23726         this.rendered = true;
23727         
23728         // the all the btns;
23729         editor.on('editorevent', this.updateToolbar, this);
23730         // other toolbars need to implement this..
23731         //editor.on('editmodechange', this.updateToolbar, this);
23732     },
23733     
23734     
23735     relayBtnCmd : function(btn) {
23736         this.editorcore.relayCmd(btn.cmd);
23737     },
23738     // private used internally
23739     createLink : function(){
23740         Roo.log("create link?");
23741         var url = prompt(this.createLinkText, this.defaultLinkValue);
23742         if(url && url != 'http:/'+'/'){
23743             this.editorcore.relayCmd('createlink', url);
23744         }
23745     },
23746
23747     
23748     /**
23749      * Protected method that will not generally be called directly. It triggers
23750      * a toolbar update by reading the markup state of the current selection in the editor.
23751      */
23752     updateToolbar: function(){
23753
23754         if(!this.editorcore.activated){
23755             this.editor.onFirstFocus();
23756             return;
23757         }
23758
23759         var btns = this.tb.items.map, 
23760             doc = this.editorcore.doc,
23761             frameId = this.editorcore.frameId;
23762
23763         if(!this.disable.font && !Roo.isSafari){
23764             /*
23765             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23766             if(name != this.fontSelect.dom.value){
23767                 this.fontSelect.dom.value = name;
23768             }
23769             */
23770         }
23771         if(!this.disable.format){
23772             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23773             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23774             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23775             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23776         }
23777         if(!this.disable.alignments){
23778             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23779             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23780             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23781         }
23782         if(!Roo.isSafari && !this.disable.lists){
23783             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23784             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23785         }
23786         
23787         var ans = this.editorcore.getAllAncestors();
23788         if (this.formatCombo) {
23789             
23790             
23791             var store = this.formatCombo.store;
23792             this.formatCombo.setValue("");
23793             for (var i =0; i < ans.length;i++) {
23794                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23795                     // select it..
23796                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23797                     break;
23798                 }
23799             }
23800         }
23801         
23802         
23803         
23804         // hides menus... - so this cant be on a menu...
23805         Roo.menu.MenuMgr.hideAll();
23806
23807         //this.editorsyncValue();
23808     },
23809    
23810     
23811     createFontOptions : function(){
23812         var buf = [], fs = this.fontFamilies, ff, lc;
23813         
23814         
23815         
23816         for(var i = 0, len = fs.length; i< len; i++){
23817             ff = fs[i];
23818             lc = ff.toLowerCase();
23819             buf.push(
23820                 '<option value="',lc,'" style="font-family:',ff,';"',
23821                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23822                     ff,
23823                 '</option>'
23824             );
23825         }
23826         return buf.join('');
23827     },
23828     
23829     toggleSourceEdit : function(sourceEditMode){
23830         
23831         Roo.log("toolbar toogle");
23832         if(sourceEditMode === undefined){
23833             sourceEditMode = !this.sourceEditMode;
23834         }
23835         this.sourceEditMode = sourceEditMode === true;
23836         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23837         // just toggle the button?
23838         if(btn.pressed !== this.sourceEditMode){
23839             btn.toggle(this.sourceEditMode);
23840             return;
23841         }
23842         
23843         if(sourceEditMode){
23844             Roo.log("disabling buttons");
23845             this.tb.items.each(function(item){
23846                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23847                     item.disable();
23848                 }
23849             });
23850           
23851         }else{
23852             Roo.log("enabling buttons");
23853             if(this.editorcore.initialized){
23854                 this.tb.items.each(function(item){
23855                     item.enable();
23856                 });
23857             }
23858             
23859         }
23860         Roo.log("calling toggole on editor");
23861         // tell the editor that it's been pressed..
23862         this.editor.toggleSourceEdit(sourceEditMode);
23863        
23864     },
23865      /**
23866      * Object collection of toolbar tooltips for the buttons in the editor. The key
23867      * is the command id associated with that button and the value is a valid QuickTips object.
23868      * For example:
23869 <pre><code>
23870 {
23871     bold : {
23872         title: 'Bold (Ctrl+B)',
23873         text: 'Make the selected text bold.',
23874         cls: 'x-html-editor-tip'
23875     },
23876     italic : {
23877         title: 'Italic (Ctrl+I)',
23878         text: 'Make the selected text italic.',
23879         cls: 'x-html-editor-tip'
23880     },
23881     ...
23882 </code></pre>
23883     * @type Object
23884      */
23885     buttonTips : {
23886         bold : {
23887             title: 'Bold (Ctrl+B)',
23888             text: 'Make the selected text bold.',
23889             cls: 'x-html-editor-tip'
23890         },
23891         italic : {
23892             title: 'Italic (Ctrl+I)',
23893             text: 'Make the selected text italic.',
23894             cls: 'x-html-editor-tip'
23895         },
23896         underline : {
23897             title: 'Underline (Ctrl+U)',
23898             text: 'Underline the selected text.',
23899             cls: 'x-html-editor-tip'
23900         },
23901         strikethrough : {
23902             title: 'Strikethrough',
23903             text: 'Strikethrough the selected text.',
23904             cls: 'x-html-editor-tip'
23905         },
23906         increasefontsize : {
23907             title: 'Grow Text',
23908             text: 'Increase the font size.',
23909             cls: 'x-html-editor-tip'
23910         },
23911         decreasefontsize : {
23912             title: 'Shrink Text',
23913             text: 'Decrease the font size.',
23914             cls: 'x-html-editor-tip'
23915         },
23916         backcolor : {
23917             title: 'Text Highlight Color',
23918             text: 'Change the background color of the selected text.',
23919             cls: 'x-html-editor-tip'
23920         },
23921         forecolor : {
23922             title: 'Font Color',
23923             text: 'Change the color of the selected text.',
23924             cls: 'x-html-editor-tip'
23925         },
23926         justifyleft : {
23927             title: 'Align Text Left',
23928             text: 'Align text to the left.',
23929             cls: 'x-html-editor-tip'
23930         },
23931         justifycenter : {
23932             title: 'Center Text',
23933             text: 'Center text in the editor.',
23934             cls: 'x-html-editor-tip'
23935         },
23936         justifyright : {
23937             title: 'Align Text Right',
23938             text: 'Align text to the right.',
23939             cls: 'x-html-editor-tip'
23940         },
23941         insertunorderedlist : {
23942             title: 'Bullet List',
23943             text: 'Start a bulleted list.',
23944             cls: 'x-html-editor-tip'
23945         },
23946         insertorderedlist : {
23947             title: 'Numbered List',
23948             text: 'Start a numbered list.',
23949             cls: 'x-html-editor-tip'
23950         },
23951         createlink : {
23952             title: 'Hyperlink',
23953             text: 'Make the selected text a hyperlink.',
23954             cls: 'x-html-editor-tip'
23955         },
23956         sourceedit : {
23957             title: 'Source Edit',
23958             text: 'Switch to source editing mode.',
23959             cls: 'x-html-editor-tip'
23960         }
23961     },
23962     // private
23963     onDestroy : function(){
23964         if(this.rendered){
23965             
23966             this.tb.items.each(function(item){
23967                 if(item.menu){
23968                     item.menu.removeAll();
23969                     if(item.menu.el){
23970                         item.menu.el.destroy();
23971                     }
23972                 }
23973                 item.destroy();
23974             });
23975              
23976         }
23977     },
23978     onFirstFocus: function() {
23979         this.tb.items.each(function(item){
23980            item.enable();
23981         });
23982     }
23983 });
23984
23985
23986
23987
23988 // <script type="text/javascript">
23989 /*
23990  * Based on
23991  * Ext JS Library 1.1.1
23992  * Copyright(c) 2006-2007, Ext JS, LLC.
23993  *  
23994  
23995  */
23996
23997  
23998 /**
23999  * @class Roo.form.HtmlEditor.ToolbarContext
24000  * Context Toolbar
24001  * 
24002  * Usage:
24003  *
24004  new Roo.form.HtmlEditor({
24005     ....
24006     toolbars : [
24007         { xtype: 'ToolbarStandard', styles : {} }
24008         { xtype: 'ToolbarContext', disable : {} }
24009     ]
24010 })
24011
24012      
24013  * 
24014  * @config : {Object} disable List of elements to disable.. (not done yet.)
24015  * @config : {Object} styles  Map of styles available.
24016  * 
24017  */
24018
24019 Roo.form.HtmlEditor.ToolbarContext = function(config)
24020 {
24021     
24022     Roo.apply(this, config);
24023     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24024     // dont call parent... till later.
24025     this.styles = this.styles || {};
24026 }
24027
24028  
24029
24030 Roo.form.HtmlEditor.ToolbarContext.types = {
24031     'IMG' : {
24032         width : {
24033             title: "Width",
24034             width: 40
24035         },
24036         height:  {
24037             title: "Height",
24038             width: 40
24039         },
24040         align: {
24041             title: "Align",
24042             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24043             width : 80
24044             
24045         },
24046         border: {
24047             title: "Border",
24048             width: 40
24049         },
24050         alt: {
24051             title: "Alt",
24052             width: 120
24053         },
24054         src : {
24055             title: "Src",
24056             width: 220
24057         }
24058         
24059     },
24060     'A' : {
24061         name : {
24062             title: "Name",
24063             width: 50
24064         },
24065         target:  {
24066             title: "Target",
24067             width: 120
24068         },
24069         href:  {
24070             title: "Href",
24071             width: 220
24072         } // border?
24073         
24074     },
24075     'TABLE' : {
24076         rows : {
24077             title: "Rows",
24078             width: 20
24079         },
24080         cols : {
24081             title: "Cols",
24082             width: 20
24083         },
24084         width : {
24085             title: "Width",
24086             width: 40
24087         },
24088         height : {
24089             title: "Height",
24090             width: 40
24091         },
24092         border : {
24093             title: "Border",
24094             width: 20
24095         }
24096     },
24097     'TD' : {
24098         width : {
24099             title: "Width",
24100             width: 40
24101         },
24102         height : {
24103             title: "Height",
24104             width: 40
24105         },   
24106         align: {
24107             title: "Align",
24108             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24109             width: 80
24110         },
24111         valign: {
24112             title: "Valign",
24113             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24114             width: 80
24115         },
24116         colspan: {
24117             title: "Colspan",
24118             width: 20
24119             
24120         },
24121          'font-family'  : {
24122             title : "Font",
24123             style : 'fontFamily',
24124             displayField: 'display',
24125             optname : 'font-family',
24126             width: 140
24127         }
24128     },
24129     'INPUT' : {
24130         name : {
24131             title: "name",
24132             width: 120
24133         },
24134         value : {
24135             title: "Value",
24136             width: 120
24137         },
24138         width : {
24139             title: "Width",
24140             width: 40
24141         }
24142     },
24143     'LABEL' : {
24144         'for' : {
24145             title: "For",
24146             width: 120
24147         }
24148     },
24149     'TEXTAREA' : {
24150           name : {
24151             title: "name",
24152             width: 120
24153         },
24154         rows : {
24155             title: "Rows",
24156             width: 20
24157         },
24158         cols : {
24159             title: "Cols",
24160             width: 20
24161         }
24162     },
24163     'SELECT' : {
24164         name : {
24165             title: "name",
24166             width: 120
24167         },
24168         selectoptions : {
24169             title: "Options",
24170             width: 200
24171         }
24172     },
24173     
24174     // should we really allow this??
24175     // should this just be 
24176     'BODY' : {
24177         title : {
24178             title: "Title",
24179             width: 200,
24180             disabled : true
24181         }
24182     },
24183     'SPAN' : {
24184         'font-family'  : {
24185             title : "Font",
24186             style : 'fontFamily',
24187             displayField: 'display',
24188             optname : 'font-family',
24189             width: 140
24190         }
24191     },
24192     'DIV' : {
24193         'font-family'  : {
24194             title : "Font",
24195             style : 'fontFamily',
24196             displayField: 'display',
24197             optname : 'font-family',
24198             width: 140
24199         }
24200     },
24201      'P' : {
24202         'font-family'  : {
24203             title : "Font",
24204             style : 'fontFamily',
24205             displayField: 'display',
24206             optname : 'font-family',
24207             width: 140
24208         }
24209     },
24210     
24211     '*' : {
24212         // empty..
24213     }
24214
24215 };
24216
24217 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24218 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24219
24220 Roo.form.HtmlEditor.ToolbarContext.options = {
24221         'font-family'  : [ 
24222                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24223                 [ 'Courier New', 'Courier New'],
24224                 [ 'Tahoma', 'Tahoma'],
24225                 [ 'Times New Roman,serif', 'Times'],
24226                 [ 'Verdana','Verdana' ]
24227         ]
24228 };
24229
24230 // fixme - these need to be configurable..
24231  
24232
24233 //Roo.form.HtmlEditor.ToolbarContext.types
24234
24235
24236 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
24237     
24238     tb: false,
24239     
24240     rendered: false,
24241     
24242     editor : false,
24243     editorcore : false,
24244     /**
24245      * @cfg {Object} disable  List of toolbar elements to disable
24246          
24247      */
24248     disable : false,
24249     /**
24250      * @cfg {Object} styles List of styles 
24251      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
24252      *
24253      * These must be defined in the page, so they get rendered correctly..
24254      * .headline { }
24255      * TD.underline { }
24256      * 
24257      */
24258     styles : false,
24259     
24260     options: false,
24261     
24262     toolbars : false,
24263     
24264     init : function(editor)
24265     {
24266         this.editor = editor;
24267         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24268         var editorcore = this.editorcore;
24269         
24270         var fid = editorcore.frameId;
24271         var etb = this;
24272         function btn(id, toggle, handler){
24273             var xid = fid + '-'+ id ;
24274             return {
24275                 id : xid,
24276                 cmd : id,
24277                 cls : 'x-btn-icon x-edit-'+id,
24278                 enableToggle:toggle !== false,
24279                 scope: editorcore, // was editor...
24280                 handler:handler||editorcore.relayBtnCmd,
24281                 clickEvent:'mousedown',
24282                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24283                 tabIndex:-1
24284             };
24285         }
24286         // create a new element.
24287         var wdiv = editor.wrap.createChild({
24288                 tag: 'div'
24289             }, editor.wrap.dom.firstChild.nextSibling, true);
24290         
24291         // can we do this more than once??
24292         
24293          // stop form submits
24294       
24295  
24296         // disable everything...
24297         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24298         this.toolbars = {};
24299            
24300         for (var i in  ty) {
24301           
24302             this.toolbars[i] = this.buildToolbar(ty[i],i);
24303         }
24304         this.tb = this.toolbars.BODY;
24305         this.tb.el.show();
24306         this.buildFooter();
24307         this.footer.show();
24308         editor.on('hide', function( ) { this.footer.hide() }, this);
24309         editor.on('show', function( ) { this.footer.show() }, this);
24310         
24311          
24312         this.rendered = true;
24313         
24314         // the all the btns;
24315         editor.on('editorevent', this.updateToolbar, this);
24316         // other toolbars need to implement this..
24317         //editor.on('editmodechange', this.updateToolbar, this);
24318     },
24319     
24320     
24321     
24322     /**
24323      * Protected method that will not generally be called directly. It triggers
24324      * a toolbar update by reading the markup state of the current selection in the editor.
24325      *
24326      * Note you can force an update by calling on('editorevent', scope, false)
24327      */
24328     updateToolbar: function(editor,ev,sel){
24329
24330         //Roo.log(ev);
24331         // capture mouse up - this is handy for selecting images..
24332         // perhaps should go somewhere else...
24333         if(!this.editorcore.activated){
24334              this.editor.onFirstFocus();
24335             return;
24336         }
24337         
24338         
24339         
24340         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24341         // selectNode - might want to handle IE?
24342         if (ev &&
24343             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24344             ev.target && ev.target.tagName == 'IMG') {
24345             // they have click on an image...
24346             // let's see if we can change the selection...
24347             sel = ev.target;
24348          
24349               var nodeRange = sel.ownerDocument.createRange();
24350             try {
24351                 nodeRange.selectNode(sel);
24352             } catch (e) {
24353                 nodeRange.selectNodeContents(sel);
24354             }
24355             //nodeRange.collapse(true);
24356             var s = this.editorcore.win.getSelection();
24357             s.removeAllRanges();
24358             s.addRange(nodeRange);
24359         }  
24360         
24361       
24362         var updateFooter = sel ? false : true;
24363         
24364         
24365         var ans = this.editorcore.getAllAncestors();
24366         
24367         // pick
24368         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24369         
24370         if (!sel) { 
24371             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24372             sel = sel ? sel : this.editorcore.doc.body;
24373             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24374             
24375         }
24376         // pick a menu that exists..
24377         var tn = sel.tagName.toUpperCase();
24378         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24379         
24380         tn = sel.tagName.toUpperCase();
24381         
24382         var lastSel = this.tb.selectedNode;
24383         
24384         this.tb.selectedNode = sel;
24385         
24386         // if current menu does not match..
24387         
24388         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24389                 
24390             this.tb.el.hide();
24391             ///console.log("show: " + tn);
24392             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24393             this.tb.el.show();
24394             // update name
24395             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24396             
24397             
24398             // update attributes
24399             if (this.tb.fields) {
24400                 this.tb.fields.each(function(e) {
24401                     if (e.stylename) {
24402                         e.setValue(sel.style[e.stylename]);
24403                         return;
24404                     } 
24405                    e.setValue(sel.getAttribute(e.attrname));
24406                 });
24407             }
24408             
24409             var hasStyles = false;
24410             for(var i in this.styles) {
24411                 hasStyles = true;
24412                 break;
24413             }
24414             
24415             // update styles
24416             if (hasStyles) { 
24417                 var st = this.tb.fields.item(0);
24418                 
24419                 st.store.removeAll();
24420                
24421                 
24422                 var cn = sel.className.split(/\s+/);
24423                 
24424                 var avs = [];
24425                 if (this.styles['*']) {
24426                     
24427                     Roo.each(this.styles['*'], function(v) {
24428                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24429                     });
24430                 }
24431                 if (this.styles[tn]) { 
24432                     Roo.each(this.styles[tn], function(v) {
24433                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24434                     });
24435                 }
24436                 
24437                 st.store.loadData(avs);
24438                 st.collapse();
24439                 st.setValue(cn);
24440             }
24441             // flag our selected Node.
24442             this.tb.selectedNode = sel;
24443            
24444            
24445             Roo.menu.MenuMgr.hideAll();
24446
24447         }
24448         
24449         if (!updateFooter) {
24450             //this.footDisp.dom.innerHTML = ''; 
24451             return;
24452         }
24453         // update the footer
24454         //
24455         var html = '';
24456         
24457         this.footerEls = ans.reverse();
24458         Roo.each(this.footerEls, function(a,i) {
24459             if (!a) { return; }
24460             html += html.length ? ' &gt; '  :  '';
24461             
24462             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24463             
24464         });
24465        
24466         // 
24467         var sz = this.footDisp.up('td').getSize();
24468         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24469         this.footDisp.dom.style.marginLeft = '5px';
24470         
24471         this.footDisp.dom.style.overflow = 'hidden';
24472         
24473         this.footDisp.dom.innerHTML = html;
24474             
24475         //this.editorsyncValue();
24476     },
24477      
24478     
24479    
24480        
24481     // private
24482     onDestroy : function(){
24483         if(this.rendered){
24484             
24485             this.tb.items.each(function(item){
24486                 if(item.menu){
24487                     item.menu.removeAll();
24488                     if(item.menu.el){
24489                         item.menu.el.destroy();
24490                     }
24491                 }
24492                 item.destroy();
24493             });
24494              
24495         }
24496     },
24497     onFirstFocus: function() {
24498         // need to do this for all the toolbars..
24499         this.tb.items.each(function(item){
24500            item.enable();
24501         });
24502     },
24503     buildToolbar: function(tlist, nm)
24504     {
24505         var editor = this.editor;
24506         var editorcore = this.editorcore;
24507          // create a new element.
24508         var wdiv = editor.wrap.createChild({
24509                 tag: 'div'
24510             }, editor.wrap.dom.firstChild.nextSibling, true);
24511         
24512        
24513         var tb = new Roo.Toolbar(wdiv);
24514         // add the name..
24515         
24516         tb.add(nm+ ":&nbsp;");
24517         
24518         var styles = [];
24519         for(var i in this.styles) {
24520             styles.push(i);
24521         }
24522         
24523         // styles...
24524         if (styles && styles.length) {
24525             
24526             // this needs a multi-select checkbox...
24527             tb.addField( new Roo.form.ComboBox({
24528                 store: new Roo.data.SimpleStore({
24529                     id : 'val',
24530                     fields: ['val', 'selected'],
24531                     data : [] 
24532                 }),
24533                 name : '-roo-edit-className',
24534                 attrname : 'className',
24535                 displayField: 'val',
24536                 typeAhead: false,
24537                 mode: 'local',
24538                 editable : false,
24539                 triggerAction: 'all',
24540                 emptyText:'Select Style',
24541                 selectOnFocus:true,
24542                 width: 130,
24543                 listeners : {
24544                     'select': function(c, r, i) {
24545                         // initial support only for on class per el..
24546                         tb.selectedNode.className =  r ? r.get('val') : '';
24547                         editorcore.syncValue();
24548                     }
24549                 }
24550     
24551             }));
24552         }
24553         
24554         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24555         var tbops = tbc.options;
24556         
24557         for (var i in tlist) {
24558             
24559             var item = tlist[i];
24560             tb.add(item.title + ":&nbsp;");
24561             
24562             
24563             //optname == used so you can configure the options available..
24564             var opts = item.opts ? item.opts : false;
24565             if (item.optname) {
24566                 opts = tbops[item.optname];
24567            
24568             }
24569             
24570             if (opts) {
24571                 // opts == pulldown..
24572                 tb.addField( new Roo.form.ComboBox({
24573                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24574                         id : 'val',
24575                         fields: ['val', 'display'],
24576                         data : opts  
24577                     }),
24578                     name : '-roo-edit-' + i,
24579                     attrname : i,
24580                     stylename : item.style ? item.style : false,
24581                     displayField: item.displayField ? item.displayField : 'val',
24582                     valueField :  'val',
24583                     typeAhead: false,
24584                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24585                     editable : false,
24586                     triggerAction: 'all',
24587                     emptyText:'Select',
24588                     selectOnFocus:true,
24589                     width: item.width ? item.width  : 130,
24590                     listeners : {
24591                         'select': function(c, r, i) {
24592                             if (c.stylename) {
24593                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24594                                 return;
24595                             }
24596                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24597                         }
24598                     }
24599
24600                 }));
24601                 continue;
24602                     
24603                  
24604                 
24605                 tb.addField( new Roo.form.TextField({
24606                     name: i,
24607                     width: 100,
24608                     //allowBlank:false,
24609                     value: ''
24610                 }));
24611                 continue;
24612             }
24613             tb.addField( new Roo.form.TextField({
24614                 name: '-roo-edit-' + i,
24615                 attrname : i,
24616                 
24617                 width: item.width,
24618                 //allowBlank:true,
24619                 value: '',
24620                 listeners: {
24621                     'change' : function(f, nv, ov) {
24622                         tb.selectedNode.setAttribute(f.attrname, nv);
24623                         editorcore.syncValue();
24624                     }
24625                 }
24626             }));
24627              
24628         }
24629         
24630         var _this = this;
24631         
24632         if(nm == 'BODY'){
24633             tb.addSeparator();
24634         
24635             tb.addButton( {
24636                 text: 'Stylesheets',
24637
24638                 listeners : {
24639                     click : function ()
24640                     {
24641                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24642                     }
24643                 }
24644             });
24645         }
24646         
24647         tb.addFill();
24648         tb.addButton( {
24649             text: 'Remove Tag',
24650     
24651             listeners : {
24652                 click : function ()
24653                 {
24654                     // remove
24655                     // undo does not work.
24656                      
24657                     var sn = tb.selectedNode;
24658                     
24659                     var pn = sn.parentNode;
24660                     
24661                     var stn =  sn.childNodes[0];
24662                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24663                     while (sn.childNodes.length) {
24664                         var node = sn.childNodes[0];
24665                         sn.removeChild(node);
24666                         //Roo.log(node);
24667                         pn.insertBefore(node, sn);
24668                         
24669                     }
24670                     pn.removeChild(sn);
24671                     var range = editorcore.createRange();
24672         
24673                     range.setStart(stn,0);
24674                     range.setEnd(en,0); //????
24675                     //range.selectNode(sel);
24676                     
24677                     
24678                     var selection = editorcore.getSelection();
24679                     selection.removeAllRanges();
24680                     selection.addRange(range);
24681                     
24682                     
24683                     
24684                     //_this.updateToolbar(null, null, pn);
24685                     _this.updateToolbar(null, null, null);
24686                     _this.footDisp.dom.innerHTML = ''; 
24687                 }
24688             }
24689             
24690                     
24691                 
24692             
24693         });
24694         
24695         
24696         tb.el.on('click', function(e){
24697             e.preventDefault(); // what does this do?
24698         });
24699         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24700         tb.el.hide();
24701         tb.name = nm;
24702         // dont need to disable them... as they will get hidden
24703         return tb;
24704          
24705         
24706     },
24707     buildFooter : function()
24708     {
24709         
24710         var fel = this.editor.wrap.createChild();
24711         this.footer = new Roo.Toolbar(fel);
24712         // toolbar has scrolly on left / right?
24713         var footDisp= new Roo.Toolbar.Fill();
24714         var _t = this;
24715         this.footer.add(
24716             {
24717                 text : '&lt;',
24718                 xtype: 'Button',
24719                 handler : function() {
24720                     _t.footDisp.scrollTo('left',0,true)
24721                 }
24722             }
24723         );
24724         this.footer.add( footDisp );
24725         this.footer.add( 
24726             {
24727                 text : '&gt;',
24728                 xtype: 'Button',
24729                 handler : function() {
24730                     // no animation..
24731                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24732                 }
24733             }
24734         );
24735         var fel = Roo.get(footDisp.el);
24736         fel.addClass('x-editor-context');
24737         this.footDispWrap = fel; 
24738         this.footDispWrap.overflow  = 'hidden';
24739         
24740         this.footDisp = fel.createChild();
24741         this.footDispWrap.on('click', this.onContextClick, this)
24742         
24743         
24744     },
24745     onContextClick : function (ev,dom)
24746     {
24747         ev.preventDefault();
24748         var  cn = dom.className;
24749         //Roo.log(cn);
24750         if (!cn.match(/x-ed-loc-/)) {
24751             return;
24752         }
24753         var n = cn.split('-').pop();
24754         var ans = this.footerEls;
24755         var sel = ans[n];
24756         
24757          // pick
24758         var range = this.editorcore.createRange();
24759         
24760         range.selectNodeContents(sel);
24761         //range.selectNode(sel);
24762         
24763         
24764         var selection = this.editorcore.getSelection();
24765         selection.removeAllRanges();
24766         selection.addRange(range);
24767         
24768         
24769         
24770         this.updateToolbar(null, null, sel);
24771         
24772         
24773     }
24774     
24775     
24776     
24777     
24778     
24779 });
24780
24781
24782
24783
24784
24785 /*
24786  * Based on:
24787  * Ext JS Library 1.1.1
24788  * Copyright(c) 2006-2007, Ext JS, LLC.
24789  *
24790  * Originally Released Under LGPL - original licence link has changed is not relivant.
24791  *
24792  * Fork - LGPL
24793  * <script type="text/javascript">
24794  */
24795  
24796 /**
24797  * @class Roo.form.BasicForm
24798  * @extends Roo.util.Observable
24799  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24800  * @constructor
24801  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24802  * @param {Object} config Configuration options
24803  */
24804 Roo.form.BasicForm = function(el, config){
24805     this.allItems = [];
24806     this.childForms = [];
24807     Roo.apply(this, config);
24808     /*
24809      * The Roo.form.Field items in this form.
24810      * @type MixedCollection
24811      */
24812      
24813      
24814     this.items = new Roo.util.MixedCollection(false, function(o){
24815         return o.id || (o.id = Roo.id());
24816     });
24817     this.addEvents({
24818         /**
24819          * @event beforeaction
24820          * Fires before any action is performed. Return false to cancel the action.
24821          * @param {Form} this
24822          * @param {Action} action The action to be performed
24823          */
24824         beforeaction: true,
24825         /**
24826          * @event actionfailed
24827          * Fires when an action fails.
24828          * @param {Form} this
24829          * @param {Action} action The action that failed
24830          */
24831         actionfailed : true,
24832         /**
24833          * @event actioncomplete
24834          * Fires when an action is completed.
24835          * @param {Form} this
24836          * @param {Action} action The action that completed
24837          */
24838         actioncomplete : true
24839     });
24840     if(el){
24841         this.initEl(el);
24842     }
24843     Roo.form.BasicForm.superclass.constructor.call(this);
24844     
24845     Roo.form.BasicForm.popover.apply();
24846 };
24847
24848 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24849     /**
24850      * @cfg {String} method
24851      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24852      */
24853     /**
24854      * @cfg {DataReader} reader
24855      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24856      * This is optional as there is built-in support for processing JSON.
24857      */
24858     /**
24859      * @cfg {DataReader} errorReader
24860      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24861      * This is completely optional as there is built-in support for processing JSON.
24862      */
24863     /**
24864      * @cfg {String} url
24865      * The URL to use for form actions if one isn't supplied in the action options.
24866      */
24867     /**
24868      * @cfg {Boolean} fileUpload
24869      * Set to true if this form is a file upload.
24870      */
24871      
24872     /**
24873      * @cfg {Object} baseParams
24874      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24875      */
24876      /**
24877      
24878     /**
24879      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24880      */
24881     timeout: 30,
24882
24883     // private
24884     activeAction : null,
24885
24886     /**
24887      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24888      * or setValues() data instead of when the form was first created.
24889      */
24890     trackResetOnLoad : false,
24891     
24892     
24893     /**
24894      * childForms - used for multi-tab forms
24895      * @type {Array}
24896      */
24897     childForms : false,
24898     
24899     /**
24900      * allItems - full list of fields.
24901      * @type {Array}
24902      */
24903     allItems : false,
24904     
24905     /**
24906      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24907      * element by passing it or its id or mask the form itself by passing in true.
24908      * @type Mixed
24909      */
24910     waitMsgTarget : false,
24911     
24912     /**
24913      * @type Boolean
24914      */
24915     disableMask : false,
24916     
24917     /**
24918      * @cfg {Boolean} errorMask (true|false) default false
24919      */
24920     errorMask : false,
24921     
24922     /**
24923      * @cfg {Number} maskOffset Default 100
24924      */
24925     maskOffset : 100,
24926
24927     // private
24928     initEl : function(el){
24929         this.el = Roo.get(el);
24930         this.id = this.el.id || Roo.id();
24931         this.el.on('submit', this.onSubmit, this);
24932         this.el.addClass('x-form');
24933     },
24934
24935     // private
24936     onSubmit : function(e){
24937         e.stopEvent();
24938     },
24939
24940     /**
24941      * Returns true if client-side validation on the form is successful.
24942      * @return Boolean
24943      */
24944     isValid : function(){
24945         var valid = true;
24946         var target = false;
24947         this.items.each(function(f){
24948             if(f.validate()){
24949                 return;
24950             }
24951             
24952             valid = false;
24953                 
24954             if(!target && f.el.isVisible(true)){
24955                 target = f;
24956             }
24957         });
24958         
24959         if(this.errorMask && !valid){
24960             Roo.form.BasicForm.popover.mask(this, target);
24961         }
24962         
24963         return valid;
24964     },
24965
24966     /**
24967      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24968      * @return Boolean
24969      */
24970     isDirty : function(){
24971         var dirty = false;
24972         this.items.each(function(f){
24973            if(f.isDirty()){
24974                dirty = true;
24975                return false;
24976            }
24977         });
24978         return dirty;
24979     },
24980     
24981     /**
24982      * Returns true if any fields in this form have changed since their original load. (New version)
24983      * @return Boolean
24984      */
24985     
24986     hasChanged : function()
24987     {
24988         var dirty = false;
24989         this.items.each(function(f){
24990            if(f.hasChanged()){
24991                dirty = true;
24992                return false;
24993            }
24994         });
24995         return dirty;
24996         
24997     },
24998     /**
24999      * Resets all hasChanged to 'false' -
25000      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
25001      * So hasChanged storage is only to be used for this purpose
25002      * @return Boolean
25003      */
25004     resetHasChanged : function()
25005     {
25006         this.items.each(function(f){
25007            f.resetHasChanged();
25008         });
25009         
25010     },
25011     
25012     
25013     /**
25014      * Performs a predefined action (submit or load) or custom actions you define on this form.
25015      * @param {String} actionName The name of the action type
25016      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25017      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25018      * accept other config options):
25019      * <pre>
25020 Property          Type             Description
25021 ----------------  ---------------  ----------------------------------------------------------------------------------
25022 url               String           The url for the action (defaults to the form's url)
25023 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25024 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25025 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25026                                    validate the form on the client (defaults to false)
25027      * </pre>
25028      * @return {BasicForm} this
25029      */
25030     doAction : function(action, options){
25031         if(typeof action == 'string'){
25032             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25033         }
25034         if(this.fireEvent('beforeaction', this, action) !== false){
25035             this.beforeAction(action);
25036             action.run.defer(100, action);
25037         }
25038         return this;
25039     },
25040
25041     /**
25042      * Shortcut to do a submit action.
25043      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25044      * @return {BasicForm} this
25045      */
25046     submit : function(options){
25047         this.doAction('submit', options);
25048         return this;
25049     },
25050
25051     /**
25052      * Shortcut to do a load action.
25053      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25054      * @return {BasicForm} this
25055      */
25056     load : function(options){
25057         this.doAction('load', options);
25058         return this;
25059     },
25060
25061     /**
25062      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25063      * @param {Record} record The record to edit
25064      * @return {BasicForm} this
25065      */
25066     updateRecord : function(record){
25067         record.beginEdit();
25068         var fs = record.fields;
25069         fs.each(function(f){
25070             var field = this.findField(f.name);
25071             if(field){
25072                 record.set(f.name, field.getValue());
25073             }
25074         }, this);
25075         record.endEdit();
25076         return this;
25077     },
25078
25079     /**
25080      * Loads an Roo.data.Record into this form.
25081      * @param {Record} record The record to load
25082      * @return {BasicForm} this
25083      */
25084     loadRecord : function(record){
25085         this.setValues(record.data);
25086         return this;
25087     },
25088
25089     // private
25090     beforeAction : function(action){
25091         var o = action.options;
25092         
25093         if(!this.disableMask) {
25094             if(this.waitMsgTarget === true){
25095                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25096             }else if(this.waitMsgTarget){
25097                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25098                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25099             }else {
25100                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25101             }
25102         }
25103         
25104          
25105     },
25106
25107     // private
25108     afterAction : function(action, success){
25109         this.activeAction = null;
25110         var o = action.options;
25111         
25112         if(!this.disableMask) {
25113             if(this.waitMsgTarget === true){
25114                 this.el.unmask();
25115             }else if(this.waitMsgTarget){
25116                 this.waitMsgTarget.unmask();
25117             }else{
25118                 Roo.MessageBox.updateProgress(1);
25119                 Roo.MessageBox.hide();
25120             }
25121         }
25122         
25123         if(success){
25124             if(o.reset){
25125                 this.reset();
25126             }
25127             Roo.callback(o.success, o.scope, [this, action]);
25128             this.fireEvent('actioncomplete', this, action);
25129             
25130         }else{
25131             
25132             // failure condition..
25133             // we have a scenario where updates need confirming.
25134             // eg. if a locking scenario exists..
25135             // we look for { errors : { needs_confirm : true }} in the response.
25136             if (
25137                 (typeof(action.result) != 'undefined')  &&
25138                 (typeof(action.result.errors) != 'undefined')  &&
25139                 (typeof(action.result.errors.needs_confirm) != 'undefined')
25140            ){
25141                 var _t = this;
25142                 Roo.MessageBox.confirm(
25143                     "Change requires confirmation",
25144                     action.result.errorMsg,
25145                     function(r) {
25146                         if (r != 'yes') {
25147                             return;
25148                         }
25149                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
25150                     }
25151                     
25152                 );
25153                 
25154                 
25155                 
25156                 return;
25157             }
25158             
25159             Roo.callback(o.failure, o.scope, [this, action]);
25160             // show an error message if no failed handler is set..
25161             if (!this.hasListener('actionfailed')) {
25162                 Roo.MessageBox.alert("Error",
25163                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25164                         action.result.errorMsg :
25165                         "Saving Failed, please check your entries or try again"
25166                 );
25167             }
25168             
25169             this.fireEvent('actionfailed', this, action);
25170         }
25171         
25172     },
25173
25174     /**
25175      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25176      * @param {String} id The value to search for
25177      * @return Field
25178      */
25179     findField : function(id){
25180         var field = this.items.get(id);
25181         if(!field){
25182             this.items.each(function(f){
25183                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25184                     field = f;
25185                     return false;
25186                 }
25187             });
25188         }
25189         return field || null;
25190     },
25191
25192     /**
25193      * Add a secondary form to this one, 
25194      * Used to provide tabbed forms. One form is primary, with hidden values 
25195      * which mirror the elements from the other forms.
25196      * 
25197      * @param {Roo.form.Form} form to add.
25198      * 
25199      */
25200     addForm : function(form)
25201     {
25202        
25203         if (this.childForms.indexOf(form) > -1) {
25204             // already added..
25205             return;
25206         }
25207         this.childForms.push(form);
25208         var n = '';
25209         Roo.each(form.allItems, function (fe) {
25210             
25211             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25212             if (this.findField(n)) { // already added..
25213                 return;
25214             }
25215             var add = new Roo.form.Hidden({
25216                 name : n
25217             });
25218             add.render(this.el);
25219             
25220             this.add( add );
25221         }, this);
25222         
25223     },
25224     /**
25225      * Mark fields in this form invalid in bulk.
25226      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25227      * @return {BasicForm} this
25228      */
25229     markInvalid : function(errors){
25230         if(errors instanceof Array){
25231             for(var i = 0, len = errors.length; i < len; i++){
25232                 var fieldError = errors[i];
25233                 var f = this.findField(fieldError.id);
25234                 if(f){
25235                     f.markInvalid(fieldError.msg);
25236                 }
25237             }
25238         }else{
25239             var field, id;
25240             for(id in errors){
25241                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25242                     field.markInvalid(errors[id]);
25243                 }
25244             }
25245         }
25246         Roo.each(this.childForms || [], function (f) {
25247             f.markInvalid(errors);
25248         });
25249         
25250         return this;
25251     },
25252
25253     /**
25254      * Set values for fields in this form in bulk.
25255      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25256      * @return {BasicForm} this
25257      */
25258     setValues : function(values){
25259         if(values instanceof Array){ // array of objects
25260             for(var i = 0, len = values.length; i < len; i++){
25261                 var v = values[i];
25262                 var f = this.findField(v.id);
25263                 if(f){
25264                     f.setValue(v.value);
25265                     if(this.trackResetOnLoad){
25266                         f.originalValue = f.getValue();
25267                     }
25268                 }
25269             }
25270         }else{ // object hash
25271             var field, id;
25272             for(id in values){
25273                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25274                     
25275                     if (field.setFromData && 
25276                         field.valueField && 
25277                         field.displayField &&
25278                         // combos' with local stores can 
25279                         // be queried via setValue()
25280                         // to set their value..
25281                         (field.store && !field.store.isLocal)
25282                         ) {
25283                         // it's a combo
25284                         var sd = { };
25285                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25286                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25287                         field.setFromData(sd);
25288                         
25289                     } else {
25290                         field.setValue(values[id]);
25291                     }
25292                     
25293                     
25294                     if(this.trackResetOnLoad){
25295                         field.originalValue = field.getValue();
25296                     }
25297                 }
25298             }
25299         }
25300         this.resetHasChanged();
25301         
25302         
25303         Roo.each(this.childForms || [], function (f) {
25304             f.setValues(values);
25305             f.resetHasChanged();
25306         });
25307                 
25308         return this;
25309     },
25310  
25311     /**
25312      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25313      * they are returned as an array.
25314      * @param {Boolean} asString
25315      * @return {Object}
25316      */
25317     getValues : function(asString){
25318         if (this.childForms) {
25319             // copy values from the child forms
25320             Roo.each(this.childForms, function (f) {
25321                 this.setValues(f.getValues());
25322             }, this);
25323         }
25324         
25325         // use formdata
25326         if (typeof(FormData) != 'undefined' && asString !== true) {
25327             var fd = (new FormData(this.el.dom)).entries();
25328             var ret = {};
25329             var ent = fd.next();
25330             while (!ent.done) {
25331                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25332                 ent = fd.next();
25333             };
25334             return ret;
25335         }
25336         
25337         
25338         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25339         if(asString === true){
25340             return fs;
25341         }
25342         return Roo.urlDecode(fs);
25343     },
25344     
25345     /**
25346      * Returns the fields in this form as an object with key/value pairs. 
25347      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25348      * @return {Object}
25349      */
25350     getFieldValues : function(with_hidden)
25351     {
25352         if (this.childForms) {
25353             // copy values from the child forms
25354             // should this call getFieldValues - probably not as we do not currently copy
25355             // hidden fields when we generate..
25356             Roo.each(this.childForms, function (f) {
25357                 this.setValues(f.getValues());
25358             }, this);
25359         }
25360         
25361         var ret = {};
25362         this.items.each(function(f){
25363             if (!f.getName()) {
25364                 return;
25365             }
25366             var v = f.getValue();
25367             if (f.inputType =='radio') {
25368                 if (typeof(ret[f.getName()]) == 'undefined') {
25369                     ret[f.getName()] = ''; // empty..
25370                 }
25371                 
25372                 if (!f.el.dom.checked) {
25373                     return;
25374                     
25375                 }
25376                 v = f.el.dom.value;
25377                 
25378             }
25379             
25380             // not sure if this supported any more..
25381             if ((typeof(v) == 'object') && f.getRawValue) {
25382                 v = f.getRawValue() ; // dates..
25383             }
25384             // combo boxes where name != hiddenName...
25385             if (f.name != f.getName()) {
25386                 ret[f.name] = f.getRawValue();
25387             }
25388             ret[f.getName()] = v;
25389         });
25390         
25391         return ret;
25392     },
25393
25394     /**
25395      * Clears all invalid messages in this form.
25396      * @return {BasicForm} this
25397      */
25398     clearInvalid : function(){
25399         this.items.each(function(f){
25400            f.clearInvalid();
25401         });
25402         
25403         Roo.each(this.childForms || [], function (f) {
25404             f.clearInvalid();
25405         });
25406         
25407         
25408         return this;
25409     },
25410
25411     /**
25412      * Resets this form.
25413      * @return {BasicForm} this
25414      */
25415     reset : function(){
25416         this.items.each(function(f){
25417             f.reset();
25418         });
25419         
25420         Roo.each(this.childForms || [], function (f) {
25421             f.reset();
25422         });
25423         this.resetHasChanged();
25424         
25425         return this;
25426     },
25427
25428     /**
25429      * Add Roo.form components to this form.
25430      * @param {Field} field1
25431      * @param {Field} field2 (optional)
25432      * @param {Field} etc (optional)
25433      * @return {BasicForm} this
25434      */
25435     add : function(){
25436         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25437         return this;
25438     },
25439
25440
25441     /**
25442      * Removes a field from the items collection (does NOT remove its markup).
25443      * @param {Field} field
25444      * @return {BasicForm} this
25445      */
25446     remove : function(field){
25447         this.items.remove(field);
25448         return this;
25449     },
25450
25451     /**
25452      * Looks at the fields in this form, checks them for an id attribute,
25453      * and calls applyTo on the existing dom element with that id.
25454      * @return {BasicForm} this
25455      */
25456     render : function(){
25457         this.items.each(function(f){
25458             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25459                 f.applyTo(f.id);
25460             }
25461         });
25462         return this;
25463     },
25464
25465     /**
25466      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25467      * @param {Object} values
25468      * @return {BasicForm} this
25469      */
25470     applyToFields : function(o){
25471         this.items.each(function(f){
25472            Roo.apply(f, o);
25473         });
25474         return this;
25475     },
25476
25477     /**
25478      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25479      * @param {Object} values
25480      * @return {BasicForm} this
25481      */
25482     applyIfToFields : function(o){
25483         this.items.each(function(f){
25484            Roo.applyIf(f, o);
25485         });
25486         return this;
25487     }
25488 });
25489
25490 // back compat
25491 Roo.BasicForm = Roo.form.BasicForm;
25492
25493 Roo.apply(Roo.form.BasicForm, {
25494     
25495     popover : {
25496         
25497         padding : 5,
25498         
25499         isApplied : false,
25500         
25501         isMasked : false,
25502         
25503         form : false,
25504         
25505         target : false,
25506         
25507         intervalID : false,
25508         
25509         maskEl : false,
25510         
25511         apply : function()
25512         {
25513             if(this.isApplied){
25514                 return;
25515             }
25516             
25517             this.maskEl = {
25518                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25519                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25520                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25521                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25522             };
25523             
25524             this.maskEl.top.enableDisplayMode("block");
25525             this.maskEl.left.enableDisplayMode("block");
25526             this.maskEl.bottom.enableDisplayMode("block");
25527             this.maskEl.right.enableDisplayMode("block");
25528             
25529             Roo.get(document.body).on('click', function(){
25530                 this.unmask();
25531             }, this);
25532             
25533             Roo.get(document.body).on('touchstart', function(){
25534                 this.unmask();
25535             }, this);
25536             
25537             this.isApplied = true
25538         },
25539         
25540         mask : function(form, target)
25541         {
25542             this.form = form;
25543             
25544             this.target = target;
25545             
25546             if(!this.form.errorMask || !target.el){
25547                 return;
25548             }
25549             
25550             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25551             
25552             var ot = this.target.el.calcOffsetsTo(scrollable);
25553             
25554             var scrollTo = ot[1] - this.form.maskOffset;
25555             
25556             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25557             
25558             scrollable.scrollTo('top', scrollTo);
25559             
25560             var el = this.target.wrap || this.target.el;
25561             
25562             var box = el.getBox();
25563             
25564             this.maskEl.top.setStyle('position', 'absolute');
25565             this.maskEl.top.setStyle('z-index', 10000);
25566             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25567             this.maskEl.top.setLeft(0);
25568             this.maskEl.top.setTop(0);
25569             this.maskEl.top.show();
25570             
25571             this.maskEl.left.setStyle('position', 'absolute');
25572             this.maskEl.left.setStyle('z-index', 10000);
25573             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25574             this.maskEl.left.setLeft(0);
25575             this.maskEl.left.setTop(box.y - this.padding);
25576             this.maskEl.left.show();
25577
25578             this.maskEl.bottom.setStyle('position', 'absolute');
25579             this.maskEl.bottom.setStyle('z-index', 10000);
25580             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25581             this.maskEl.bottom.setLeft(0);
25582             this.maskEl.bottom.setTop(box.bottom + this.padding);
25583             this.maskEl.bottom.show();
25584
25585             this.maskEl.right.setStyle('position', 'absolute');
25586             this.maskEl.right.setStyle('z-index', 10000);
25587             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25588             this.maskEl.right.setLeft(box.right + this.padding);
25589             this.maskEl.right.setTop(box.y - this.padding);
25590             this.maskEl.right.show();
25591
25592             this.intervalID = window.setInterval(function() {
25593                 Roo.form.BasicForm.popover.unmask();
25594             }, 10000);
25595
25596             window.onwheel = function(){ return false;};
25597             
25598             (function(){ this.isMasked = true; }).defer(500, this);
25599             
25600         },
25601         
25602         unmask : function()
25603         {
25604             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25605                 return;
25606             }
25607             
25608             this.maskEl.top.setStyle('position', 'absolute');
25609             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25610             this.maskEl.top.hide();
25611
25612             this.maskEl.left.setStyle('position', 'absolute');
25613             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25614             this.maskEl.left.hide();
25615
25616             this.maskEl.bottom.setStyle('position', 'absolute');
25617             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25618             this.maskEl.bottom.hide();
25619
25620             this.maskEl.right.setStyle('position', 'absolute');
25621             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25622             this.maskEl.right.hide();
25623             
25624             window.onwheel = function(){ return true;};
25625             
25626             if(this.intervalID){
25627                 window.clearInterval(this.intervalID);
25628                 this.intervalID = false;
25629             }
25630             
25631             this.isMasked = false;
25632             
25633         }
25634         
25635     }
25636     
25637 });/*
25638  * Based on:
25639  * Ext JS Library 1.1.1
25640  * Copyright(c) 2006-2007, Ext JS, LLC.
25641  *
25642  * Originally Released Under LGPL - original licence link has changed is not relivant.
25643  *
25644  * Fork - LGPL
25645  * <script type="text/javascript">
25646  */
25647
25648 /**
25649  * @class Roo.form.Form
25650  * @extends Roo.form.BasicForm
25651  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25652  * @constructor
25653  * @param {Object} config Configuration options
25654  */
25655 Roo.form.Form = function(config){
25656     var xitems =  [];
25657     if (config.items) {
25658         xitems = config.items;
25659         delete config.items;
25660     }
25661    
25662     
25663     Roo.form.Form.superclass.constructor.call(this, null, config);
25664     this.url = this.url || this.action;
25665     if(!this.root){
25666         this.root = new Roo.form.Layout(Roo.applyIf({
25667             id: Roo.id()
25668         }, config));
25669     }
25670     this.active = this.root;
25671     /**
25672      * Array of all the buttons that have been added to this form via {@link addButton}
25673      * @type Array
25674      */
25675     this.buttons = [];
25676     this.allItems = [];
25677     this.addEvents({
25678         /**
25679          * @event clientvalidation
25680          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25681          * @param {Form} this
25682          * @param {Boolean} valid true if the form has passed client-side validation
25683          */
25684         clientvalidation: true,
25685         /**
25686          * @event rendered
25687          * Fires when the form is rendered
25688          * @param {Roo.form.Form} form
25689          */
25690         rendered : true
25691     });
25692     
25693     if (this.progressUrl) {
25694             // push a hidden field onto the list of fields..
25695             this.addxtype( {
25696                     xns: Roo.form, 
25697                     xtype : 'Hidden', 
25698                     name : 'UPLOAD_IDENTIFIER' 
25699             });
25700         }
25701         
25702     
25703     Roo.each(xitems, this.addxtype, this);
25704     
25705 };
25706
25707 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25708     /**
25709      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25710      */
25711     /**
25712      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25713      */
25714     /**
25715      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25716      */
25717     buttonAlign:'center',
25718
25719     /**
25720      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25721      */
25722     minButtonWidth:75,
25723
25724     /**
25725      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25726      * This property cascades to child containers if not set.
25727      */
25728     labelAlign:'left',
25729
25730     /**
25731      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25732      * fires a looping event with that state. This is required to bind buttons to the valid
25733      * state using the config value formBind:true on the button.
25734      */
25735     monitorValid : false,
25736
25737     /**
25738      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25739      */
25740     monitorPoll : 200,
25741     
25742     /**
25743      * @cfg {String} progressUrl - Url to return progress data 
25744      */
25745     
25746     progressUrl : false,
25747     /**
25748      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25749      * sending a formdata with extra parameters - eg uploaded elements.
25750      */
25751     
25752     formData : false,
25753     
25754     /**
25755      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25756      * fields are added and the column is closed. If no fields are passed the column remains open
25757      * until end() is called.
25758      * @param {Object} config The config to pass to the column
25759      * @param {Field} field1 (optional)
25760      * @param {Field} field2 (optional)
25761      * @param {Field} etc (optional)
25762      * @return Column The column container object
25763      */
25764     column : function(c){
25765         var col = new Roo.form.Column(c);
25766         this.start(col);
25767         if(arguments.length > 1){ // duplicate code required because of Opera
25768             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25769             this.end();
25770         }
25771         return col;
25772     },
25773
25774     /**
25775      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25776      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25777      * until end() is called.
25778      * @param {Object} config The config to pass to the fieldset
25779      * @param {Field} field1 (optional)
25780      * @param {Field} field2 (optional)
25781      * @param {Field} etc (optional)
25782      * @return FieldSet The fieldset container object
25783      */
25784     fieldset : function(c){
25785         var fs = new Roo.form.FieldSet(c);
25786         this.start(fs);
25787         if(arguments.length > 1){ // duplicate code required because of Opera
25788             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25789             this.end();
25790         }
25791         return fs;
25792     },
25793
25794     /**
25795      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25796      * fields are added and the container is closed. If no fields are passed the container remains open
25797      * until end() is called.
25798      * @param {Object} config The config to pass to the Layout
25799      * @param {Field} field1 (optional)
25800      * @param {Field} field2 (optional)
25801      * @param {Field} etc (optional)
25802      * @return Layout The container object
25803      */
25804     container : function(c){
25805         var l = new Roo.form.Layout(c);
25806         this.start(l);
25807         if(arguments.length > 1){ // duplicate code required because of Opera
25808             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25809             this.end();
25810         }
25811         return l;
25812     },
25813
25814     /**
25815      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25816      * @param {Object} container A Roo.form.Layout or subclass of Layout
25817      * @return {Form} this
25818      */
25819     start : function(c){
25820         // cascade label info
25821         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25822         this.active.stack.push(c);
25823         c.ownerCt = this.active;
25824         this.active = c;
25825         return this;
25826     },
25827
25828     /**
25829      * Closes the current open container
25830      * @return {Form} this
25831      */
25832     end : function(){
25833         if(this.active == this.root){
25834             return this;
25835         }
25836         this.active = this.active.ownerCt;
25837         return this;
25838     },
25839
25840     /**
25841      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25842      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25843      * as the label of the field.
25844      * @param {Field} field1
25845      * @param {Field} field2 (optional)
25846      * @param {Field} etc. (optional)
25847      * @return {Form} this
25848      */
25849     add : function(){
25850         this.active.stack.push.apply(this.active.stack, arguments);
25851         this.allItems.push.apply(this.allItems,arguments);
25852         var r = [];
25853         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25854             if(a[i].isFormField){
25855                 r.push(a[i]);
25856             }
25857         }
25858         if(r.length > 0){
25859             Roo.form.Form.superclass.add.apply(this, r);
25860         }
25861         return this;
25862     },
25863     
25864
25865     
25866     
25867     
25868      /**
25869      * Find any element that has been added to a form, using it's ID or name
25870      * This can include framesets, columns etc. along with regular fields..
25871      * @param {String} id - id or name to find.
25872      
25873      * @return {Element} e - or false if nothing found.
25874      */
25875     findbyId : function(id)
25876     {
25877         var ret = false;
25878         if (!id) {
25879             return ret;
25880         }
25881         Roo.each(this.allItems, function(f){
25882             if (f.id == id || f.name == id ){
25883                 ret = f;
25884                 return false;
25885             }
25886         });
25887         return ret;
25888     },
25889
25890     
25891     
25892     /**
25893      * Render this form into the passed container. This should only be called once!
25894      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25895      * @return {Form} this
25896      */
25897     render : function(ct)
25898     {
25899         
25900         
25901         
25902         ct = Roo.get(ct);
25903         var o = this.autoCreate || {
25904             tag: 'form',
25905             method : this.method || 'POST',
25906             id : this.id || Roo.id()
25907         };
25908         this.initEl(ct.createChild(o));
25909
25910         this.root.render(this.el);
25911         
25912        
25913              
25914         this.items.each(function(f){
25915             f.render('x-form-el-'+f.id);
25916         });
25917
25918         if(this.buttons.length > 0){
25919             // tables are required to maintain order and for correct IE layout
25920             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25921                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25922                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25923             }}, null, true);
25924             var tr = tb.getElementsByTagName('tr')[0];
25925             for(var i = 0, len = this.buttons.length; i < len; i++) {
25926                 var b = this.buttons[i];
25927                 var td = document.createElement('td');
25928                 td.className = 'x-form-btn-td';
25929                 b.render(tr.appendChild(td));
25930             }
25931         }
25932         if(this.monitorValid){ // initialize after render
25933             this.startMonitoring();
25934         }
25935         this.fireEvent('rendered', this);
25936         return this;
25937     },
25938
25939     /**
25940      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25941      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25942      * object or a valid Roo.DomHelper element config
25943      * @param {Function} handler The function called when the button is clicked
25944      * @param {Object} scope (optional) The scope of the handler function
25945      * @return {Roo.Button}
25946      */
25947     addButton : function(config, handler, scope){
25948         var bc = {
25949             handler: handler,
25950             scope: scope,
25951             minWidth: this.minButtonWidth,
25952             hideParent:true
25953         };
25954         if(typeof config == "string"){
25955             bc.text = config;
25956         }else{
25957             Roo.apply(bc, config);
25958         }
25959         var btn = new Roo.Button(null, bc);
25960         this.buttons.push(btn);
25961         return btn;
25962     },
25963
25964      /**
25965      * Adds a series of form elements (using the xtype property as the factory method.
25966      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25967      * @param {Object} config 
25968      */
25969     
25970     addxtype : function()
25971     {
25972         var ar = Array.prototype.slice.call(arguments, 0);
25973         var ret = false;
25974         for(var i = 0; i < ar.length; i++) {
25975             if (!ar[i]) {
25976                 continue; // skip -- if this happends something invalid got sent, we 
25977                 // should ignore it, as basically that interface element will not show up
25978                 // and that should be pretty obvious!!
25979             }
25980             
25981             if (Roo.form[ar[i].xtype]) {
25982                 ar[i].form = this;
25983                 var fe = Roo.factory(ar[i], Roo.form);
25984                 if (!ret) {
25985                     ret = fe;
25986                 }
25987                 fe.form = this;
25988                 if (fe.store) {
25989                     fe.store.form = this;
25990                 }
25991                 if (fe.isLayout) {  
25992                          
25993                     this.start(fe);
25994                     this.allItems.push(fe);
25995                     if (fe.items && fe.addxtype) {
25996                         fe.addxtype.apply(fe, fe.items);
25997                         delete fe.items;
25998                     }
25999                      this.end();
26000                     continue;
26001                 }
26002                 
26003                 
26004                  
26005                 this.add(fe);
26006               //  console.log('adding ' + ar[i].xtype);
26007             }
26008             if (ar[i].xtype == 'Button') {  
26009                 //console.log('adding button');
26010                 //console.log(ar[i]);
26011                 this.addButton(ar[i]);
26012                 this.allItems.push(fe);
26013                 continue;
26014             }
26015             
26016             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26017                 alert('end is not supported on xtype any more, use items');
26018             //    this.end();
26019             //    //console.log('adding end');
26020             }
26021             
26022         }
26023         return ret;
26024     },
26025     
26026     /**
26027      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26028      * option "monitorValid"
26029      */
26030     startMonitoring : function(){
26031         if(!this.bound){
26032             this.bound = true;
26033             Roo.TaskMgr.start({
26034                 run : this.bindHandler,
26035                 interval : this.monitorPoll || 200,
26036                 scope: this
26037             });
26038         }
26039     },
26040
26041     /**
26042      * Stops monitoring of the valid state of this form
26043      */
26044     stopMonitoring : function(){
26045         this.bound = false;
26046     },
26047
26048     // private
26049     bindHandler : function(){
26050         if(!this.bound){
26051             return false; // stops binding
26052         }
26053         var valid = true;
26054         this.items.each(function(f){
26055             if(!f.isValid(true)){
26056                 valid = false;
26057                 return false;
26058             }
26059         });
26060         for(var i = 0, len = this.buttons.length; i < len; i++){
26061             var btn = this.buttons[i];
26062             if(btn.formBind === true && btn.disabled === valid){
26063                 btn.setDisabled(!valid);
26064             }
26065         }
26066         this.fireEvent('clientvalidation', this, valid);
26067     }
26068     
26069     
26070     
26071     
26072     
26073     
26074     
26075     
26076 });
26077
26078
26079 // back compat
26080 Roo.Form = Roo.form.Form;
26081 /*
26082  * Based on:
26083  * Ext JS Library 1.1.1
26084  * Copyright(c) 2006-2007, Ext JS, LLC.
26085  *
26086  * Originally Released Under LGPL - original licence link has changed is not relivant.
26087  *
26088  * Fork - LGPL
26089  * <script type="text/javascript">
26090  */
26091
26092 // as we use this in bootstrap.
26093 Roo.namespace('Roo.form');
26094  /**
26095  * @class Roo.form.Action
26096  * Internal Class used to handle form actions
26097  * @constructor
26098  * @param {Roo.form.BasicForm} el The form element or its id
26099  * @param {Object} config Configuration options
26100  */
26101
26102  
26103  
26104 // define the action interface
26105 Roo.form.Action = function(form, options){
26106     this.form = form;
26107     this.options = options || {};
26108 };
26109 /**
26110  * Client Validation Failed
26111  * @const 
26112  */
26113 Roo.form.Action.CLIENT_INVALID = 'client';
26114 /**
26115  * Server Validation Failed
26116  * @const 
26117  */
26118 Roo.form.Action.SERVER_INVALID = 'server';
26119  /**
26120  * Connect to Server Failed
26121  * @const 
26122  */
26123 Roo.form.Action.CONNECT_FAILURE = 'connect';
26124 /**
26125  * Reading Data from Server Failed
26126  * @const 
26127  */
26128 Roo.form.Action.LOAD_FAILURE = 'load';
26129
26130 Roo.form.Action.prototype = {
26131     type : 'default',
26132     failureType : undefined,
26133     response : undefined,
26134     result : undefined,
26135
26136     // interface method
26137     run : function(options){
26138
26139     },
26140
26141     // interface method
26142     success : function(response){
26143
26144     },
26145
26146     // interface method
26147     handleResponse : function(response){
26148
26149     },
26150
26151     // default connection failure
26152     failure : function(response){
26153         
26154         this.response = response;
26155         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26156         this.form.afterAction(this, false);
26157     },
26158
26159     processResponse : function(response){
26160         this.response = response;
26161         if(!response.responseText){
26162             return true;
26163         }
26164         this.result = this.handleResponse(response);
26165         return this.result;
26166     },
26167
26168     // utility functions used internally
26169     getUrl : function(appendParams){
26170         var url = this.options.url || this.form.url || this.form.el.dom.action;
26171         if(appendParams){
26172             var p = this.getParams();
26173             if(p){
26174                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26175             }
26176         }
26177         return url;
26178     },
26179
26180     getMethod : function(){
26181         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26182     },
26183
26184     getParams : function(){
26185         var bp = this.form.baseParams;
26186         var p = this.options.params;
26187         if(p){
26188             if(typeof p == "object"){
26189                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26190             }else if(typeof p == 'string' && bp){
26191                 p += '&' + Roo.urlEncode(bp);
26192             }
26193         }else if(bp){
26194             p = Roo.urlEncode(bp);
26195         }
26196         return p;
26197     },
26198
26199     createCallback : function(){
26200         return {
26201             success: this.success,
26202             failure: this.failure,
26203             scope: this,
26204             timeout: (this.form.timeout*1000),
26205             upload: this.form.fileUpload ? this.success : undefined
26206         };
26207     }
26208 };
26209
26210 Roo.form.Action.Submit = function(form, options){
26211     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26212 };
26213
26214 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26215     type : 'submit',
26216
26217     haveProgress : false,
26218     uploadComplete : false,
26219     
26220     // uploadProgress indicator.
26221     uploadProgress : function()
26222     {
26223         if (!this.form.progressUrl) {
26224             return;
26225         }
26226         
26227         if (!this.haveProgress) {
26228             Roo.MessageBox.progress("Uploading", "Uploading");
26229         }
26230         if (this.uploadComplete) {
26231            Roo.MessageBox.hide();
26232            return;
26233         }
26234         
26235         this.haveProgress = true;
26236    
26237         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26238         
26239         var c = new Roo.data.Connection();
26240         c.request({
26241             url : this.form.progressUrl,
26242             params: {
26243                 id : uid
26244             },
26245             method: 'GET',
26246             success : function(req){
26247                //console.log(data);
26248                 var rdata = false;
26249                 var edata;
26250                 try  {
26251                    rdata = Roo.decode(req.responseText)
26252                 } catch (e) {
26253                     Roo.log("Invalid data from server..");
26254                     Roo.log(edata);
26255                     return;
26256                 }
26257                 if (!rdata || !rdata.success) {
26258                     Roo.log(rdata);
26259                     Roo.MessageBox.alert(Roo.encode(rdata));
26260                     return;
26261                 }
26262                 var data = rdata.data;
26263                 
26264                 if (this.uploadComplete) {
26265                    Roo.MessageBox.hide();
26266                    return;
26267                 }
26268                    
26269                 if (data){
26270                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26271                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26272                     );
26273                 }
26274                 this.uploadProgress.defer(2000,this);
26275             },
26276        
26277             failure: function(data) {
26278                 Roo.log('progress url failed ');
26279                 Roo.log(data);
26280             },
26281             scope : this
26282         });
26283            
26284     },
26285     
26286     
26287     run : function()
26288     {
26289         // run get Values on the form, so it syncs any secondary forms.
26290         this.form.getValues();
26291         
26292         var o = this.options;
26293         var method = this.getMethod();
26294         var isPost = method == 'POST';
26295         if(o.clientValidation === false || this.form.isValid()){
26296             
26297             if (this.form.progressUrl) {
26298                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26299                     (new Date() * 1) + '' + Math.random());
26300                     
26301             } 
26302             
26303             
26304             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26305                 form:this.form.el.dom,
26306                 url:this.getUrl(!isPost),
26307                 method: method,
26308                 params:isPost ? this.getParams() : null,
26309                 isUpload: this.form.fileUpload,
26310                 formData : this.form.formData
26311             }));
26312             
26313             this.uploadProgress();
26314
26315         }else if (o.clientValidation !== false){ // client validation failed
26316             this.failureType = Roo.form.Action.CLIENT_INVALID;
26317             this.form.afterAction(this, false);
26318         }
26319     },
26320
26321     success : function(response)
26322     {
26323         this.uploadComplete= true;
26324         if (this.haveProgress) {
26325             Roo.MessageBox.hide();
26326         }
26327         
26328         
26329         var result = this.processResponse(response);
26330         if(result === true || result.success){
26331             this.form.afterAction(this, true);
26332             return;
26333         }
26334         if(result.errors){
26335             this.form.markInvalid(result.errors);
26336             this.failureType = Roo.form.Action.SERVER_INVALID;
26337         }
26338         this.form.afterAction(this, false);
26339     },
26340     failure : function(response)
26341     {
26342         this.uploadComplete= true;
26343         if (this.haveProgress) {
26344             Roo.MessageBox.hide();
26345         }
26346         
26347         this.response = response;
26348         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26349         this.form.afterAction(this, false);
26350     },
26351     
26352     handleResponse : function(response){
26353         if(this.form.errorReader){
26354             var rs = this.form.errorReader.read(response);
26355             var errors = [];
26356             if(rs.records){
26357                 for(var i = 0, len = rs.records.length; i < len; i++) {
26358                     var r = rs.records[i];
26359                     errors[i] = r.data;
26360                 }
26361             }
26362             if(errors.length < 1){
26363                 errors = null;
26364             }
26365             return {
26366                 success : rs.success,
26367                 errors : errors
26368             };
26369         }
26370         var ret = false;
26371         try {
26372             ret = Roo.decode(response.responseText);
26373         } catch (e) {
26374             ret = {
26375                 success: false,
26376                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26377                 errors : []
26378             };
26379         }
26380         return ret;
26381         
26382     }
26383 });
26384
26385
26386 Roo.form.Action.Load = function(form, options){
26387     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26388     this.reader = this.form.reader;
26389 };
26390
26391 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26392     type : 'load',
26393
26394     run : function(){
26395         
26396         Roo.Ajax.request(Roo.apply(
26397                 this.createCallback(), {
26398                     method:this.getMethod(),
26399                     url:this.getUrl(false),
26400                     params:this.getParams()
26401         }));
26402     },
26403
26404     success : function(response){
26405         
26406         var result = this.processResponse(response);
26407         if(result === true || !result.success || !result.data){
26408             this.failureType = Roo.form.Action.LOAD_FAILURE;
26409             this.form.afterAction(this, false);
26410             return;
26411         }
26412         this.form.clearInvalid();
26413         this.form.setValues(result.data);
26414         this.form.afterAction(this, true);
26415     },
26416
26417     handleResponse : function(response){
26418         if(this.form.reader){
26419             var rs = this.form.reader.read(response);
26420             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26421             return {
26422                 success : rs.success,
26423                 data : data
26424             };
26425         }
26426         return Roo.decode(response.responseText);
26427     }
26428 });
26429
26430 Roo.form.Action.ACTION_TYPES = {
26431     'load' : Roo.form.Action.Load,
26432     'submit' : Roo.form.Action.Submit
26433 };/*
26434  * Based on:
26435  * Ext JS Library 1.1.1
26436  * Copyright(c) 2006-2007, Ext JS, LLC.
26437  *
26438  * Originally Released Under LGPL - original licence link has changed is not relivant.
26439  *
26440  * Fork - LGPL
26441  * <script type="text/javascript">
26442  */
26443  
26444 /**
26445  * @class Roo.form.Layout
26446  * @extends Roo.Component
26447  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26448  * @constructor
26449  * @param {Object} config Configuration options
26450  */
26451 Roo.form.Layout = function(config){
26452     var xitems = [];
26453     if (config.items) {
26454         xitems = config.items;
26455         delete config.items;
26456     }
26457     Roo.form.Layout.superclass.constructor.call(this, config);
26458     this.stack = [];
26459     Roo.each(xitems, this.addxtype, this);
26460      
26461 };
26462
26463 Roo.extend(Roo.form.Layout, Roo.Component, {
26464     /**
26465      * @cfg {String/Object} autoCreate
26466      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26467      */
26468     /**
26469      * @cfg {String/Object/Function} style
26470      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26471      * a function which returns such a specification.
26472      */
26473     /**
26474      * @cfg {String} labelAlign
26475      * Valid values are "left," "top" and "right" (defaults to "left")
26476      */
26477     /**
26478      * @cfg {Number} labelWidth
26479      * Fixed width in pixels of all field labels (defaults to undefined)
26480      */
26481     /**
26482      * @cfg {Boolean} clear
26483      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26484      */
26485     clear : true,
26486     /**
26487      * @cfg {String} labelSeparator
26488      * The separator to use after field labels (defaults to ':')
26489      */
26490     labelSeparator : ':',
26491     /**
26492      * @cfg {Boolean} hideLabels
26493      * True to suppress the display of field labels in this layout (defaults to false)
26494      */
26495     hideLabels : false,
26496
26497     // private
26498     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26499     
26500     isLayout : true,
26501     
26502     // private
26503     onRender : function(ct, position){
26504         if(this.el){ // from markup
26505             this.el = Roo.get(this.el);
26506         }else {  // generate
26507             var cfg = this.getAutoCreate();
26508             this.el = ct.createChild(cfg, position);
26509         }
26510         if(this.style){
26511             this.el.applyStyles(this.style);
26512         }
26513         if(this.labelAlign){
26514             this.el.addClass('x-form-label-'+this.labelAlign);
26515         }
26516         if(this.hideLabels){
26517             this.labelStyle = "display:none";
26518             this.elementStyle = "padding-left:0;";
26519         }else{
26520             if(typeof this.labelWidth == 'number'){
26521                 this.labelStyle = "width:"+this.labelWidth+"px;";
26522                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26523             }
26524             if(this.labelAlign == 'top'){
26525                 this.labelStyle = "width:auto;";
26526                 this.elementStyle = "padding-left:0;";
26527             }
26528         }
26529         var stack = this.stack;
26530         var slen = stack.length;
26531         if(slen > 0){
26532             if(!this.fieldTpl){
26533                 var t = new Roo.Template(
26534                     '<div class="x-form-item {5}">',
26535                         '<label for="{0}" style="{2}">{1}{4}</label>',
26536                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26537                         '</div>',
26538                     '</div><div class="x-form-clear-left"></div>'
26539                 );
26540                 t.disableFormats = true;
26541                 t.compile();
26542                 Roo.form.Layout.prototype.fieldTpl = t;
26543             }
26544             for(var i = 0; i < slen; i++) {
26545                 if(stack[i].isFormField){
26546                     this.renderField(stack[i]);
26547                 }else{
26548                     this.renderComponent(stack[i]);
26549                 }
26550             }
26551         }
26552         if(this.clear){
26553             this.el.createChild({cls:'x-form-clear'});
26554         }
26555     },
26556
26557     // private
26558     renderField : function(f){
26559         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26560                f.id, //0
26561                f.fieldLabel, //1
26562                f.labelStyle||this.labelStyle||'', //2
26563                this.elementStyle||'', //3
26564                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26565                f.itemCls||this.itemCls||''  //5
26566        ], true).getPrevSibling());
26567     },
26568
26569     // private
26570     renderComponent : function(c){
26571         c.render(c.isLayout ? this.el : this.el.createChild());    
26572     },
26573     /**
26574      * Adds a object form elements (using the xtype property as the factory method.)
26575      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26576      * @param {Object} config 
26577      */
26578     addxtype : function(o)
26579     {
26580         // create the lement.
26581         o.form = this.form;
26582         var fe = Roo.factory(o, Roo.form);
26583         this.form.allItems.push(fe);
26584         this.stack.push(fe);
26585         
26586         if (fe.isFormField) {
26587             this.form.items.add(fe);
26588         }
26589          
26590         return fe;
26591     }
26592 });
26593
26594 /**
26595  * @class Roo.form.Column
26596  * @extends Roo.form.Layout
26597  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26598  * @constructor
26599  * @param {Object} config Configuration options
26600  */
26601 Roo.form.Column = function(config){
26602     Roo.form.Column.superclass.constructor.call(this, config);
26603 };
26604
26605 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26606     /**
26607      * @cfg {Number/String} width
26608      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26609      */
26610     /**
26611      * @cfg {String/Object} autoCreate
26612      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26613      */
26614
26615     // private
26616     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26617
26618     // private
26619     onRender : function(ct, position){
26620         Roo.form.Column.superclass.onRender.call(this, ct, position);
26621         if(this.width){
26622             this.el.setWidth(this.width);
26623         }
26624     }
26625 });
26626
26627
26628 /**
26629  * @class Roo.form.Row
26630  * @extends Roo.form.Layout
26631  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26632  * @constructor
26633  * @param {Object} config Configuration options
26634  */
26635
26636  
26637 Roo.form.Row = function(config){
26638     Roo.form.Row.superclass.constructor.call(this, config);
26639 };
26640  
26641 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26642       /**
26643      * @cfg {Number/String} width
26644      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26645      */
26646     /**
26647      * @cfg {Number/String} height
26648      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26649      */
26650     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26651     
26652     padWidth : 20,
26653     // private
26654     onRender : function(ct, position){
26655         //console.log('row render');
26656         if(!this.rowTpl){
26657             var t = new Roo.Template(
26658                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26659                     '<label for="{0}" style="{2}">{1}{4}</label>',
26660                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26661                     '</div>',
26662                 '</div>'
26663             );
26664             t.disableFormats = true;
26665             t.compile();
26666             Roo.form.Layout.prototype.rowTpl = t;
26667         }
26668         this.fieldTpl = this.rowTpl;
26669         
26670         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26671         var labelWidth = 100;
26672         
26673         if ((this.labelAlign != 'top')) {
26674             if (typeof this.labelWidth == 'number') {
26675                 labelWidth = this.labelWidth
26676             }
26677             this.padWidth =  20 + labelWidth;
26678             
26679         }
26680         
26681         Roo.form.Column.superclass.onRender.call(this, ct, position);
26682         if(this.width){
26683             this.el.setWidth(this.width);
26684         }
26685         if(this.height){
26686             this.el.setHeight(this.height);
26687         }
26688     },
26689     
26690     // private
26691     renderField : function(f){
26692         f.fieldEl = this.fieldTpl.append(this.el, [
26693                f.id, f.fieldLabel,
26694                f.labelStyle||this.labelStyle||'',
26695                this.elementStyle||'',
26696                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26697                f.itemCls||this.itemCls||'',
26698                f.width ? f.width + this.padWidth : 160 + this.padWidth
26699        ],true);
26700     }
26701 });
26702  
26703
26704 /**
26705  * @class Roo.form.FieldSet
26706  * @extends Roo.form.Layout
26707  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26708  * @constructor
26709  * @param {Object} config Configuration options
26710  */
26711 Roo.form.FieldSet = function(config){
26712     Roo.form.FieldSet.superclass.constructor.call(this, config);
26713 };
26714
26715 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26716     /**
26717      * @cfg {String} legend
26718      * The text to display as the legend for the FieldSet (defaults to '')
26719      */
26720     /**
26721      * @cfg {String/Object} autoCreate
26722      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26723      */
26724
26725     // private
26726     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26727
26728     // private
26729     onRender : function(ct, position){
26730         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26731         if(this.legend){
26732             this.setLegend(this.legend);
26733         }
26734     },
26735
26736     // private
26737     setLegend : function(text){
26738         if(this.rendered){
26739             this.el.child('legend').update(text);
26740         }
26741     }
26742 });/*
26743  * Based on:
26744  * Ext JS Library 1.1.1
26745  * Copyright(c) 2006-2007, Ext JS, LLC.
26746  *
26747  * Originally Released Under LGPL - original licence link has changed is not relivant.
26748  *
26749  * Fork - LGPL
26750  * <script type="text/javascript">
26751  */
26752 /**
26753  * @class Roo.form.VTypes
26754  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26755  * @singleton
26756  */
26757 Roo.form.VTypes = function(){
26758     // closure these in so they are only created once.
26759     var alpha = /^[a-zA-Z_]+$/;
26760     var alphanum = /^[a-zA-Z0-9_]+$/;
26761     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26762     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26763
26764     // All these messages and functions are configurable
26765     return {
26766         /**
26767          * The function used to validate email addresses
26768          * @param {String} value The email address
26769          */
26770         'email' : function(v){
26771             return email.test(v);
26772         },
26773         /**
26774          * The error text to display when the email validation function returns false
26775          * @type String
26776          */
26777         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26778         /**
26779          * The keystroke filter mask to be applied on email input
26780          * @type RegExp
26781          */
26782         'emailMask' : /[a-z0-9_\.\-@]/i,
26783
26784         /**
26785          * The function used to validate URLs
26786          * @param {String} value The URL
26787          */
26788         'url' : function(v){
26789             return url.test(v);
26790         },
26791         /**
26792          * The error text to display when the url validation function returns false
26793          * @type String
26794          */
26795         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26796         
26797         /**
26798          * The function used to validate alpha values
26799          * @param {String} value The value
26800          */
26801         'alpha' : function(v){
26802             return alpha.test(v);
26803         },
26804         /**
26805          * The error text to display when the alpha validation function returns false
26806          * @type String
26807          */
26808         'alphaText' : 'This field should only contain letters and _',
26809         /**
26810          * The keystroke filter mask to be applied on alpha input
26811          * @type RegExp
26812          */
26813         'alphaMask' : /[a-z_]/i,
26814
26815         /**
26816          * The function used to validate alphanumeric values
26817          * @param {String} value The value
26818          */
26819         'alphanum' : function(v){
26820             return alphanum.test(v);
26821         },
26822         /**
26823          * The error text to display when the alphanumeric validation function returns false
26824          * @type String
26825          */
26826         'alphanumText' : 'This field should only contain letters, numbers and _',
26827         /**
26828          * The keystroke filter mask to be applied on alphanumeric input
26829          * @type RegExp
26830          */
26831         'alphanumMask' : /[a-z0-9_]/i
26832     };
26833 }();//<script type="text/javascript">
26834
26835 /**
26836  * @class Roo.form.FCKeditor
26837  * @extends Roo.form.TextArea
26838  * Wrapper around the FCKEditor http://www.fckeditor.net
26839  * @constructor
26840  * Creates a new FCKeditor
26841  * @param {Object} config Configuration options
26842  */
26843 Roo.form.FCKeditor = function(config){
26844     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26845     this.addEvents({
26846          /**
26847          * @event editorinit
26848          * Fired when the editor is initialized - you can add extra handlers here..
26849          * @param {FCKeditor} this
26850          * @param {Object} the FCK object.
26851          */
26852         editorinit : true
26853     });
26854     
26855     
26856 };
26857 Roo.form.FCKeditor.editors = { };
26858 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26859 {
26860     //defaultAutoCreate : {
26861     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26862     //},
26863     // private
26864     /**
26865      * @cfg {Object} fck options - see fck manual for details.
26866      */
26867     fckconfig : false,
26868     
26869     /**
26870      * @cfg {Object} fck toolbar set (Basic or Default)
26871      */
26872     toolbarSet : 'Basic',
26873     /**
26874      * @cfg {Object} fck BasePath
26875      */ 
26876     basePath : '/fckeditor/',
26877     
26878     
26879     frame : false,
26880     
26881     value : '',
26882     
26883    
26884     onRender : function(ct, position)
26885     {
26886         if(!this.el){
26887             this.defaultAutoCreate = {
26888                 tag: "textarea",
26889                 style:"width:300px;height:60px;",
26890                 autocomplete: "new-password"
26891             };
26892         }
26893         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26894         /*
26895         if(this.grow){
26896             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26897             if(this.preventScrollbars){
26898                 this.el.setStyle("overflow", "hidden");
26899             }
26900             this.el.setHeight(this.growMin);
26901         }
26902         */
26903         //console.log('onrender' + this.getId() );
26904         Roo.form.FCKeditor.editors[this.getId()] = this;
26905          
26906
26907         this.replaceTextarea() ;
26908         
26909     },
26910     
26911     getEditor : function() {
26912         return this.fckEditor;
26913     },
26914     /**
26915      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26916      * @param {Mixed} value The value to set
26917      */
26918     
26919     
26920     setValue : function(value)
26921     {
26922         //console.log('setValue: ' + value);
26923         
26924         if(typeof(value) == 'undefined') { // not sure why this is happending...
26925             return;
26926         }
26927         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26928         
26929         //if(!this.el || !this.getEditor()) {
26930         //    this.value = value;
26931             //this.setValue.defer(100,this,[value]);    
26932         //    return;
26933         //} 
26934         
26935         if(!this.getEditor()) {
26936             return;
26937         }
26938         
26939         this.getEditor().SetData(value);
26940         
26941         //
26942
26943     },
26944
26945     /**
26946      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26947      * @return {Mixed} value The field value
26948      */
26949     getValue : function()
26950     {
26951         
26952         if (this.frame && this.frame.dom.style.display == 'none') {
26953             return Roo.form.FCKeditor.superclass.getValue.call(this);
26954         }
26955         
26956         if(!this.el || !this.getEditor()) {
26957            
26958            // this.getValue.defer(100,this); 
26959             return this.value;
26960         }
26961        
26962         
26963         var value=this.getEditor().GetData();
26964         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26965         return Roo.form.FCKeditor.superclass.getValue.call(this);
26966         
26967
26968     },
26969
26970     /**
26971      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26972      * @return {Mixed} value The field value
26973      */
26974     getRawValue : function()
26975     {
26976         if (this.frame && this.frame.dom.style.display == 'none') {
26977             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26978         }
26979         
26980         if(!this.el || !this.getEditor()) {
26981             //this.getRawValue.defer(100,this); 
26982             return this.value;
26983             return;
26984         }
26985         
26986         
26987         
26988         var value=this.getEditor().GetData();
26989         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26990         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26991          
26992     },
26993     
26994     setSize : function(w,h) {
26995         
26996         
26997         
26998         //if (this.frame && this.frame.dom.style.display == 'none') {
26999         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27000         //    return;
27001         //}
27002         //if(!this.el || !this.getEditor()) {
27003         //    this.setSize.defer(100,this, [w,h]); 
27004         //    return;
27005         //}
27006         
27007         
27008         
27009         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27010         
27011         this.frame.dom.setAttribute('width', w);
27012         this.frame.dom.setAttribute('height', h);
27013         this.frame.setSize(w,h);
27014         
27015     },
27016     
27017     toggleSourceEdit : function(value) {
27018         
27019       
27020          
27021         this.el.dom.style.display = value ? '' : 'none';
27022         this.frame.dom.style.display = value ?  'none' : '';
27023         
27024     },
27025     
27026     
27027     focus: function(tag)
27028     {
27029         if (this.frame.dom.style.display == 'none') {
27030             return Roo.form.FCKeditor.superclass.focus.call(this);
27031         }
27032         if(!this.el || !this.getEditor()) {
27033             this.focus.defer(100,this, [tag]); 
27034             return;
27035         }
27036         
27037         
27038         
27039         
27040         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27041         this.getEditor().Focus();
27042         if (tgs.length) {
27043             if (!this.getEditor().Selection.GetSelection()) {
27044                 this.focus.defer(100,this, [tag]); 
27045                 return;
27046             }
27047             
27048             
27049             var r = this.getEditor().EditorDocument.createRange();
27050             r.setStart(tgs[0],0);
27051             r.setEnd(tgs[0],0);
27052             this.getEditor().Selection.GetSelection().removeAllRanges();
27053             this.getEditor().Selection.GetSelection().addRange(r);
27054             this.getEditor().Focus();
27055         }
27056         
27057     },
27058     
27059     
27060     
27061     replaceTextarea : function()
27062     {
27063         if ( document.getElementById( this.getId() + '___Frame' ) ) {
27064             return ;
27065         }
27066         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27067         //{
27068             // We must check the elements firstly using the Id and then the name.
27069         var oTextarea = document.getElementById( this.getId() );
27070         
27071         var colElementsByName = document.getElementsByName( this.getId() ) ;
27072          
27073         oTextarea.style.display = 'none' ;
27074
27075         if ( oTextarea.tabIndex ) {            
27076             this.TabIndex = oTextarea.tabIndex ;
27077         }
27078         
27079         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27080         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27081         this.frame = Roo.get(this.getId() + '___Frame')
27082     },
27083     
27084     _getConfigHtml : function()
27085     {
27086         var sConfig = '' ;
27087
27088         for ( var o in this.fckconfig ) {
27089             sConfig += sConfig.length > 0  ? '&amp;' : '';
27090             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27091         }
27092
27093         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27094     },
27095     
27096     
27097     _getIFrameHtml : function()
27098     {
27099         var sFile = 'fckeditor.html' ;
27100         /* no idea what this is about..
27101         try
27102         {
27103             if ( (/fcksource=true/i).test( window.top.location.search ) )
27104                 sFile = 'fckeditor.original.html' ;
27105         }
27106         catch (e) { 
27107         */
27108
27109         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27110         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27111         
27112         
27113         var html = '<iframe id="' + this.getId() +
27114             '___Frame" src="' + sLink +
27115             '" width="' + this.width +
27116             '" height="' + this.height + '"' +
27117             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27118             ' frameborder="0" scrolling="no"></iframe>' ;
27119
27120         return html ;
27121     },
27122     
27123     _insertHtmlBefore : function( html, element )
27124     {
27125         if ( element.insertAdjacentHTML )       {
27126             // IE
27127             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27128         } else { // Gecko
27129             var oRange = document.createRange() ;
27130             oRange.setStartBefore( element ) ;
27131             var oFragment = oRange.createContextualFragment( html );
27132             element.parentNode.insertBefore( oFragment, element ) ;
27133         }
27134     }
27135     
27136     
27137   
27138     
27139     
27140     
27141     
27142
27143 });
27144
27145 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27146
27147 function FCKeditor_OnComplete(editorInstance){
27148     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27149     f.fckEditor = editorInstance;
27150     //console.log("loaded");
27151     f.fireEvent('editorinit', f, editorInstance);
27152
27153   
27154
27155  
27156
27157
27158
27159
27160
27161
27162
27163
27164
27165
27166
27167
27168
27169
27170
27171 //<script type="text/javascript">
27172 /**
27173  * @class Roo.form.GridField
27174  * @extends Roo.form.Field
27175  * Embed a grid (or editable grid into a form)
27176  * STATUS ALPHA
27177  * 
27178  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27179  * it needs 
27180  * xgrid.store = Roo.data.Store
27181  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27182  * xgrid.store.reader = Roo.data.JsonReader 
27183  * 
27184  * 
27185  * @constructor
27186  * Creates a new GridField
27187  * @param {Object} config Configuration options
27188  */
27189 Roo.form.GridField = function(config){
27190     Roo.form.GridField.superclass.constructor.call(this, config);
27191      
27192 };
27193
27194 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27195     /**
27196      * @cfg {Number} width  - used to restrict width of grid..
27197      */
27198     width : 100,
27199     /**
27200      * @cfg {Number} height - used to restrict height of grid..
27201      */
27202     height : 50,
27203      /**
27204      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27205          * 
27206          *}
27207      */
27208     xgrid : false, 
27209     /**
27210      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27211      * {tag: "input", type: "checkbox", autocomplete: "off"})
27212      */
27213    // defaultAutoCreate : { tag: 'div' },
27214     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27215     /**
27216      * @cfg {String} addTitle Text to include for adding a title.
27217      */
27218     addTitle : false,
27219     //
27220     onResize : function(){
27221         Roo.form.Field.superclass.onResize.apply(this, arguments);
27222     },
27223
27224     initEvents : function(){
27225         // Roo.form.Checkbox.superclass.initEvents.call(this);
27226         // has no events...
27227        
27228     },
27229
27230
27231     getResizeEl : function(){
27232         return this.wrap;
27233     },
27234
27235     getPositionEl : function(){
27236         return this.wrap;
27237     },
27238
27239     // private
27240     onRender : function(ct, position){
27241         
27242         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27243         var style = this.style;
27244         delete this.style;
27245         
27246         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27247         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27248         this.viewEl = this.wrap.createChild({ tag: 'div' });
27249         if (style) {
27250             this.viewEl.applyStyles(style);
27251         }
27252         if (this.width) {
27253             this.viewEl.setWidth(this.width);
27254         }
27255         if (this.height) {
27256             this.viewEl.setHeight(this.height);
27257         }
27258         //if(this.inputValue !== undefined){
27259         //this.setValue(this.value);
27260         
27261         
27262         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27263         
27264         
27265         this.grid.render();
27266         this.grid.getDataSource().on('remove', this.refreshValue, this);
27267         this.grid.getDataSource().on('update', this.refreshValue, this);
27268         this.grid.on('afteredit', this.refreshValue, this);
27269  
27270     },
27271      
27272     
27273     /**
27274      * Sets the value of the item. 
27275      * @param {String} either an object  or a string..
27276      */
27277     setValue : function(v){
27278         //this.value = v;
27279         v = v || []; // empty set..
27280         // this does not seem smart - it really only affects memoryproxy grids..
27281         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27282             var ds = this.grid.getDataSource();
27283             // assumes a json reader..
27284             var data = {}
27285             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27286             ds.loadData( data);
27287         }
27288         // clear selection so it does not get stale.
27289         if (this.grid.sm) { 
27290             this.grid.sm.clearSelections();
27291         }
27292         
27293         Roo.form.GridField.superclass.setValue.call(this, v);
27294         this.refreshValue();
27295         // should load data in the grid really....
27296     },
27297     
27298     // private
27299     refreshValue: function() {
27300          var val = [];
27301         this.grid.getDataSource().each(function(r) {
27302             val.push(r.data);
27303         });
27304         this.el.dom.value = Roo.encode(val);
27305     }
27306     
27307      
27308     
27309     
27310 });/*
27311  * Based on:
27312  * Ext JS Library 1.1.1
27313  * Copyright(c) 2006-2007, Ext JS, LLC.
27314  *
27315  * Originally Released Under LGPL - original licence link has changed is not relivant.
27316  *
27317  * Fork - LGPL
27318  * <script type="text/javascript">
27319  */
27320 /**
27321  * @class Roo.form.DisplayField
27322  * @extends Roo.form.Field
27323  * A generic Field to display non-editable data.
27324  * @cfg {Boolean} closable (true|false) default false
27325  * @constructor
27326  * Creates a new Display Field item.
27327  * @param {Object} config Configuration options
27328  */
27329 Roo.form.DisplayField = function(config){
27330     Roo.form.DisplayField.superclass.constructor.call(this, config);
27331     
27332     this.addEvents({
27333         /**
27334          * @event close
27335          * Fires after the click the close btn
27336              * @param {Roo.form.DisplayField} this
27337              */
27338         close : true
27339     });
27340 };
27341
27342 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27343     inputType:      'hidden',
27344     allowBlank:     true,
27345     readOnly:         true,
27346     
27347  
27348     /**
27349      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27350      */
27351     focusClass : undefined,
27352     /**
27353      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27354      */
27355     fieldClass: 'x-form-field',
27356     
27357      /**
27358      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27359      */
27360     valueRenderer: undefined,
27361     
27362     width: 100,
27363     /**
27364      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27365      * {tag: "input", type: "checkbox", autocomplete: "off"})
27366      */
27367      
27368  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27369  
27370     closable : false,
27371     
27372     onResize : function(){
27373         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27374         
27375     },
27376
27377     initEvents : function(){
27378         // Roo.form.Checkbox.superclass.initEvents.call(this);
27379         // has no events...
27380         
27381         if(this.closable){
27382             this.closeEl.on('click', this.onClose, this);
27383         }
27384        
27385     },
27386
27387
27388     getResizeEl : function(){
27389         return this.wrap;
27390     },
27391
27392     getPositionEl : function(){
27393         return this.wrap;
27394     },
27395
27396     // private
27397     onRender : function(ct, position){
27398         
27399         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27400         //if(this.inputValue !== undefined){
27401         this.wrap = this.el.wrap();
27402         
27403         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27404         
27405         if(this.closable){
27406             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27407         }
27408         
27409         if (this.bodyStyle) {
27410             this.viewEl.applyStyles(this.bodyStyle);
27411         }
27412         //this.viewEl.setStyle('padding', '2px');
27413         
27414         this.setValue(this.value);
27415         
27416     },
27417 /*
27418     // private
27419     initValue : Roo.emptyFn,
27420
27421   */
27422
27423         // private
27424     onClick : function(){
27425         
27426     },
27427
27428     /**
27429      * Sets the checked state of the checkbox.
27430      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27431      */
27432     setValue : function(v){
27433         this.value = v;
27434         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27435         // this might be called before we have a dom element..
27436         if (!this.viewEl) {
27437             return;
27438         }
27439         this.viewEl.dom.innerHTML = html;
27440         Roo.form.DisplayField.superclass.setValue.call(this, v);
27441
27442     },
27443     
27444     onClose : function(e)
27445     {
27446         e.preventDefault();
27447         
27448         this.fireEvent('close', this);
27449     }
27450 });/*
27451  * 
27452  * Licence- LGPL
27453  * 
27454  */
27455
27456 /**
27457  * @class Roo.form.DayPicker
27458  * @extends Roo.form.Field
27459  * A Day picker show [M] [T] [W] ....
27460  * @constructor
27461  * Creates a new Day Picker
27462  * @param {Object} config Configuration options
27463  */
27464 Roo.form.DayPicker= function(config){
27465     Roo.form.DayPicker.superclass.constructor.call(this, config);
27466      
27467 };
27468
27469 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27470     /**
27471      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27472      */
27473     focusClass : undefined,
27474     /**
27475      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27476      */
27477     fieldClass: "x-form-field",
27478    
27479     /**
27480      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27481      * {tag: "input", type: "checkbox", autocomplete: "off"})
27482      */
27483     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27484     
27485    
27486     actionMode : 'viewEl', 
27487     //
27488     // private
27489  
27490     inputType : 'hidden',
27491     
27492      
27493     inputElement: false, // real input element?
27494     basedOn: false, // ????
27495     
27496     isFormField: true, // not sure where this is needed!!!!
27497
27498     onResize : function(){
27499         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27500         if(!this.boxLabel){
27501             this.el.alignTo(this.wrap, 'c-c');
27502         }
27503     },
27504
27505     initEvents : function(){
27506         Roo.form.Checkbox.superclass.initEvents.call(this);
27507         this.el.on("click", this.onClick,  this);
27508         this.el.on("change", this.onClick,  this);
27509     },
27510
27511
27512     getResizeEl : function(){
27513         return this.wrap;
27514     },
27515
27516     getPositionEl : function(){
27517         return this.wrap;
27518     },
27519
27520     
27521     // private
27522     onRender : function(ct, position){
27523         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27524        
27525         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27526         
27527         var r1 = '<table><tr>';
27528         var r2 = '<tr class="x-form-daypick-icons">';
27529         for (var i=0; i < 7; i++) {
27530             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27531             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27532         }
27533         
27534         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27535         viewEl.select('img').on('click', this.onClick, this);
27536         this.viewEl = viewEl;   
27537         
27538         
27539         // this will not work on Chrome!!!
27540         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27541         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27542         
27543         
27544           
27545
27546     },
27547
27548     // private
27549     initValue : Roo.emptyFn,
27550
27551     /**
27552      * Returns the checked state of the checkbox.
27553      * @return {Boolean} True if checked, else false
27554      */
27555     getValue : function(){
27556         return this.el.dom.value;
27557         
27558     },
27559
27560         // private
27561     onClick : function(e){ 
27562         //this.setChecked(!this.checked);
27563         Roo.get(e.target).toggleClass('x-menu-item-checked');
27564         this.refreshValue();
27565         //if(this.el.dom.checked != this.checked){
27566         //    this.setValue(this.el.dom.checked);
27567        // }
27568     },
27569     
27570     // private
27571     refreshValue : function()
27572     {
27573         var val = '';
27574         this.viewEl.select('img',true).each(function(e,i,n)  {
27575             val += e.is(".x-menu-item-checked") ? String(n) : '';
27576         });
27577         this.setValue(val, true);
27578     },
27579
27580     /**
27581      * Sets the checked state of the checkbox.
27582      * On is always based on a string comparison between inputValue and the param.
27583      * @param {Boolean/String} value - the value to set 
27584      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27585      */
27586     setValue : function(v,suppressEvent){
27587         if (!this.el.dom) {
27588             return;
27589         }
27590         var old = this.el.dom.value ;
27591         this.el.dom.value = v;
27592         if (suppressEvent) {
27593             return ;
27594         }
27595          
27596         // update display..
27597         this.viewEl.select('img',true).each(function(e,i,n)  {
27598             
27599             var on = e.is(".x-menu-item-checked");
27600             var newv = v.indexOf(String(n)) > -1;
27601             if (on != newv) {
27602                 e.toggleClass('x-menu-item-checked');
27603             }
27604             
27605         });
27606         
27607         
27608         this.fireEvent('change', this, v, old);
27609         
27610         
27611     },
27612    
27613     // handle setting of hidden value by some other method!!?!?
27614     setFromHidden: function()
27615     {
27616         if(!this.el){
27617             return;
27618         }
27619         //console.log("SET FROM HIDDEN");
27620         //alert('setFrom hidden');
27621         this.setValue(this.el.dom.value);
27622     },
27623     
27624     onDestroy : function()
27625     {
27626         if(this.viewEl){
27627             Roo.get(this.viewEl).remove();
27628         }
27629          
27630         Roo.form.DayPicker.superclass.onDestroy.call(this);
27631     }
27632
27633 });/*
27634  * RooJS Library 1.1.1
27635  * Copyright(c) 2008-2011  Alan Knowles
27636  *
27637  * License - LGPL
27638  */
27639  
27640
27641 /**
27642  * @class Roo.form.ComboCheck
27643  * @extends Roo.form.ComboBox
27644  * A combobox for multiple select items.
27645  *
27646  * FIXME - could do with a reset button..
27647  * 
27648  * @constructor
27649  * Create a new ComboCheck
27650  * @param {Object} config Configuration options
27651  */
27652 Roo.form.ComboCheck = function(config){
27653     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27654     // should verify some data...
27655     // like
27656     // hiddenName = required..
27657     // displayField = required
27658     // valudField == required
27659     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27660     var _t = this;
27661     Roo.each(req, function(e) {
27662         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27663             throw "Roo.form.ComboCheck : missing value for: " + e;
27664         }
27665     });
27666     
27667     
27668 };
27669
27670 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27671      
27672      
27673     editable : false,
27674      
27675     selectedClass: 'x-menu-item-checked', 
27676     
27677     // private
27678     onRender : function(ct, position){
27679         var _t = this;
27680         
27681         
27682         
27683         if(!this.tpl){
27684             var cls = 'x-combo-list';
27685
27686             
27687             this.tpl =  new Roo.Template({
27688                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27689                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27690                    '<span>{' + this.displayField + '}</span>' +
27691                     '</div>' 
27692                 
27693             });
27694         }
27695  
27696         
27697         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27698         this.view.singleSelect = false;
27699         this.view.multiSelect = true;
27700         this.view.toggleSelect = true;
27701         this.pageTb.add(new Roo.Toolbar.Fill(), {
27702             
27703             text: 'Done',
27704             handler: function()
27705             {
27706                 _t.collapse();
27707             }
27708         });
27709     },
27710     
27711     onViewOver : function(e, t){
27712         // do nothing...
27713         return;
27714         
27715     },
27716     
27717     onViewClick : function(doFocus,index){
27718         return;
27719         
27720     },
27721     select: function () {
27722         //Roo.log("SELECT CALLED");
27723     },
27724      
27725     selectByValue : function(xv, scrollIntoView){
27726         var ar = this.getValueArray();
27727         var sels = [];
27728         
27729         Roo.each(ar, function(v) {
27730             if(v === undefined || v === null){
27731                 return;
27732             }
27733             var r = this.findRecord(this.valueField, v);
27734             if(r){
27735                 sels.push(this.store.indexOf(r))
27736                 
27737             }
27738         },this);
27739         this.view.select(sels);
27740         return false;
27741     },
27742     
27743     
27744     
27745     onSelect : function(record, index){
27746        // Roo.log("onselect Called");
27747        // this is only called by the clear button now..
27748         this.view.clearSelections();
27749         this.setValue('[]');
27750         if (this.value != this.valueBefore) {
27751             this.fireEvent('change', this, this.value, this.valueBefore);
27752             this.valueBefore = this.value;
27753         }
27754     },
27755     getValueArray : function()
27756     {
27757         var ar = [] ;
27758         
27759         try {
27760             //Roo.log(this.value);
27761             if (typeof(this.value) == 'undefined') {
27762                 return [];
27763             }
27764             var ar = Roo.decode(this.value);
27765             return  ar instanceof Array ? ar : []; //?? valid?
27766             
27767         } catch(e) {
27768             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27769             return [];
27770         }
27771          
27772     },
27773     expand : function ()
27774     {
27775         
27776         Roo.form.ComboCheck.superclass.expand.call(this);
27777         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27778         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27779         
27780
27781     },
27782     
27783     collapse : function(){
27784         Roo.form.ComboCheck.superclass.collapse.call(this);
27785         var sl = this.view.getSelectedIndexes();
27786         var st = this.store;
27787         var nv = [];
27788         var tv = [];
27789         var r;
27790         Roo.each(sl, function(i) {
27791             r = st.getAt(i);
27792             nv.push(r.get(this.valueField));
27793         },this);
27794         this.setValue(Roo.encode(nv));
27795         if (this.value != this.valueBefore) {
27796
27797             this.fireEvent('change', this, this.value, this.valueBefore);
27798             this.valueBefore = this.value;
27799         }
27800         
27801     },
27802     
27803     setValue : function(v){
27804         // Roo.log(v);
27805         this.value = v;
27806         
27807         var vals = this.getValueArray();
27808         var tv = [];
27809         Roo.each(vals, function(k) {
27810             var r = this.findRecord(this.valueField, k);
27811             if(r){
27812                 tv.push(r.data[this.displayField]);
27813             }else if(this.valueNotFoundText !== undefined){
27814                 tv.push( this.valueNotFoundText );
27815             }
27816         },this);
27817        // Roo.log(tv);
27818         
27819         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27820         this.hiddenField.value = v;
27821         this.value = v;
27822     }
27823     
27824 });/*
27825  * Based on:
27826  * Ext JS Library 1.1.1
27827  * Copyright(c) 2006-2007, Ext JS, LLC.
27828  *
27829  * Originally Released Under LGPL - original licence link has changed is not relivant.
27830  *
27831  * Fork - LGPL
27832  * <script type="text/javascript">
27833  */
27834  
27835 /**
27836  * @class Roo.form.Signature
27837  * @extends Roo.form.Field
27838  * Signature field.  
27839  * @constructor
27840  * 
27841  * @param {Object} config Configuration options
27842  */
27843
27844 Roo.form.Signature = function(config){
27845     Roo.form.Signature.superclass.constructor.call(this, config);
27846     
27847     this.addEvents({// not in used??
27848          /**
27849          * @event confirm
27850          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27851              * @param {Roo.form.Signature} combo This combo box
27852              */
27853         'confirm' : true,
27854         /**
27855          * @event reset
27856          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27857              * @param {Roo.form.ComboBox} combo This combo box
27858              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27859              */
27860         'reset' : true
27861     });
27862 };
27863
27864 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27865     /**
27866      * @cfg {Object} labels Label to use when rendering a form.
27867      * defaults to 
27868      * labels : { 
27869      *      clear : "Clear",
27870      *      confirm : "Confirm"
27871      *  }
27872      */
27873     labels : { 
27874         clear : "Clear",
27875         confirm : "Confirm"
27876     },
27877     /**
27878      * @cfg {Number} width The signature panel width (defaults to 300)
27879      */
27880     width: 300,
27881     /**
27882      * @cfg {Number} height The signature panel height (defaults to 100)
27883      */
27884     height : 100,
27885     /**
27886      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27887      */
27888     allowBlank : false,
27889     
27890     //private
27891     // {Object} signPanel The signature SVG panel element (defaults to {})
27892     signPanel : {},
27893     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27894     isMouseDown : false,
27895     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27896     isConfirmed : false,
27897     // {String} signatureTmp SVG mapping string (defaults to empty string)
27898     signatureTmp : '',
27899     
27900     
27901     defaultAutoCreate : { // modified by initCompnoent..
27902         tag: "input",
27903         type:"hidden"
27904     },
27905
27906     // private
27907     onRender : function(ct, position){
27908         
27909         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27910         
27911         this.wrap = this.el.wrap({
27912             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27913         });
27914         
27915         this.createToolbar(this);
27916         this.signPanel = this.wrap.createChild({
27917                 tag: 'div',
27918                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27919             }, this.el
27920         );
27921             
27922         this.svgID = Roo.id();
27923         this.svgEl = this.signPanel.createChild({
27924               xmlns : 'http://www.w3.org/2000/svg',
27925               tag : 'svg',
27926               id : this.svgID + "-svg",
27927               width: this.width,
27928               height: this.height,
27929               viewBox: '0 0 '+this.width+' '+this.height,
27930               cn : [
27931                 {
27932                     tag: "rect",
27933                     id: this.svgID + "-svg-r",
27934                     width: this.width,
27935                     height: this.height,
27936                     fill: "#ffa"
27937                 },
27938                 {
27939                     tag: "line",
27940                     id: this.svgID + "-svg-l",
27941                     x1: "0", // start
27942                     y1: (this.height*0.8), // start set the line in 80% of height
27943                     x2: this.width, // end
27944                     y2: (this.height*0.8), // end set the line in 80% of height
27945                     'stroke': "#666",
27946                     'stroke-width': "1",
27947                     'stroke-dasharray': "3",
27948                     'shape-rendering': "crispEdges",
27949                     'pointer-events': "none"
27950                 },
27951                 {
27952                     tag: "path",
27953                     id: this.svgID + "-svg-p",
27954                     'stroke': "navy",
27955                     'stroke-width': "3",
27956                     'fill': "none",
27957                     'pointer-events': 'none'
27958                 }
27959               ]
27960         });
27961         this.createSVG();
27962         this.svgBox = this.svgEl.dom.getScreenCTM();
27963     },
27964     createSVG : function(){ 
27965         var svg = this.signPanel;
27966         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27967         var t = this;
27968
27969         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27970         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27971         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27972         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27973         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27974         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27975         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27976         
27977     },
27978     isTouchEvent : function(e){
27979         return e.type.match(/^touch/);
27980     },
27981     getCoords : function (e) {
27982         var pt    = this.svgEl.dom.createSVGPoint();
27983         pt.x = e.clientX; 
27984         pt.y = e.clientY;
27985         if (this.isTouchEvent(e)) {
27986             pt.x =  e.targetTouches[0].clientX;
27987             pt.y = e.targetTouches[0].clientY;
27988         }
27989         var a = this.svgEl.dom.getScreenCTM();
27990         var b = a.inverse();
27991         var mx = pt.matrixTransform(b);
27992         return mx.x + ',' + mx.y;
27993     },
27994     //mouse event headler 
27995     down : function (e) {
27996         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27997         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27998         
27999         this.isMouseDown = true;
28000         
28001         e.preventDefault();
28002     },
28003     move : function (e) {
28004         if (this.isMouseDown) {
28005             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
28006             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
28007         }
28008         
28009         e.preventDefault();
28010     },
28011     up : function (e) {
28012         this.isMouseDown = false;
28013         var sp = this.signatureTmp.split(' ');
28014         
28015         if(sp.length > 1){
28016             if(!sp[sp.length-2].match(/^L/)){
28017                 sp.pop();
28018                 sp.pop();
28019                 sp.push("");
28020                 this.signatureTmp = sp.join(" ");
28021             }
28022         }
28023         if(this.getValue() != this.signatureTmp){
28024             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28025             this.isConfirmed = false;
28026         }
28027         e.preventDefault();
28028     },
28029     
28030     /**
28031      * Protected method that will not generally be called directly. It
28032      * is called when the editor creates its toolbar. Override this method if you need to
28033      * add custom toolbar buttons.
28034      * @param {HtmlEditor} editor
28035      */
28036     createToolbar : function(editor){
28037          function btn(id, toggle, handler){
28038             var xid = fid + '-'+ id ;
28039             return {
28040                 id : xid,
28041                 cmd : id,
28042                 cls : 'x-btn-icon x-edit-'+id,
28043                 enableToggle:toggle !== false,
28044                 scope: editor, // was editor...
28045                 handler:handler||editor.relayBtnCmd,
28046                 clickEvent:'mousedown',
28047                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28048                 tabIndex:-1
28049             };
28050         }
28051         
28052         
28053         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28054         this.tb = tb;
28055         this.tb.add(
28056            {
28057                 cls : ' x-signature-btn x-signature-'+id,
28058                 scope: editor, // was editor...
28059                 handler: this.reset,
28060                 clickEvent:'mousedown',
28061                 text: this.labels.clear
28062             },
28063             {
28064                  xtype : 'Fill',
28065                  xns: Roo.Toolbar
28066             }, 
28067             {
28068                 cls : '  x-signature-btn x-signature-'+id,
28069                 scope: editor, // was editor...
28070                 handler: this.confirmHandler,
28071                 clickEvent:'mousedown',
28072                 text: this.labels.confirm
28073             }
28074         );
28075     
28076     },
28077     //public
28078     /**
28079      * when user is clicked confirm then show this image.....
28080      * 
28081      * @return {String} Image Data URI
28082      */
28083     getImageDataURI : function(){
28084         var svg = this.svgEl.dom.parentNode.innerHTML;
28085         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28086         return src; 
28087     },
28088     /**
28089      * 
28090      * @return {Boolean} this.isConfirmed
28091      */
28092     getConfirmed : function(){
28093         return this.isConfirmed;
28094     },
28095     /**
28096      * 
28097      * @return {Number} this.width
28098      */
28099     getWidth : function(){
28100         return this.width;
28101     },
28102     /**
28103      * 
28104      * @return {Number} this.height
28105      */
28106     getHeight : function(){
28107         return this.height;
28108     },
28109     // private
28110     getSignature : function(){
28111         return this.signatureTmp;
28112     },
28113     // private
28114     reset : function(){
28115         this.signatureTmp = '';
28116         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28117         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28118         this.isConfirmed = false;
28119         Roo.form.Signature.superclass.reset.call(this);
28120     },
28121     setSignature : function(s){
28122         this.signatureTmp = s;
28123         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28124         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28125         this.setValue(s);
28126         this.isConfirmed = false;
28127         Roo.form.Signature.superclass.reset.call(this);
28128     }, 
28129     test : function(){
28130 //        Roo.log(this.signPanel.dom.contentWindow.up())
28131     },
28132     //private
28133     setConfirmed : function(){
28134         
28135         
28136         
28137 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28138     },
28139     // private
28140     confirmHandler : function(){
28141         if(!this.getSignature()){
28142             return;
28143         }
28144         
28145         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28146         this.setValue(this.getSignature());
28147         this.isConfirmed = true;
28148         
28149         this.fireEvent('confirm', this);
28150     },
28151     // private
28152     // Subclasses should provide the validation implementation by overriding this
28153     validateValue : function(value){
28154         if(this.allowBlank){
28155             return true;
28156         }
28157         
28158         if(this.isConfirmed){
28159             return true;
28160         }
28161         return false;
28162     }
28163 });/*
28164  * Based on:
28165  * Ext JS Library 1.1.1
28166  * Copyright(c) 2006-2007, Ext JS, LLC.
28167  *
28168  * Originally Released Under LGPL - original licence link has changed is not relivant.
28169  *
28170  * Fork - LGPL
28171  * <script type="text/javascript">
28172  */
28173  
28174
28175 /**
28176  * @class Roo.form.ComboBox
28177  * @extends Roo.form.TriggerField
28178  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28179  * @constructor
28180  * Create a new ComboBox.
28181  * @param {Object} config Configuration options
28182  */
28183 Roo.form.Select = function(config){
28184     Roo.form.Select.superclass.constructor.call(this, config);
28185      
28186 };
28187
28188 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28189     /**
28190      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28191      */
28192     /**
28193      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28194      * rendering into an Roo.Editor, defaults to false)
28195      */
28196     /**
28197      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28198      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28199      */
28200     /**
28201      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28202      */
28203     /**
28204      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28205      * the dropdown list (defaults to undefined, with no header element)
28206      */
28207
28208      /**
28209      * @cfg {String/Roo.Template} tpl The template to use to render the output
28210      */
28211      
28212     // private
28213     defaultAutoCreate : {tag: "select"  },
28214     /**
28215      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28216      */
28217     listWidth: undefined,
28218     /**
28219      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28220      * mode = 'remote' or 'text' if mode = 'local')
28221      */
28222     displayField: undefined,
28223     /**
28224      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28225      * mode = 'remote' or 'value' if mode = 'local'). 
28226      * Note: use of a valueField requires the user make a selection
28227      * in order for a value to be mapped.
28228      */
28229     valueField: undefined,
28230     
28231     
28232     /**
28233      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28234      * field's data value (defaults to the underlying DOM element's name)
28235      */
28236     hiddenName: undefined,
28237     /**
28238      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28239      */
28240     listClass: '',
28241     /**
28242      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28243      */
28244     selectedClass: 'x-combo-selected',
28245     /**
28246      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
28247      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28248      * which displays a downward arrow icon).
28249      */
28250     triggerClass : 'x-form-arrow-trigger',
28251     /**
28252      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28253      */
28254     shadow:'sides',
28255     /**
28256      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28257      * anchor positions (defaults to 'tl-bl')
28258      */
28259     listAlign: 'tl-bl?',
28260     /**
28261      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28262      */
28263     maxHeight: 300,
28264     /**
28265      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
28266      * query specified by the allQuery config option (defaults to 'query')
28267      */
28268     triggerAction: 'query',
28269     /**
28270      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28271      * (defaults to 4, does not apply if editable = false)
28272      */
28273     minChars : 4,
28274     /**
28275      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28276      * delay (typeAheadDelay) if it matches a known value (defaults to false)
28277      */
28278     typeAhead: false,
28279     /**
28280      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28281      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28282      */
28283     queryDelay: 500,
28284     /**
28285      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28286      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28287      */
28288     pageSize: 0,
28289     /**
28290      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28291      * when editable = true (defaults to false)
28292      */
28293     selectOnFocus:false,
28294     /**
28295      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28296      */
28297     queryParam: 'query',
28298     /**
28299      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28300      * when mode = 'remote' (defaults to 'Loading...')
28301      */
28302     loadingText: 'Loading...',
28303     /**
28304      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28305      */
28306     resizable: false,
28307     /**
28308      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28309      */
28310     handleHeight : 8,
28311     /**
28312      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28313      * traditional select (defaults to true)
28314      */
28315     editable: true,
28316     /**
28317      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28318      */
28319     allQuery: '',
28320     /**
28321      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28322      */
28323     mode: 'remote',
28324     /**
28325      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28326      * listWidth has a higher value)
28327      */
28328     minListWidth : 70,
28329     /**
28330      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28331      * allow the user to set arbitrary text into the field (defaults to false)
28332      */
28333     forceSelection:false,
28334     /**
28335      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28336      * if typeAhead = true (defaults to 250)
28337      */
28338     typeAheadDelay : 250,
28339     /**
28340      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28341      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28342      */
28343     valueNotFoundText : undefined,
28344     
28345     /**
28346      * @cfg {String} defaultValue The value displayed after loading the store.
28347      */
28348     defaultValue: '',
28349     
28350     /**
28351      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28352      */
28353     blockFocus : false,
28354     
28355     /**
28356      * @cfg {Boolean} disableClear Disable showing of clear button.
28357      */
28358     disableClear : false,
28359     /**
28360      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28361      */
28362     alwaysQuery : false,
28363     
28364     //private
28365     addicon : false,
28366     editicon: false,
28367     
28368     // element that contains real text value.. (when hidden is used..)
28369      
28370     // private
28371     onRender : function(ct, position){
28372         Roo.form.Field.prototype.onRender.call(this, ct, position);
28373         
28374         if(this.store){
28375             this.store.on('beforeload', this.onBeforeLoad, this);
28376             this.store.on('load', this.onLoad, this);
28377             this.store.on('loadexception', this.onLoadException, this);
28378             this.store.load({});
28379         }
28380         
28381         
28382         
28383     },
28384
28385     // private
28386     initEvents : function(){
28387         //Roo.form.ComboBox.superclass.initEvents.call(this);
28388  
28389     },
28390
28391     onDestroy : function(){
28392        
28393         if(this.store){
28394             this.store.un('beforeload', this.onBeforeLoad, this);
28395             this.store.un('load', this.onLoad, this);
28396             this.store.un('loadexception', this.onLoadException, this);
28397         }
28398         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28399     },
28400
28401     // private
28402     fireKey : function(e){
28403         if(e.isNavKeyPress() && !this.list.isVisible()){
28404             this.fireEvent("specialkey", this, e);
28405         }
28406     },
28407
28408     // private
28409     onResize: function(w, h){
28410         
28411         return; 
28412     
28413         
28414     },
28415
28416     /**
28417      * Allow or prevent the user from directly editing the field text.  If false is passed,
28418      * the user will only be able to select from the items defined in the dropdown list.  This method
28419      * is the runtime equivalent of setting the 'editable' config option at config time.
28420      * @param {Boolean} value True to allow the user to directly edit the field text
28421      */
28422     setEditable : function(value){
28423          
28424     },
28425
28426     // private
28427     onBeforeLoad : function(){
28428         
28429         Roo.log("Select before load");
28430         return;
28431     
28432         this.innerList.update(this.loadingText ?
28433                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28434         //this.restrictHeight();
28435         this.selectedIndex = -1;
28436     },
28437
28438     // private
28439     onLoad : function(){
28440
28441     
28442         var dom = this.el.dom;
28443         dom.innerHTML = '';
28444          var od = dom.ownerDocument;
28445          
28446         if (this.emptyText) {
28447             var op = od.createElement('option');
28448             op.setAttribute('value', '');
28449             op.innerHTML = String.format('{0}', this.emptyText);
28450             dom.appendChild(op);
28451         }
28452         if(this.store.getCount() > 0){
28453            
28454             var vf = this.valueField;
28455             var df = this.displayField;
28456             this.store.data.each(function(r) {
28457                 // which colmsn to use... testing - cdoe / title..
28458                 var op = od.createElement('option');
28459                 op.setAttribute('value', r.data[vf]);
28460                 op.innerHTML = String.format('{0}', r.data[df]);
28461                 dom.appendChild(op);
28462             });
28463             if (typeof(this.defaultValue != 'undefined')) {
28464                 this.setValue(this.defaultValue);
28465             }
28466             
28467              
28468         }else{
28469             //this.onEmptyResults();
28470         }
28471         //this.el.focus();
28472     },
28473     // private
28474     onLoadException : function()
28475     {
28476         dom.innerHTML = '';
28477             
28478         Roo.log("Select on load exception");
28479         return;
28480     
28481         this.collapse();
28482         Roo.log(this.store.reader.jsonData);
28483         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28484             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28485         }
28486         
28487         
28488     },
28489     // private
28490     onTypeAhead : function(){
28491          
28492     },
28493
28494     // private
28495     onSelect : function(record, index){
28496         Roo.log('on select?');
28497         return;
28498         if(this.fireEvent('beforeselect', this, record, index) !== false){
28499             this.setFromData(index > -1 ? record.data : false);
28500             this.collapse();
28501             this.fireEvent('select', this, record, index);
28502         }
28503     },
28504
28505     /**
28506      * Returns the currently selected field value or empty string if no value is set.
28507      * @return {String} value The selected value
28508      */
28509     getValue : function(){
28510         var dom = this.el.dom;
28511         this.value = dom.options[dom.selectedIndex].value;
28512         return this.value;
28513         
28514     },
28515
28516     /**
28517      * Clears any text/value currently set in the field
28518      */
28519     clearValue : function(){
28520         this.value = '';
28521         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28522         
28523     },
28524
28525     /**
28526      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28527      * will be displayed in the field.  If the value does not match the data value of an existing item,
28528      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28529      * Otherwise the field will be blank (although the value will still be set).
28530      * @param {String} value The value to match
28531      */
28532     setValue : function(v){
28533         var d = this.el.dom;
28534         for (var i =0; i < d.options.length;i++) {
28535             if (v == d.options[i].value) {
28536                 d.selectedIndex = i;
28537                 this.value = v;
28538                 return;
28539             }
28540         }
28541         this.clearValue();
28542     },
28543     /**
28544      * @property {Object} the last set data for the element
28545      */
28546     
28547     lastData : false,
28548     /**
28549      * Sets the value of the field based on a object which is related to the record format for the store.
28550      * @param {Object} value the value to set as. or false on reset?
28551      */
28552     setFromData : function(o){
28553         Roo.log('setfrom data?');
28554          
28555         
28556         
28557     },
28558     // private
28559     reset : function(){
28560         this.clearValue();
28561     },
28562     // private
28563     findRecord : function(prop, value){
28564         
28565         return false;
28566     
28567         var record;
28568         if(this.store.getCount() > 0){
28569             this.store.each(function(r){
28570                 if(r.data[prop] == value){
28571                     record = r;
28572                     return false;
28573                 }
28574                 return true;
28575             });
28576         }
28577         return record;
28578     },
28579     
28580     getName: function()
28581     {
28582         // returns hidden if it's set..
28583         if (!this.rendered) {return ''};
28584         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28585         
28586     },
28587      
28588
28589     
28590
28591     // private
28592     onEmptyResults : function(){
28593         Roo.log('empty results');
28594         //this.collapse();
28595     },
28596
28597     /**
28598      * Returns true if the dropdown list is expanded, else false.
28599      */
28600     isExpanded : function(){
28601         return false;
28602     },
28603
28604     /**
28605      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28606      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28607      * @param {String} value The data value of the item to select
28608      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28609      * selected item if it is not currently in view (defaults to true)
28610      * @return {Boolean} True if the value matched an item in the list, else false
28611      */
28612     selectByValue : function(v, scrollIntoView){
28613         Roo.log('select By Value');
28614         return false;
28615     
28616         if(v !== undefined && v !== null){
28617             var r = this.findRecord(this.valueField || this.displayField, v);
28618             if(r){
28619                 this.select(this.store.indexOf(r), scrollIntoView);
28620                 return true;
28621             }
28622         }
28623         return false;
28624     },
28625
28626     /**
28627      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28628      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28629      * @param {Number} index The zero-based index of the list item to select
28630      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28631      * selected item if it is not currently in view (defaults to true)
28632      */
28633     select : function(index, scrollIntoView){
28634         Roo.log('select ');
28635         return  ;
28636         
28637         this.selectedIndex = index;
28638         this.view.select(index);
28639         if(scrollIntoView !== false){
28640             var el = this.view.getNode(index);
28641             if(el){
28642                 this.innerList.scrollChildIntoView(el, false);
28643             }
28644         }
28645     },
28646
28647       
28648
28649     // private
28650     validateBlur : function(){
28651         
28652         return;
28653         
28654     },
28655
28656     // private
28657     initQuery : function(){
28658         this.doQuery(this.getRawValue());
28659     },
28660
28661     // private
28662     doForce : function(){
28663         if(this.el.dom.value.length > 0){
28664             this.el.dom.value =
28665                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28666              
28667         }
28668     },
28669
28670     /**
28671      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28672      * query allowing the query action to be canceled if needed.
28673      * @param {String} query The SQL query to execute
28674      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28675      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28676      * saved in the current store (defaults to false)
28677      */
28678     doQuery : function(q, forceAll){
28679         
28680         Roo.log('doQuery?');
28681         if(q === undefined || q === null){
28682             q = '';
28683         }
28684         var qe = {
28685             query: q,
28686             forceAll: forceAll,
28687             combo: this,
28688             cancel:false
28689         };
28690         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28691             return false;
28692         }
28693         q = qe.query;
28694         forceAll = qe.forceAll;
28695         if(forceAll === true || (q.length >= this.minChars)){
28696             if(this.lastQuery != q || this.alwaysQuery){
28697                 this.lastQuery = q;
28698                 if(this.mode == 'local'){
28699                     this.selectedIndex = -1;
28700                     if(forceAll){
28701                         this.store.clearFilter();
28702                     }else{
28703                         this.store.filter(this.displayField, q);
28704                     }
28705                     this.onLoad();
28706                 }else{
28707                     this.store.baseParams[this.queryParam] = q;
28708                     this.store.load({
28709                         params: this.getParams(q)
28710                     });
28711                     this.expand();
28712                 }
28713             }else{
28714                 this.selectedIndex = -1;
28715                 this.onLoad();   
28716             }
28717         }
28718     },
28719
28720     // private
28721     getParams : function(q){
28722         var p = {};
28723         //p[this.queryParam] = q;
28724         if(this.pageSize){
28725             p.start = 0;
28726             p.limit = this.pageSize;
28727         }
28728         return p;
28729     },
28730
28731     /**
28732      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28733      */
28734     collapse : function(){
28735         
28736     },
28737
28738     // private
28739     collapseIf : function(e){
28740         
28741     },
28742
28743     /**
28744      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28745      */
28746     expand : function(){
28747         
28748     } ,
28749
28750     // private
28751      
28752
28753     /** 
28754     * @cfg {Boolean} grow 
28755     * @hide 
28756     */
28757     /** 
28758     * @cfg {Number} growMin 
28759     * @hide 
28760     */
28761     /** 
28762     * @cfg {Number} growMax 
28763     * @hide 
28764     */
28765     /**
28766      * @hide
28767      * @method autoSize
28768      */
28769     
28770     setWidth : function()
28771     {
28772         
28773     },
28774     getResizeEl : function(){
28775         return this.el;
28776     }
28777 });//<script type="text/javasscript">
28778  
28779
28780 /**
28781  * @class Roo.DDView
28782  * A DnD enabled version of Roo.View.
28783  * @param {Element/String} container The Element in which to create the View.
28784  * @param {String} tpl The template string used to create the markup for each element of the View
28785  * @param {Object} config The configuration properties. These include all the config options of
28786  * {@link Roo.View} plus some specific to this class.<br>
28787  * <p>
28788  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28789  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28790  * <p>
28791  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28792 .x-view-drag-insert-above {
28793         border-top:1px dotted #3366cc;
28794 }
28795 .x-view-drag-insert-below {
28796         border-bottom:1px dotted #3366cc;
28797 }
28798 </code></pre>
28799  * 
28800  */
28801  
28802 Roo.DDView = function(container, tpl, config) {
28803     Roo.DDView.superclass.constructor.apply(this, arguments);
28804     this.getEl().setStyle("outline", "0px none");
28805     this.getEl().unselectable();
28806     if (this.dragGroup) {
28807                 this.setDraggable(this.dragGroup.split(","));
28808     }
28809     if (this.dropGroup) {
28810                 this.setDroppable(this.dropGroup.split(","));
28811     }
28812     if (this.deletable) {
28813         this.setDeletable();
28814     }
28815     this.isDirtyFlag = false;
28816         this.addEvents({
28817                 "drop" : true
28818         });
28819 };
28820
28821 Roo.extend(Roo.DDView, Roo.View, {
28822 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28823 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28824 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28825 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28826
28827         isFormField: true,
28828
28829         reset: Roo.emptyFn,
28830         
28831         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28832
28833         validate: function() {
28834                 return true;
28835         },
28836         
28837         destroy: function() {
28838                 this.purgeListeners();
28839                 this.getEl.removeAllListeners();
28840                 this.getEl().remove();
28841                 if (this.dragZone) {
28842                         if (this.dragZone.destroy) {
28843                                 this.dragZone.destroy();
28844                         }
28845                 }
28846                 if (this.dropZone) {
28847                         if (this.dropZone.destroy) {
28848                                 this.dropZone.destroy();
28849                         }
28850                 }
28851         },
28852
28853 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28854         getName: function() {
28855                 return this.name;
28856         },
28857
28858 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28859         setValue: function(v) {
28860                 if (!this.store) {
28861                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28862                 }
28863                 var data = {};
28864                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28865                 this.store.proxy = new Roo.data.MemoryProxy(data);
28866                 this.store.load();
28867         },
28868
28869 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28870         getValue: function() {
28871                 var result = '(';
28872                 this.store.each(function(rec) {
28873                         result += rec.id + ',';
28874                 });
28875                 return result.substr(0, result.length - 1) + ')';
28876         },
28877         
28878         getIds: function() {
28879                 var i = 0, result = new Array(this.store.getCount());
28880                 this.store.each(function(rec) {
28881                         result[i++] = rec.id;
28882                 });
28883                 return result;
28884         },
28885         
28886         isDirty: function() {
28887                 return this.isDirtyFlag;
28888         },
28889
28890 /**
28891  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28892  *      whole Element becomes the target, and this causes the drop gesture to append.
28893  */
28894     getTargetFromEvent : function(e) {
28895                 var target = e.getTarget();
28896                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28897                 target = target.parentNode;
28898                 }
28899                 if (!target) {
28900                         target = this.el.dom.lastChild || this.el.dom;
28901                 }
28902                 return target;
28903     },
28904
28905 /**
28906  *      Create the drag data which consists of an object which has the property "ddel" as
28907  *      the drag proxy element. 
28908  */
28909     getDragData : function(e) {
28910         var target = this.findItemFromChild(e.getTarget());
28911                 if(target) {
28912                         this.handleSelection(e);
28913                         var selNodes = this.getSelectedNodes();
28914             var dragData = {
28915                 source: this,
28916                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28917                 nodes: selNodes,
28918                 records: []
28919                         };
28920                         var selectedIndices = this.getSelectedIndexes();
28921                         for (var i = 0; i < selectedIndices.length; i++) {
28922                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28923                         }
28924                         if (selNodes.length == 1) {
28925                                 dragData.ddel = target.cloneNode(true); // the div element
28926                         } else {
28927                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28928                                 div.className = 'multi-proxy';
28929                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28930                                         div.appendChild(selNodes[i].cloneNode(true));
28931                                 }
28932                                 dragData.ddel = div;
28933                         }
28934             //console.log(dragData)
28935             //console.log(dragData.ddel.innerHTML)
28936                         return dragData;
28937                 }
28938         //console.log('nodragData')
28939                 return false;
28940     },
28941     
28942 /**     Specify to which ddGroup items in this DDView may be dragged. */
28943     setDraggable: function(ddGroup) {
28944         if (ddGroup instanceof Array) {
28945                 Roo.each(ddGroup, this.setDraggable, this);
28946                 return;
28947         }
28948         if (this.dragZone) {
28949                 this.dragZone.addToGroup(ddGroup);
28950         } else {
28951                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28952                                 containerScroll: true,
28953                                 ddGroup: ddGroup 
28954
28955                         });
28956 //                      Draggability implies selection. DragZone's mousedown selects the element.
28957                         if (!this.multiSelect) { this.singleSelect = true; }
28958
28959 //                      Wire the DragZone's handlers up to methods in *this*
28960                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28961                 }
28962     },
28963
28964 /**     Specify from which ddGroup this DDView accepts drops. */
28965     setDroppable: function(ddGroup) {
28966         if (ddGroup instanceof Array) {
28967                 Roo.each(ddGroup, this.setDroppable, this);
28968                 return;
28969         }
28970         if (this.dropZone) {
28971                 this.dropZone.addToGroup(ddGroup);
28972         } else {
28973                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28974                                 containerScroll: true,
28975                                 ddGroup: ddGroup
28976                         });
28977
28978 //                      Wire the DropZone's handlers up to methods in *this*
28979                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28980                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28981                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28982                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28983                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28984                 }
28985     },
28986
28987 /**     Decide whether to drop above or below a View node. */
28988     getDropPoint : function(e, n, dd){
28989         if (n == this.el.dom) { return "above"; }
28990                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28991                 var c = t + (b - t) / 2;
28992                 var y = Roo.lib.Event.getPageY(e);
28993                 if(y <= c) {
28994                         return "above";
28995                 }else{
28996                         return "below";
28997                 }
28998     },
28999
29000     onNodeEnter : function(n, dd, e, data){
29001                 return false;
29002     },
29003     
29004     onNodeOver : function(n, dd, e, data){
29005                 var pt = this.getDropPoint(e, n, dd);
29006                 // set the insert point style on the target node
29007                 var dragElClass = this.dropNotAllowed;
29008                 if (pt) {
29009                         var targetElClass;
29010                         if (pt == "above"){
29011                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29012                                 targetElClass = "x-view-drag-insert-above";
29013                         } else {
29014                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29015                                 targetElClass = "x-view-drag-insert-below";
29016                         }
29017                         if (this.lastInsertClass != targetElClass){
29018                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29019                                 this.lastInsertClass = targetElClass;
29020                         }
29021                 }
29022                 return dragElClass;
29023         },
29024
29025     onNodeOut : function(n, dd, e, data){
29026                 this.removeDropIndicators(n);
29027     },
29028
29029     onNodeDrop : function(n, dd, e, data){
29030         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29031                 return false;
29032         }
29033         var pt = this.getDropPoint(e, n, dd);
29034                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29035                 if (pt == "below") { insertAt++; }
29036                 for (var i = 0; i < data.records.length; i++) {
29037                         var r = data.records[i];
29038                         var dup = this.store.getById(r.id);
29039                         if (dup && (dd != this.dragZone)) {
29040                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29041                         } else {
29042                                 if (data.copy) {
29043                                         this.store.insert(insertAt++, r.copy());
29044                                 } else {
29045                                         data.source.isDirtyFlag = true;
29046                                         r.store.remove(r);
29047                                         this.store.insert(insertAt++, r);
29048                                 }
29049                                 this.isDirtyFlag = true;
29050                         }
29051                 }
29052                 this.dragZone.cachedTarget = null;
29053                 return true;
29054     },
29055
29056     removeDropIndicators : function(n){
29057                 if(n){
29058                         Roo.fly(n).removeClass([
29059                                 "x-view-drag-insert-above",
29060                                 "x-view-drag-insert-below"]);
29061                         this.lastInsertClass = "_noclass";
29062                 }
29063     },
29064
29065 /**
29066  *      Utility method. Add a delete option to the DDView's context menu.
29067  *      @param {String} imageUrl The URL of the "delete" icon image.
29068  */
29069         setDeletable: function(imageUrl) {
29070                 if (!this.singleSelect && !this.multiSelect) {
29071                         this.singleSelect = true;
29072                 }
29073                 var c = this.getContextMenu();
29074                 this.contextMenu.on("itemclick", function(item) {
29075                         switch (item.id) {
29076                                 case "delete":
29077                                         this.remove(this.getSelectedIndexes());
29078                                         break;
29079                         }
29080                 }, this);
29081                 this.contextMenu.add({
29082                         icon: imageUrl,
29083                         id: "delete",
29084                         text: 'Delete'
29085                 });
29086         },
29087         
29088 /**     Return the context menu for this DDView. */
29089         getContextMenu: function() {
29090                 if (!this.contextMenu) {
29091 //                      Create the View's context menu
29092                         this.contextMenu = new Roo.menu.Menu({
29093                                 id: this.id + "-contextmenu"
29094                         });
29095                         this.el.on("contextmenu", this.showContextMenu, this);
29096                 }
29097                 return this.contextMenu;
29098         },
29099         
29100         disableContextMenu: function() {
29101                 if (this.contextMenu) {
29102                         this.el.un("contextmenu", this.showContextMenu, this);
29103                 }
29104         },
29105
29106         showContextMenu: function(e, item) {
29107         item = this.findItemFromChild(e.getTarget());
29108                 if (item) {
29109                         e.stopEvent();
29110                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29111                         this.contextMenu.showAt(e.getXY());
29112             }
29113     },
29114
29115 /**
29116  *      Remove {@link Roo.data.Record}s at the specified indices.
29117  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29118  */
29119     remove: function(selectedIndices) {
29120                 selectedIndices = [].concat(selectedIndices);
29121                 for (var i = 0; i < selectedIndices.length; i++) {
29122                         var rec = this.store.getAt(selectedIndices[i]);
29123                         this.store.remove(rec);
29124                 }
29125     },
29126
29127 /**
29128  *      Double click fires the event, but also, if this is draggable, and there is only one other
29129  *      related DropZone, it transfers the selected node.
29130  */
29131     onDblClick : function(e){
29132         var item = this.findItemFromChild(e.getTarget());
29133         if(item){
29134             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29135                 return false;
29136             }
29137             if (this.dragGroup) {
29138                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29139                     while (targets.indexOf(this.dropZone) > -1) {
29140                             targets.remove(this.dropZone);
29141                                 }
29142                     if (targets.length == 1) {
29143                                         this.dragZone.cachedTarget = null;
29144                         var el = Roo.get(targets[0].getEl());
29145                         var box = el.getBox(true);
29146                         targets[0].onNodeDrop(el.dom, {
29147                                 target: el.dom,
29148                                 xy: [box.x, box.y + box.height - 1]
29149                         }, null, this.getDragData(e));
29150                     }
29151                 }
29152         }
29153     },
29154     
29155     handleSelection: function(e) {
29156                 this.dragZone.cachedTarget = null;
29157         var item = this.findItemFromChild(e.getTarget());
29158         if (!item) {
29159                 this.clearSelections(true);
29160                 return;
29161         }
29162                 if (item && (this.multiSelect || this.singleSelect)){
29163                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29164                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29165                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29166                                 this.unselect(item);
29167                         } else {
29168                                 this.select(item, this.multiSelect && e.ctrlKey);
29169                                 this.lastSelection = item;
29170                         }
29171                 }
29172     },
29173
29174     onItemClick : function(item, index, e){
29175                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29176                         return false;
29177                 }
29178                 return true;
29179     },
29180
29181     unselect : function(nodeInfo, suppressEvent){
29182                 var node = this.getNode(nodeInfo);
29183                 if(node && this.isSelected(node)){
29184                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29185                                 Roo.fly(node).removeClass(this.selectedClass);
29186                                 this.selections.remove(node);
29187                                 if(!suppressEvent){
29188                                         this.fireEvent("selectionchange", this, this.selections);
29189                                 }
29190                         }
29191                 }
29192     }
29193 });
29194 /*
29195  * Based on:
29196  * Ext JS Library 1.1.1
29197  * Copyright(c) 2006-2007, Ext JS, LLC.
29198  *
29199  * Originally Released Under LGPL - original licence link has changed is not relivant.
29200  *
29201  * Fork - LGPL
29202  * <script type="text/javascript">
29203  */
29204  
29205 /**
29206  * @class Roo.LayoutManager
29207  * @extends Roo.util.Observable
29208  * Base class for layout managers.
29209  */
29210 Roo.LayoutManager = function(container, config){
29211     Roo.LayoutManager.superclass.constructor.call(this);
29212     this.el = Roo.get(container);
29213     // ie scrollbar fix
29214     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29215         document.body.scroll = "no";
29216     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29217         this.el.position('relative');
29218     }
29219     this.id = this.el.id;
29220     this.el.addClass("x-layout-container");
29221     /** false to disable window resize monitoring @type Boolean */
29222     this.monitorWindowResize = true;
29223     this.regions = {};
29224     this.addEvents({
29225         /**
29226          * @event layout
29227          * Fires when a layout is performed. 
29228          * @param {Roo.LayoutManager} this
29229          */
29230         "layout" : true,
29231         /**
29232          * @event regionresized
29233          * Fires when the user resizes a region. 
29234          * @param {Roo.LayoutRegion} region The resized region
29235          * @param {Number} newSize The new size (width for east/west, height for north/south)
29236          */
29237         "regionresized" : true,
29238         /**
29239          * @event regioncollapsed
29240          * Fires when a region is collapsed. 
29241          * @param {Roo.LayoutRegion} region The collapsed region
29242          */
29243         "regioncollapsed" : true,
29244         /**
29245          * @event regionexpanded
29246          * Fires when a region is expanded.  
29247          * @param {Roo.LayoutRegion} region The expanded region
29248          */
29249         "regionexpanded" : true
29250     });
29251     this.updating = false;
29252     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29253 };
29254
29255 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29256     /**
29257      * Returns true if this layout is currently being updated
29258      * @return {Boolean}
29259      */
29260     isUpdating : function(){
29261         return this.updating; 
29262     },
29263     
29264     /**
29265      * Suspend the LayoutManager from doing auto-layouts while
29266      * making multiple add or remove calls
29267      */
29268     beginUpdate : function(){
29269         this.updating = true;    
29270     },
29271     
29272     /**
29273      * Restore auto-layouts and optionally disable the manager from performing a layout
29274      * @param {Boolean} noLayout true to disable a layout update 
29275      */
29276     endUpdate : function(noLayout){
29277         this.updating = false;
29278         if(!noLayout){
29279             this.layout();
29280         }    
29281     },
29282     
29283     layout: function(){
29284         
29285     },
29286     
29287     onRegionResized : function(region, newSize){
29288         this.fireEvent("regionresized", region, newSize);
29289         this.layout();
29290     },
29291     
29292     onRegionCollapsed : function(region){
29293         this.fireEvent("regioncollapsed", region);
29294     },
29295     
29296     onRegionExpanded : function(region){
29297         this.fireEvent("regionexpanded", region);
29298     },
29299         
29300     /**
29301      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29302      * performs box-model adjustments.
29303      * @return {Object} The size as an object {width: (the width), height: (the height)}
29304      */
29305     getViewSize : function(){
29306         var size;
29307         if(this.el.dom != document.body){
29308             size = this.el.getSize();
29309         }else{
29310             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29311         }
29312         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29313         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29314         return size;
29315     },
29316     
29317     /**
29318      * Returns the Element this layout is bound to.
29319      * @return {Roo.Element}
29320      */
29321     getEl : function(){
29322         return this.el;
29323     },
29324     
29325     /**
29326      * Returns the specified region.
29327      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29328      * @return {Roo.LayoutRegion}
29329      */
29330     getRegion : function(target){
29331         return this.regions[target.toLowerCase()];
29332     },
29333     
29334     onWindowResize : function(){
29335         if(this.monitorWindowResize){
29336             this.layout();
29337         }
29338     }
29339 });/*
29340  * Based on:
29341  * Ext JS Library 1.1.1
29342  * Copyright(c) 2006-2007, Ext JS, LLC.
29343  *
29344  * Originally Released Under LGPL - original licence link has changed is not relivant.
29345  *
29346  * Fork - LGPL
29347  * <script type="text/javascript">
29348  */
29349 /**
29350  * @class Roo.BorderLayout
29351  * @extends Roo.LayoutManager
29352  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29353  * please see: <br><br>
29354  * <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>
29355  * <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>
29356  * Example:
29357  <pre><code>
29358  var layout = new Roo.BorderLayout(document.body, {
29359     north: {
29360         initialSize: 25,
29361         titlebar: false
29362     },
29363     west: {
29364         split:true,
29365         initialSize: 200,
29366         minSize: 175,
29367         maxSize: 400,
29368         titlebar: true,
29369         collapsible: true
29370     },
29371     east: {
29372         split:true,
29373         initialSize: 202,
29374         minSize: 175,
29375         maxSize: 400,
29376         titlebar: true,
29377         collapsible: true
29378     },
29379     south: {
29380         split:true,
29381         initialSize: 100,
29382         minSize: 100,
29383         maxSize: 200,
29384         titlebar: true,
29385         collapsible: true
29386     },
29387     center: {
29388         titlebar: true,
29389         autoScroll:true,
29390         resizeTabs: true,
29391         minTabWidth: 50,
29392         preferredTabWidth: 150
29393     }
29394 });
29395
29396 // shorthand
29397 var CP = Roo.ContentPanel;
29398
29399 layout.beginUpdate();
29400 layout.add("north", new CP("north", "North"));
29401 layout.add("south", new CP("south", {title: "South", closable: true}));
29402 layout.add("west", new CP("west", {title: "West"}));
29403 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29404 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29405 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29406 layout.getRegion("center").showPanel("center1");
29407 layout.endUpdate();
29408 </code></pre>
29409
29410 <b>The container the layout is rendered into can be either the body element or any other element.
29411 If it is not the body element, the container needs to either be an absolute positioned element,
29412 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29413 the container size if it is not the body element.</b>
29414
29415 * @constructor
29416 * Create a new BorderLayout
29417 * @param {String/HTMLElement/Element} container The container this layout is bound to
29418 * @param {Object} config Configuration options
29419  */
29420 Roo.BorderLayout = function(container, config){
29421     config = config || {};
29422     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29423     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29424     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29425         var target = this.factory.validRegions[i];
29426         if(config[target]){
29427             this.addRegion(target, config[target]);
29428         }
29429     }
29430 };
29431
29432 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29433     /**
29434      * Creates and adds a new region if it doesn't already exist.
29435      * @param {String} target The target region key (north, south, east, west or center).
29436      * @param {Object} config The regions config object
29437      * @return {BorderLayoutRegion} The new region
29438      */
29439     addRegion : function(target, config){
29440         if(!this.regions[target]){
29441             var r = this.factory.create(target, this, config);
29442             this.bindRegion(target, r);
29443         }
29444         return this.regions[target];
29445     },
29446
29447     // private (kinda)
29448     bindRegion : function(name, r){
29449         this.regions[name] = r;
29450         r.on("visibilitychange", this.layout, this);
29451         r.on("paneladded", this.layout, this);
29452         r.on("panelremoved", this.layout, this);
29453         r.on("invalidated", this.layout, this);
29454         r.on("resized", this.onRegionResized, this);
29455         r.on("collapsed", this.onRegionCollapsed, this);
29456         r.on("expanded", this.onRegionExpanded, this);
29457     },
29458
29459     /**
29460      * Performs a layout update.
29461      */
29462     layout : function(){
29463         if(this.updating) {
29464             return;
29465         }
29466         var size = this.getViewSize();
29467         var w = size.width;
29468         var h = size.height;
29469         var centerW = w;
29470         var centerH = h;
29471         var centerY = 0;
29472         var centerX = 0;
29473         //var x = 0, y = 0;
29474
29475         var rs = this.regions;
29476         var north = rs["north"];
29477         var south = rs["south"]; 
29478         var west = rs["west"];
29479         var east = rs["east"];
29480         var center = rs["center"];
29481         //if(this.hideOnLayout){ // not supported anymore
29482             //c.el.setStyle("display", "none");
29483         //}
29484         if(north && north.isVisible()){
29485             var b = north.getBox();
29486             var m = north.getMargins();
29487             b.width = w - (m.left+m.right);
29488             b.x = m.left;
29489             b.y = m.top;
29490             centerY = b.height + b.y + m.bottom;
29491             centerH -= centerY;
29492             north.updateBox(this.safeBox(b));
29493         }
29494         if(south && south.isVisible()){
29495             var b = south.getBox();
29496             var m = south.getMargins();
29497             b.width = w - (m.left+m.right);
29498             b.x = m.left;
29499             var totalHeight = (b.height + m.top + m.bottom);
29500             b.y = h - totalHeight + m.top;
29501             centerH -= totalHeight;
29502             south.updateBox(this.safeBox(b));
29503         }
29504         if(west && west.isVisible()){
29505             var b = west.getBox();
29506             var m = west.getMargins();
29507             b.height = centerH - (m.top+m.bottom);
29508             b.x = m.left;
29509             b.y = centerY + m.top;
29510             var totalWidth = (b.width + m.left + m.right);
29511             centerX += totalWidth;
29512             centerW -= totalWidth;
29513             west.updateBox(this.safeBox(b));
29514         }
29515         if(east && east.isVisible()){
29516             var b = east.getBox();
29517             var m = east.getMargins();
29518             b.height = centerH - (m.top+m.bottom);
29519             var totalWidth = (b.width + m.left + m.right);
29520             b.x = w - totalWidth + m.left;
29521             b.y = centerY + m.top;
29522             centerW -= totalWidth;
29523             east.updateBox(this.safeBox(b));
29524         }
29525         if(center){
29526             var m = center.getMargins();
29527             var centerBox = {
29528                 x: centerX + m.left,
29529                 y: centerY + m.top,
29530                 width: centerW - (m.left+m.right),
29531                 height: centerH - (m.top+m.bottom)
29532             };
29533             //if(this.hideOnLayout){
29534                 //center.el.setStyle("display", "block");
29535             //}
29536             center.updateBox(this.safeBox(centerBox));
29537         }
29538         this.el.repaint();
29539         this.fireEvent("layout", this);
29540     },
29541
29542     // private
29543     safeBox : function(box){
29544         box.width = Math.max(0, box.width);
29545         box.height = Math.max(0, box.height);
29546         return box;
29547     },
29548
29549     /**
29550      * Adds a ContentPanel (or subclass) to this layout.
29551      * @param {String} target The target region key (north, south, east, west or center).
29552      * @param {Roo.ContentPanel} panel The panel to add
29553      * @return {Roo.ContentPanel} The added panel
29554      */
29555     add : function(target, panel){
29556          
29557         target = target.toLowerCase();
29558         return this.regions[target].add(panel);
29559     },
29560
29561     /**
29562      * Remove a ContentPanel (or subclass) to this layout.
29563      * @param {String} target The target region key (north, south, east, west or center).
29564      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29565      * @return {Roo.ContentPanel} The removed panel
29566      */
29567     remove : function(target, panel){
29568         target = target.toLowerCase();
29569         return this.regions[target].remove(panel);
29570     },
29571
29572     /**
29573      * Searches all regions for a panel with the specified id
29574      * @param {String} panelId
29575      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29576      */
29577     findPanel : function(panelId){
29578         var rs = this.regions;
29579         for(var target in rs){
29580             if(typeof rs[target] != "function"){
29581                 var p = rs[target].getPanel(panelId);
29582                 if(p){
29583                     return p;
29584                 }
29585             }
29586         }
29587         return null;
29588     },
29589
29590     /**
29591      * Searches all regions for a panel with the specified id and activates (shows) it.
29592      * @param {String/ContentPanel} panelId The panels id or the panel itself
29593      * @return {Roo.ContentPanel} The shown panel or null
29594      */
29595     showPanel : function(panelId) {
29596       var rs = this.regions;
29597       for(var target in rs){
29598          var r = rs[target];
29599          if(typeof r != "function"){
29600             if(r.hasPanel(panelId)){
29601                return r.showPanel(panelId);
29602             }
29603          }
29604       }
29605       return null;
29606    },
29607
29608    /**
29609      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29610      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29611      */
29612     restoreState : function(provider){
29613         if(!provider){
29614             provider = Roo.state.Manager;
29615         }
29616         var sm = new Roo.LayoutStateManager();
29617         sm.init(this, provider);
29618     },
29619
29620     /**
29621      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29622      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29623      * a valid ContentPanel config object.  Example:
29624      * <pre><code>
29625 // Create the main layout
29626 var layout = new Roo.BorderLayout('main-ct', {
29627     west: {
29628         split:true,
29629         minSize: 175,
29630         titlebar: true
29631     },
29632     center: {
29633         title:'Components'
29634     }
29635 }, 'main-ct');
29636
29637 // Create and add multiple ContentPanels at once via configs
29638 layout.batchAdd({
29639    west: {
29640        id: 'source-files',
29641        autoCreate:true,
29642        title:'Ext Source Files',
29643        autoScroll:true,
29644        fitToFrame:true
29645    },
29646    center : {
29647        el: cview,
29648        autoScroll:true,
29649        fitToFrame:true,
29650        toolbar: tb,
29651        resizeEl:'cbody'
29652    }
29653 });
29654 </code></pre>
29655      * @param {Object} regions An object containing ContentPanel configs by region name
29656      */
29657     batchAdd : function(regions){
29658         this.beginUpdate();
29659         for(var rname in regions){
29660             var lr = this.regions[rname];
29661             if(lr){
29662                 this.addTypedPanels(lr, regions[rname]);
29663             }
29664         }
29665         this.endUpdate();
29666     },
29667
29668     // private
29669     addTypedPanels : function(lr, ps){
29670         if(typeof ps == 'string'){
29671             lr.add(new Roo.ContentPanel(ps));
29672         }
29673         else if(ps instanceof Array){
29674             for(var i =0, len = ps.length; i < len; i++){
29675                 this.addTypedPanels(lr, ps[i]);
29676             }
29677         }
29678         else if(!ps.events){ // raw config?
29679             var el = ps.el;
29680             delete ps.el; // prevent conflict
29681             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29682         }
29683         else {  // panel object assumed!
29684             lr.add(ps);
29685         }
29686     },
29687     /**
29688      * Adds a xtype elements to the layout.
29689      * <pre><code>
29690
29691 layout.addxtype({
29692        xtype : 'ContentPanel',
29693        region: 'west',
29694        items: [ .... ]
29695    }
29696 );
29697
29698 layout.addxtype({
29699         xtype : 'NestedLayoutPanel',
29700         region: 'west',
29701         layout: {
29702            center: { },
29703            west: { }   
29704         },
29705         items : [ ... list of content panels or nested layout panels.. ]
29706    }
29707 );
29708 </code></pre>
29709      * @param {Object} cfg Xtype definition of item to add.
29710      */
29711     addxtype : function(cfg)
29712     {
29713         // basically accepts a pannel...
29714         // can accept a layout region..!?!?
29715         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29716         
29717         if (!cfg.xtype.match(/Panel$/)) {
29718             return false;
29719         }
29720         var ret = false;
29721         
29722         if (typeof(cfg.region) == 'undefined') {
29723             Roo.log("Failed to add Panel, region was not set");
29724             Roo.log(cfg);
29725             return false;
29726         }
29727         var region = cfg.region;
29728         delete cfg.region;
29729         
29730           
29731         var xitems = [];
29732         if (cfg.items) {
29733             xitems = cfg.items;
29734             delete cfg.items;
29735         }
29736         var nb = false;
29737         
29738         switch(cfg.xtype) 
29739         {
29740             case 'ContentPanel':  // ContentPanel (el, cfg)
29741             case 'ScrollPanel':  // ContentPanel (el, cfg)
29742             case 'ViewPanel': 
29743                 if(cfg.autoCreate) {
29744                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29745                 } else {
29746                     var el = this.el.createChild();
29747                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29748                 }
29749                 
29750                 this.add(region, ret);
29751                 break;
29752             
29753             
29754             case 'TreePanel': // our new panel!
29755                 cfg.el = this.el.createChild();
29756                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29757                 this.add(region, ret);
29758                 break;
29759             
29760             case 'NestedLayoutPanel': 
29761                 // create a new Layout (which is  a Border Layout...
29762                 var el = this.el.createChild();
29763                 var clayout = cfg.layout;
29764                 delete cfg.layout;
29765                 clayout.items   = clayout.items  || [];
29766                 // replace this exitems with the clayout ones..
29767                 xitems = clayout.items;
29768                  
29769                 
29770                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29771                     cfg.background = false;
29772                 }
29773                 var layout = new Roo.BorderLayout(el, clayout);
29774                 
29775                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29776                 //console.log('adding nested layout panel '  + cfg.toSource());
29777                 this.add(region, ret);
29778                 nb = {}; /// find first...
29779                 break;
29780                 
29781             case 'GridPanel': 
29782             
29783                 // needs grid and region
29784                 
29785                 //var el = this.getRegion(region).el.createChild();
29786                 var el = this.el.createChild();
29787                 // create the grid first...
29788                 
29789                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29790                 delete cfg.grid;
29791                 if (region == 'center' && this.active ) {
29792                     cfg.background = false;
29793                 }
29794                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29795                 
29796                 this.add(region, ret);
29797                 if (cfg.background) {
29798                     ret.on('activate', function(gp) {
29799                         if (!gp.grid.rendered) {
29800                             gp.grid.render();
29801                         }
29802                     });
29803                 } else {
29804                     grid.render();
29805                 }
29806                 break;
29807            
29808            
29809            
29810                 
29811                 
29812                 
29813             default:
29814                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29815                     
29816                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29817                     this.add(region, ret);
29818                 } else {
29819                 
29820                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29821                     return null;
29822                 }
29823                 
29824              // GridPanel (grid, cfg)
29825             
29826         }
29827         this.beginUpdate();
29828         // add children..
29829         var region = '';
29830         var abn = {};
29831         Roo.each(xitems, function(i)  {
29832             region = nb && i.region ? i.region : false;
29833             
29834             var add = ret.addxtype(i);
29835            
29836             if (region) {
29837                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29838                 if (!i.background) {
29839                     abn[region] = nb[region] ;
29840                 }
29841             }
29842             
29843         });
29844         this.endUpdate();
29845
29846         // make the last non-background panel active..
29847         //if (nb) { Roo.log(abn); }
29848         if (nb) {
29849             
29850             for(var r in abn) {
29851                 region = this.getRegion(r);
29852                 if (region) {
29853                     // tried using nb[r], but it does not work..
29854                      
29855                     region.showPanel(abn[r]);
29856                    
29857                 }
29858             }
29859         }
29860         return ret;
29861         
29862     }
29863 });
29864
29865 /**
29866  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29867  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29868  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29869  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29870  * <pre><code>
29871 // shorthand
29872 var CP = Roo.ContentPanel;
29873
29874 var layout = Roo.BorderLayout.create({
29875     north: {
29876         initialSize: 25,
29877         titlebar: false,
29878         panels: [new CP("north", "North")]
29879     },
29880     west: {
29881         split:true,
29882         initialSize: 200,
29883         minSize: 175,
29884         maxSize: 400,
29885         titlebar: true,
29886         collapsible: true,
29887         panels: [new CP("west", {title: "West"})]
29888     },
29889     east: {
29890         split:true,
29891         initialSize: 202,
29892         minSize: 175,
29893         maxSize: 400,
29894         titlebar: true,
29895         collapsible: true,
29896         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29897     },
29898     south: {
29899         split:true,
29900         initialSize: 100,
29901         minSize: 100,
29902         maxSize: 200,
29903         titlebar: true,
29904         collapsible: true,
29905         panels: [new CP("south", {title: "South", closable: true})]
29906     },
29907     center: {
29908         titlebar: true,
29909         autoScroll:true,
29910         resizeTabs: true,
29911         minTabWidth: 50,
29912         preferredTabWidth: 150,
29913         panels: [
29914             new CP("center1", {title: "Close Me", closable: true}),
29915             new CP("center2", {title: "Center Panel", closable: false})
29916         ]
29917     }
29918 }, document.body);
29919
29920 layout.getRegion("center").showPanel("center1");
29921 </code></pre>
29922  * @param config
29923  * @param targetEl
29924  */
29925 Roo.BorderLayout.create = function(config, targetEl){
29926     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29927     layout.beginUpdate();
29928     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29929     for(var j = 0, jlen = regions.length; j < jlen; j++){
29930         var lr = regions[j];
29931         if(layout.regions[lr] && config[lr].panels){
29932             var r = layout.regions[lr];
29933             var ps = config[lr].panels;
29934             layout.addTypedPanels(r, ps);
29935         }
29936     }
29937     layout.endUpdate();
29938     return layout;
29939 };
29940
29941 // private
29942 Roo.BorderLayout.RegionFactory = {
29943     // private
29944     validRegions : ["north","south","east","west","center"],
29945
29946     // private
29947     create : function(target, mgr, config){
29948         target = target.toLowerCase();
29949         if(config.lightweight || config.basic){
29950             return new Roo.BasicLayoutRegion(mgr, config, target);
29951         }
29952         switch(target){
29953             case "north":
29954                 return new Roo.NorthLayoutRegion(mgr, config);
29955             case "south":
29956                 return new Roo.SouthLayoutRegion(mgr, config);
29957             case "east":
29958                 return new Roo.EastLayoutRegion(mgr, config);
29959             case "west":
29960                 return new Roo.WestLayoutRegion(mgr, config);
29961             case "center":
29962                 return new Roo.CenterLayoutRegion(mgr, config);
29963         }
29964         throw 'Layout region "'+target+'" not supported.';
29965     }
29966 };/*
29967  * Based on:
29968  * Ext JS Library 1.1.1
29969  * Copyright(c) 2006-2007, Ext JS, LLC.
29970  *
29971  * Originally Released Under LGPL - original licence link has changed is not relivant.
29972  *
29973  * Fork - LGPL
29974  * <script type="text/javascript">
29975  */
29976  
29977 /**
29978  * @class Roo.BasicLayoutRegion
29979  * @extends Roo.util.Observable
29980  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29981  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29982  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29983  */
29984 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29985     this.mgr = mgr;
29986     this.position  = pos;
29987     this.events = {
29988         /**
29989          * @scope Roo.BasicLayoutRegion
29990          */
29991         
29992         /**
29993          * @event beforeremove
29994          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29995          * @param {Roo.LayoutRegion} this
29996          * @param {Roo.ContentPanel} panel The panel
29997          * @param {Object} e The cancel event object
29998          */
29999         "beforeremove" : true,
30000         /**
30001          * @event invalidated
30002          * Fires when the layout for this region is changed.
30003          * @param {Roo.LayoutRegion} this
30004          */
30005         "invalidated" : true,
30006         /**
30007          * @event visibilitychange
30008          * Fires when this region is shown or hidden 
30009          * @param {Roo.LayoutRegion} this
30010          * @param {Boolean} visibility true or false
30011          */
30012         "visibilitychange" : true,
30013         /**
30014          * @event paneladded
30015          * Fires when a panel is added. 
30016          * @param {Roo.LayoutRegion} this
30017          * @param {Roo.ContentPanel} panel The panel
30018          */
30019         "paneladded" : true,
30020         /**
30021          * @event panelremoved
30022          * Fires when a panel is removed. 
30023          * @param {Roo.LayoutRegion} this
30024          * @param {Roo.ContentPanel} panel The panel
30025          */
30026         "panelremoved" : true,
30027         /**
30028          * @event beforecollapse
30029          * Fires when this region before collapse.
30030          * @param {Roo.LayoutRegion} this
30031          */
30032         "beforecollapse" : true,
30033         /**
30034          * @event collapsed
30035          * Fires when this region is collapsed.
30036          * @param {Roo.LayoutRegion} this
30037          */
30038         "collapsed" : true,
30039         /**
30040          * @event expanded
30041          * Fires when this region is expanded.
30042          * @param {Roo.LayoutRegion} this
30043          */
30044         "expanded" : true,
30045         /**
30046          * @event slideshow
30047          * Fires when this region is slid into view.
30048          * @param {Roo.LayoutRegion} this
30049          */
30050         "slideshow" : true,
30051         /**
30052          * @event slidehide
30053          * Fires when this region slides out of view. 
30054          * @param {Roo.LayoutRegion} this
30055          */
30056         "slidehide" : true,
30057         /**
30058          * @event panelactivated
30059          * Fires when a panel is activated. 
30060          * @param {Roo.LayoutRegion} this
30061          * @param {Roo.ContentPanel} panel The activated panel
30062          */
30063         "panelactivated" : true,
30064         /**
30065          * @event resized
30066          * Fires when the user resizes this region. 
30067          * @param {Roo.LayoutRegion} this
30068          * @param {Number} newSize The new size (width for east/west, height for north/south)
30069          */
30070         "resized" : true
30071     };
30072     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30073     this.panels = new Roo.util.MixedCollection();
30074     this.panels.getKey = this.getPanelId.createDelegate(this);
30075     this.box = null;
30076     this.activePanel = null;
30077     // ensure listeners are added...
30078     
30079     if (config.listeners || config.events) {
30080         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30081             listeners : config.listeners || {},
30082             events : config.events || {}
30083         });
30084     }
30085     
30086     if(skipConfig !== true){
30087         this.applyConfig(config);
30088     }
30089 };
30090
30091 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30092     getPanelId : function(p){
30093         return p.getId();
30094     },
30095     
30096     applyConfig : function(config){
30097         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30098         this.config = config;
30099         
30100     },
30101     
30102     /**
30103      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30104      * the width, for horizontal (north, south) the height.
30105      * @param {Number} newSize The new width or height
30106      */
30107     resizeTo : function(newSize){
30108         var el = this.el ? this.el :
30109                  (this.activePanel ? this.activePanel.getEl() : null);
30110         if(el){
30111             switch(this.position){
30112                 case "east":
30113                 case "west":
30114                     el.setWidth(newSize);
30115                     this.fireEvent("resized", this, newSize);
30116                 break;
30117                 case "north":
30118                 case "south":
30119                     el.setHeight(newSize);
30120                     this.fireEvent("resized", this, newSize);
30121                 break;                
30122             }
30123         }
30124     },
30125     
30126     getBox : function(){
30127         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30128     },
30129     
30130     getMargins : function(){
30131         return this.margins;
30132     },
30133     
30134     updateBox : function(box){
30135         this.box = box;
30136         var el = this.activePanel.getEl();
30137         el.dom.style.left = box.x + "px";
30138         el.dom.style.top = box.y + "px";
30139         this.activePanel.setSize(box.width, box.height);
30140     },
30141     
30142     /**
30143      * Returns the container element for this region.
30144      * @return {Roo.Element}
30145      */
30146     getEl : function(){
30147         return this.activePanel;
30148     },
30149     
30150     /**
30151      * Returns true if this region is currently visible.
30152      * @return {Boolean}
30153      */
30154     isVisible : function(){
30155         return this.activePanel ? true : false;
30156     },
30157     
30158     setActivePanel : function(panel){
30159         panel = this.getPanel(panel);
30160         if(this.activePanel && this.activePanel != panel){
30161             this.activePanel.setActiveState(false);
30162             this.activePanel.getEl().setLeftTop(-10000,-10000);
30163         }
30164         this.activePanel = panel;
30165         panel.setActiveState(true);
30166         if(this.box){
30167             panel.setSize(this.box.width, this.box.height);
30168         }
30169         this.fireEvent("panelactivated", this, panel);
30170         this.fireEvent("invalidated");
30171     },
30172     
30173     /**
30174      * Show the specified panel.
30175      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30176      * @return {Roo.ContentPanel} The shown panel or null
30177      */
30178     showPanel : function(panel){
30179         if(panel = this.getPanel(panel)){
30180             this.setActivePanel(panel);
30181         }
30182         return panel;
30183     },
30184     
30185     /**
30186      * Get the active panel for this region.
30187      * @return {Roo.ContentPanel} The active panel or null
30188      */
30189     getActivePanel : function(){
30190         return this.activePanel;
30191     },
30192     
30193     /**
30194      * Add the passed ContentPanel(s)
30195      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30196      * @return {Roo.ContentPanel} The panel added (if only one was added)
30197      */
30198     add : function(panel){
30199         if(arguments.length > 1){
30200             for(var i = 0, len = arguments.length; i < len; i++) {
30201                 this.add(arguments[i]);
30202             }
30203             return null;
30204         }
30205         if(this.hasPanel(panel)){
30206             this.showPanel(panel);
30207             return panel;
30208         }
30209         var el = panel.getEl();
30210         if(el.dom.parentNode != this.mgr.el.dom){
30211             this.mgr.el.dom.appendChild(el.dom);
30212         }
30213         if(panel.setRegion){
30214             panel.setRegion(this);
30215         }
30216         this.panels.add(panel);
30217         el.setStyle("position", "absolute");
30218         if(!panel.background){
30219             this.setActivePanel(panel);
30220             if(this.config.initialSize && this.panels.getCount()==1){
30221                 this.resizeTo(this.config.initialSize);
30222             }
30223         }
30224         this.fireEvent("paneladded", this, panel);
30225         return panel;
30226     },
30227     
30228     /**
30229      * Returns true if the panel is in this region.
30230      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30231      * @return {Boolean}
30232      */
30233     hasPanel : function(panel){
30234         if(typeof panel == "object"){ // must be panel obj
30235             panel = panel.getId();
30236         }
30237         return this.getPanel(panel) ? true : false;
30238     },
30239     
30240     /**
30241      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30242      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30243      * @param {Boolean} preservePanel Overrides the config preservePanel option
30244      * @return {Roo.ContentPanel} The panel that was removed
30245      */
30246     remove : function(panel, preservePanel){
30247         panel = this.getPanel(panel);
30248         if(!panel){
30249             return null;
30250         }
30251         var e = {};
30252         this.fireEvent("beforeremove", this, panel, e);
30253         if(e.cancel === true){
30254             return null;
30255         }
30256         var panelId = panel.getId();
30257         this.panels.removeKey(panelId);
30258         return panel;
30259     },
30260     
30261     /**
30262      * Returns the panel specified or null if it's not in this region.
30263      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30264      * @return {Roo.ContentPanel}
30265      */
30266     getPanel : function(id){
30267         if(typeof id == "object"){ // must be panel obj
30268             return id;
30269         }
30270         return this.panels.get(id);
30271     },
30272     
30273     /**
30274      * Returns this regions position (north/south/east/west/center).
30275      * @return {String} 
30276      */
30277     getPosition: function(){
30278         return this.position;    
30279     }
30280 });/*
30281  * Based on:
30282  * Ext JS Library 1.1.1
30283  * Copyright(c) 2006-2007, Ext JS, LLC.
30284  *
30285  * Originally Released Under LGPL - original licence link has changed is not relivant.
30286  *
30287  * Fork - LGPL
30288  * <script type="text/javascript">
30289  */
30290  
30291 /**
30292  * @class Roo.LayoutRegion
30293  * @extends Roo.BasicLayoutRegion
30294  * This class represents a region in a layout manager.
30295  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30296  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30297  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30298  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30299  * @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})
30300  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30301  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30302  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30303  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30304  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30305  * @cfg {String}    title           The title for the region (overrides panel titles)
30306  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30307  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30308  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30309  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30310  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30311  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30312  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30313  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30314  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30315  * @cfg {Boolean}   showPin         True to show a pin button
30316  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30317  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30318  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30319  * @cfg {Number}    width           For East/West panels
30320  * @cfg {Number}    height          For North/South panels
30321  * @cfg {Boolean}   split           To show the splitter
30322  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30323  */
30324 Roo.LayoutRegion = function(mgr, config, pos){
30325     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30326     var dh = Roo.DomHelper;
30327     /** This region's container element 
30328     * @type Roo.Element */
30329     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30330     /** This region's title element 
30331     * @type Roo.Element */
30332
30333     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30334         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30335         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30336     ]}, true);
30337     this.titleEl.enableDisplayMode();
30338     /** This region's title text element 
30339     * @type HTMLElement */
30340     this.titleTextEl = this.titleEl.dom.firstChild;
30341     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30342     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30343     this.closeBtn.enableDisplayMode();
30344     this.closeBtn.on("click", this.closeClicked, this);
30345     this.closeBtn.hide();
30346
30347     this.createBody(config);
30348     this.visible = true;
30349     this.collapsed = false;
30350
30351     if(config.hideWhenEmpty){
30352         this.hide();
30353         this.on("paneladded", this.validateVisibility, this);
30354         this.on("panelremoved", this.validateVisibility, this);
30355     }
30356     this.applyConfig(config);
30357 };
30358
30359 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30360
30361     createBody : function(){
30362         /** This region's body element 
30363         * @type Roo.Element */
30364         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30365     },
30366
30367     applyConfig : function(c){
30368         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30369             var dh = Roo.DomHelper;
30370             if(c.titlebar !== false){
30371                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30372                 this.collapseBtn.on("click", this.collapse, this);
30373                 this.collapseBtn.enableDisplayMode();
30374
30375                 if(c.showPin === true || this.showPin){
30376                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30377                     this.stickBtn.enableDisplayMode();
30378                     this.stickBtn.on("click", this.expand, this);
30379                     this.stickBtn.hide();
30380                 }
30381             }
30382             /** This region's collapsed element
30383             * @type Roo.Element */
30384             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30385                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30386             ]}, true);
30387             if(c.floatable !== false){
30388                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30389                this.collapsedEl.on("click", this.collapseClick, this);
30390             }
30391
30392             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30393                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30394                    id: "message", unselectable: "on", style:{"float":"left"}});
30395                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30396              }
30397             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30398             this.expandBtn.on("click", this.expand, this);
30399         }
30400         if(this.collapseBtn){
30401             this.collapseBtn.setVisible(c.collapsible == true);
30402         }
30403         this.cmargins = c.cmargins || this.cmargins ||
30404                          (this.position == "west" || this.position == "east" ?
30405                              {top: 0, left: 2, right:2, bottom: 0} :
30406                              {top: 2, left: 0, right:0, bottom: 2});
30407         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30408         this.bottomTabs = c.tabPosition != "top";
30409         this.autoScroll = c.autoScroll || false;
30410         if(this.autoScroll){
30411             this.bodyEl.setStyle("overflow", "auto");
30412         }else{
30413             this.bodyEl.setStyle("overflow", "hidden");
30414         }
30415         //if(c.titlebar !== false){
30416             if((!c.titlebar && !c.title) || c.titlebar === false){
30417                 this.titleEl.hide();
30418             }else{
30419                 this.titleEl.show();
30420                 if(c.title){
30421                     this.titleTextEl.innerHTML = c.title;
30422                 }
30423             }
30424         //}
30425         this.duration = c.duration || .30;
30426         this.slideDuration = c.slideDuration || .45;
30427         this.config = c;
30428         if(c.collapsed){
30429             this.collapse(true);
30430         }
30431         if(c.hidden){
30432             this.hide();
30433         }
30434     },
30435     /**
30436      * Returns true if this region is currently visible.
30437      * @return {Boolean}
30438      */
30439     isVisible : function(){
30440         return this.visible;
30441     },
30442
30443     /**
30444      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30445      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30446      */
30447     setCollapsedTitle : function(title){
30448         title = title || "&#160;";
30449         if(this.collapsedTitleTextEl){
30450             this.collapsedTitleTextEl.innerHTML = title;
30451         }
30452     },
30453
30454     getBox : function(){
30455         var b;
30456         if(!this.collapsed){
30457             b = this.el.getBox(false, true);
30458         }else{
30459             b = this.collapsedEl.getBox(false, true);
30460         }
30461         return b;
30462     },
30463
30464     getMargins : function(){
30465         return this.collapsed ? this.cmargins : this.margins;
30466     },
30467
30468     highlight : function(){
30469         this.el.addClass("x-layout-panel-dragover");
30470     },
30471
30472     unhighlight : function(){
30473         this.el.removeClass("x-layout-panel-dragover");
30474     },
30475
30476     updateBox : function(box){
30477         this.box = box;
30478         if(!this.collapsed){
30479             this.el.dom.style.left = box.x + "px";
30480             this.el.dom.style.top = box.y + "px";
30481             this.updateBody(box.width, box.height);
30482         }else{
30483             this.collapsedEl.dom.style.left = box.x + "px";
30484             this.collapsedEl.dom.style.top = box.y + "px";
30485             this.collapsedEl.setSize(box.width, box.height);
30486         }
30487         if(this.tabs){
30488             this.tabs.autoSizeTabs();
30489         }
30490     },
30491
30492     updateBody : function(w, h){
30493         if(w !== null){
30494             this.el.setWidth(w);
30495             w -= this.el.getBorderWidth("rl");
30496             if(this.config.adjustments){
30497                 w += this.config.adjustments[0];
30498             }
30499         }
30500         if(h !== null){
30501             this.el.setHeight(h);
30502             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30503             h -= this.el.getBorderWidth("tb");
30504             if(this.config.adjustments){
30505                 h += this.config.adjustments[1];
30506             }
30507             this.bodyEl.setHeight(h);
30508             if(this.tabs){
30509                 h = this.tabs.syncHeight(h);
30510             }
30511         }
30512         if(this.panelSize){
30513             w = w !== null ? w : this.panelSize.width;
30514             h = h !== null ? h : this.panelSize.height;
30515         }
30516         if(this.activePanel){
30517             var el = this.activePanel.getEl();
30518             w = w !== null ? w : el.getWidth();
30519             h = h !== null ? h : el.getHeight();
30520             this.panelSize = {width: w, height: h};
30521             this.activePanel.setSize(w, h);
30522         }
30523         if(Roo.isIE && this.tabs){
30524             this.tabs.el.repaint();
30525         }
30526     },
30527
30528     /**
30529      * Returns the container element for this region.
30530      * @return {Roo.Element}
30531      */
30532     getEl : function(){
30533         return this.el;
30534     },
30535
30536     /**
30537      * Hides this region.
30538      */
30539     hide : function(){
30540         if(!this.collapsed){
30541             this.el.dom.style.left = "-2000px";
30542             this.el.hide();
30543         }else{
30544             this.collapsedEl.dom.style.left = "-2000px";
30545             this.collapsedEl.hide();
30546         }
30547         this.visible = false;
30548         this.fireEvent("visibilitychange", this, false);
30549     },
30550
30551     /**
30552      * Shows this region if it was previously hidden.
30553      */
30554     show : function(){
30555         if(!this.collapsed){
30556             this.el.show();
30557         }else{
30558             this.collapsedEl.show();
30559         }
30560         this.visible = true;
30561         this.fireEvent("visibilitychange", this, true);
30562     },
30563
30564     closeClicked : function(){
30565         if(this.activePanel){
30566             this.remove(this.activePanel);
30567         }
30568     },
30569
30570     collapseClick : function(e){
30571         if(this.isSlid){
30572            e.stopPropagation();
30573            this.slideIn();
30574         }else{
30575            e.stopPropagation();
30576            this.slideOut();
30577         }
30578     },
30579
30580     /**
30581      * Collapses this region.
30582      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30583      */
30584     collapse : function(skipAnim, skipCheck){
30585         if(this.collapsed) {
30586             return;
30587         }
30588         
30589         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30590             
30591             this.collapsed = true;
30592             if(this.split){
30593                 this.split.el.hide();
30594             }
30595             if(this.config.animate && skipAnim !== true){
30596                 this.fireEvent("invalidated", this);
30597                 this.animateCollapse();
30598             }else{
30599                 this.el.setLocation(-20000,-20000);
30600                 this.el.hide();
30601                 this.collapsedEl.show();
30602                 this.fireEvent("collapsed", this);
30603                 this.fireEvent("invalidated", this);
30604             }
30605         }
30606         
30607     },
30608
30609     animateCollapse : function(){
30610         // overridden
30611     },
30612
30613     /**
30614      * Expands this region if it was previously collapsed.
30615      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30616      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30617      */
30618     expand : function(e, skipAnim){
30619         if(e) {
30620             e.stopPropagation();
30621         }
30622         if(!this.collapsed || this.el.hasActiveFx()) {
30623             return;
30624         }
30625         if(this.isSlid){
30626             this.afterSlideIn();
30627             skipAnim = true;
30628         }
30629         this.collapsed = false;
30630         if(this.config.animate && skipAnim !== true){
30631             this.animateExpand();
30632         }else{
30633             this.el.show();
30634             if(this.split){
30635                 this.split.el.show();
30636             }
30637             this.collapsedEl.setLocation(-2000,-2000);
30638             this.collapsedEl.hide();
30639             this.fireEvent("invalidated", this);
30640             this.fireEvent("expanded", this);
30641         }
30642     },
30643
30644     animateExpand : function(){
30645         // overridden
30646     },
30647
30648     initTabs : function()
30649     {
30650         this.bodyEl.setStyle("overflow", "hidden");
30651         var ts = new Roo.TabPanel(
30652                 this.bodyEl.dom,
30653                 {
30654                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30655                     disableTooltips: this.config.disableTabTips,
30656                     toolbar : this.config.toolbar
30657                 }
30658         );
30659         if(this.config.hideTabs){
30660             ts.stripWrap.setDisplayed(false);
30661         }
30662         this.tabs = ts;
30663         ts.resizeTabs = this.config.resizeTabs === true;
30664         ts.minTabWidth = this.config.minTabWidth || 40;
30665         ts.maxTabWidth = this.config.maxTabWidth || 250;
30666         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30667         ts.monitorResize = false;
30668         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30669         ts.bodyEl.addClass('x-layout-tabs-body');
30670         this.panels.each(this.initPanelAsTab, this);
30671     },
30672
30673     initPanelAsTab : function(panel){
30674         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30675                     this.config.closeOnTab && panel.isClosable());
30676         if(panel.tabTip !== undefined){
30677             ti.setTooltip(panel.tabTip);
30678         }
30679         ti.on("activate", function(){
30680               this.setActivePanel(panel);
30681         }, this);
30682         if(this.config.closeOnTab){
30683             ti.on("beforeclose", function(t, e){
30684                 e.cancel = true;
30685                 this.remove(panel);
30686             }, this);
30687         }
30688         return ti;
30689     },
30690
30691     updatePanelTitle : function(panel, title){
30692         if(this.activePanel == panel){
30693             this.updateTitle(title);
30694         }
30695         if(this.tabs){
30696             var ti = this.tabs.getTab(panel.getEl().id);
30697             ti.setText(title);
30698             if(panel.tabTip !== undefined){
30699                 ti.setTooltip(panel.tabTip);
30700             }
30701         }
30702     },
30703
30704     updateTitle : function(title){
30705         if(this.titleTextEl && !this.config.title){
30706             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30707         }
30708     },
30709
30710     setActivePanel : function(panel){
30711         panel = this.getPanel(panel);
30712         if(this.activePanel && this.activePanel != panel){
30713             this.activePanel.setActiveState(false);
30714         }
30715         this.activePanel = panel;
30716         panel.setActiveState(true);
30717         if(this.panelSize){
30718             panel.setSize(this.panelSize.width, this.panelSize.height);
30719         }
30720         if(this.closeBtn){
30721             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30722         }
30723         this.updateTitle(panel.getTitle());
30724         if(this.tabs){
30725             this.fireEvent("invalidated", this);
30726         }
30727         this.fireEvent("panelactivated", this, panel);
30728     },
30729
30730     /**
30731      * Shows the specified panel.
30732      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30733      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30734      */
30735     showPanel : function(panel)
30736     {
30737         panel = this.getPanel(panel);
30738         if(panel){
30739             if(this.tabs){
30740                 var tab = this.tabs.getTab(panel.getEl().id);
30741                 if(tab.isHidden()){
30742                     this.tabs.unhideTab(tab.id);
30743                 }
30744                 tab.activate();
30745             }else{
30746                 this.setActivePanel(panel);
30747             }
30748         }
30749         return panel;
30750     },
30751
30752     /**
30753      * Get the active panel for this region.
30754      * @return {Roo.ContentPanel} The active panel or null
30755      */
30756     getActivePanel : function(){
30757         return this.activePanel;
30758     },
30759
30760     validateVisibility : function(){
30761         if(this.panels.getCount() < 1){
30762             this.updateTitle("&#160;");
30763             this.closeBtn.hide();
30764             this.hide();
30765         }else{
30766             if(!this.isVisible()){
30767                 this.show();
30768             }
30769         }
30770     },
30771
30772     /**
30773      * Adds the passed ContentPanel(s) to this region.
30774      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30775      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30776      */
30777     add : function(panel){
30778         if(arguments.length > 1){
30779             for(var i = 0, len = arguments.length; i < len; i++) {
30780                 this.add(arguments[i]);
30781             }
30782             return null;
30783         }
30784         if(this.hasPanel(panel)){
30785             this.showPanel(panel);
30786             return panel;
30787         }
30788         panel.setRegion(this);
30789         this.panels.add(panel);
30790         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30791             this.bodyEl.dom.appendChild(panel.getEl().dom);
30792             if(panel.background !== true){
30793                 this.setActivePanel(panel);
30794             }
30795             this.fireEvent("paneladded", this, panel);
30796             return panel;
30797         }
30798         if(!this.tabs){
30799             this.initTabs();
30800         }else{
30801             this.initPanelAsTab(panel);
30802         }
30803         if(panel.background !== true){
30804             this.tabs.activate(panel.getEl().id);
30805         }
30806         this.fireEvent("paneladded", this, panel);
30807         return panel;
30808     },
30809
30810     /**
30811      * Hides the tab for the specified panel.
30812      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30813      */
30814     hidePanel : function(panel){
30815         if(this.tabs && (panel = this.getPanel(panel))){
30816             this.tabs.hideTab(panel.getEl().id);
30817         }
30818     },
30819
30820     /**
30821      * Unhides the tab for a previously hidden panel.
30822      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30823      */
30824     unhidePanel : function(panel){
30825         if(this.tabs && (panel = this.getPanel(panel))){
30826             this.tabs.unhideTab(panel.getEl().id);
30827         }
30828     },
30829
30830     clearPanels : function(){
30831         while(this.panels.getCount() > 0){
30832              this.remove(this.panels.first());
30833         }
30834     },
30835
30836     /**
30837      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30838      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30839      * @param {Boolean} preservePanel Overrides the config preservePanel option
30840      * @return {Roo.ContentPanel} The panel that was removed
30841      */
30842     remove : function(panel, preservePanel){
30843         panel = this.getPanel(panel);
30844         if(!panel){
30845             return null;
30846         }
30847         var e = {};
30848         this.fireEvent("beforeremove", this, panel, e);
30849         if(e.cancel === true){
30850             return null;
30851         }
30852         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30853         var panelId = panel.getId();
30854         this.panels.removeKey(panelId);
30855         if(preservePanel){
30856             document.body.appendChild(panel.getEl().dom);
30857         }
30858         if(this.tabs){
30859             this.tabs.removeTab(panel.getEl().id);
30860         }else if (!preservePanel){
30861             this.bodyEl.dom.removeChild(panel.getEl().dom);
30862         }
30863         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30864             var p = this.panels.first();
30865             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30866             tempEl.appendChild(p.getEl().dom);
30867             this.bodyEl.update("");
30868             this.bodyEl.dom.appendChild(p.getEl().dom);
30869             tempEl = null;
30870             this.updateTitle(p.getTitle());
30871             this.tabs = null;
30872             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30873             this.setActivePanel(p);
30874         }
30875         panel.setRegion(null);
30876         if(this.activePanel == panel){
30877             this.activePanel = null;
30878         }
30879         if(this.config.autoDestroy !== false && preservePanel !== true){
30880             try{panel.destroy();}catch(e){}
30881         }
30882         this.fireEvent("panelremoved", this, panel);
30883         return panel;
30884     },
30885
30886     /**
30887      * Returns the TabPanel component used by this region
30888      * @return {Roo.TabPanel}
30889      */
30890     getTabs : function(){
30891         return this.tabs;
30892     },
30893
30894     createTool : function(parentEl, className){
30895         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30896             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30897         btn.addClassOnOver("x-layout-tools-button-over");
30898         return btn;
30899     }
30900 });/*
30901  * Based on:
30902  * Ext JS Library 1.1.1
30903  * Copyright(c) 2006-2007, Ext JS, LLC.
30904  *
30905  * Originally Released Under LGPL - original licence link has changed is not relivant.
30906  *
30907  * Fork - LGPL
30908  * <script type="text/javascript">
30909  */
30910  
30911
30912
30913 /**
30914  * @class Roo.SplitLayoutRegion
30915  * @extends Roo.LayoutRegion
30916  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30917  */
30918 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30919     this.cursor = cursor;
30920     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30921 };
30922
30923 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30924     splitTip : "Drag to resize.",
30925     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30926     useSplitTips : false,
30927
30928     applyConfig : function(config){
30929         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30930         if(config.split){
30931             if(!this.split){
30932                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30933                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30934                 /** The SplitBar for this region 
30935                 * @type Roo.SplitBar */
30936                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30937                 this.split.on("moved", this.onSplitMove, this);
30938                 this.split.useShim = config.useShim === true;
30939                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30940                 if(this.useSplitTips){
30941                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30942                 }
30943                 if(config.collapsible){
30944                     this.split.el.on("dblclick", this.collapse,  this);
30945                 }
30946             }
30947             if(typeof config.minSize != "undefined"){
30948                 this.split.minSize = config.minSize;
30949             }
30950             if(typeof config.maxSize != "undefined"){
30951                 this.split.maxSize = config.maxSize;
30952             }
30953             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30954                 this.hideSplitter();
30955             }
30956         }
30957     },
30958
30959     getHMaxSize : function(){
30960          var cmax = this.config.maxSize || 10000;
30961          var center = this.mgr.getRegion("center");
30962          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30963     },
30964
30965     getVMaxSize : function(){
30966          var cmax = this.config.maxSize || 10000;
30967          var center = this.mgr.getRegion("center");
30968          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30969     },
30970
30971     onSplitMove : function(split, newSize){
30972         this.fireEvent("resized", this, newSize);
30973     },
30974     
30975     /** 
30976      * Returns the {@link Roo.SplitBar} for this region.
30977      * @return {Roo.SplitBar}
30978      */
30979     getSplitBar : function(){
30980         return this.split;
30981     },
30982     
30983     hide : function(){
30984         this.hideSplitter();
30985         Roo.SplitLayoutRegion.superclass.hide.call(this);
30986     },
30987
30988     hideSplitter : function(){
30989         if(this.split){
30990             this.split.el.setLocation(-2000,-2000);
30991             this.split.el.hide();
30992         }
30993     },
30994
30995     show : function(){
30996         if(this.split){
30997             this.split.el.show();
30998         }
30999         Roo.SplitLayoutRegion.superclass.show.call(this);
31000     },
31001     
31002     beforeSlide: function(){
31003         if(Roo.isGecko){// firefox overflow auto bug workaround
31004             this.bodyEl.clip();
31005             if(this.tabs) {
31006                 this.tabs.bodyEl.clip();
31007             }
31008             if(this.activePanel){
31009                 this.activePanel.getEl().clip();
31010                 
31011                 if(this.activePanel.beforeSlide){
31012                     this.activePanel.beforeSlide();
31013                 }
31014             }
31015         }
31016     },
31017     
31018     afterSlide : function(){
31019         if(Roo.isGecko){// firefox overflow auto bug workaround
31020             this.bodyEl.unclip();
31021             if(this.tabs) {
31022                 this.tabs.bodyEl.unclip();
31023             }
31024             if(this.activePanel){
31025                 this.activePanel.getEl().unclip();
31026                 if(this.activePanel.afterSlide){
31027                     this.activePanel.afterSlide();
31028                 }
31029             }
31030         }
31031     },
31032
31033     initAutoHide : function(){
31034         if(this.autoHide !== false){
31035             if(!this.autoHideHd){
31036                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31037                 this.autoHideHd = {
31038                     "mouseout": function(e){
31039                         if(!e.within(this.el, true)){
31040                             st.delay(500);
31041                         }
31042                     },
31043                     "mouseover" : function(e){
31044                         st.cancel();
31045                     },
31046                     scope : this
31047                 };
31048             }
31049             this.el.on(this.autoHideHd);
31050         }
31051     },
31052
31053     clearAutoHide : function(){
31054         if(this.autoHide !== false){
31055             this.el.un("mouseout", this.autoHideHd.mouseout);
31056             this.el.un("mouseover", this.autoHideHd.mouseover);
31057         }
31058     },
31059
31060     clearMonitor : function(){
31061         Roo.get(document).un("click", this.slideInIf, this);
31062     },
31063
31064     // these names are backwards but not changed for compat
31065     slideOut : function(){
31066         if(this.isSlid || this.el.hasActiveFx()){
31067             return;
31068         }
31069         this.isSlid = true;
31070         if(this.collapseBtn){
31071             this.collapseBtn.hide();
31072         }
31073         this.closeBtnState = this.closeBtn.getStyle('display');
31074         this.closeBtn.hide();
31075         if(this.stickBtn){
31076             this.stickBtn.show();
31077         }
31078         this.el.show();
31079         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31080         this.beforeSlide();
31081         this.el.setStyle("z-index", 10001);
31082         this.el.slideIn(this.getSlideAnchor(), {
31083             callback: function(){
31084                 this.afterSlide();
31085                 this.initAutoHide();
31086                 Roo.get(document).on("click", this.slideInIf, this);
31087                 this.fireEvent("slideshow", this);
31088             },
31089             scope: this,
31090             block: true
31091         });
31092     },
31093
31094     afterSlideIn : function(){
31095         this.clearAutoHide();
31096         this.isSlid = false;
31097         this.clearMonitor();
31098         this.el.setStyle("z-index", "");
31099         if(this.collapseBtn){
31100             this.collapseBtn.show();
31101         }
31102         this.closeBtn.setStyle('display', this.closeBtnState);
31103         if(this.stickBtn){
31104             this.stickBtn.hide();
31105         }
31106         this.fireEvent("slidehide", this);
31107     },
31108
31109     slideIn : function(cb){
31110         if(!this.isSlid || this.el.hasActiveFx()){
31111             Roo.callback(cb);
31112             return;
31113         }
31114         this.isSlid = false;
31115         this.beforeSlide();
31116         this.el.slideOut(this.getSlideAnchor(), {
31117             callback: function(){
31118                 this.el.setLeftTop(-10000, -10000);
31119                 this.afterSlide();
31120                 this.afterSlideIn();
31121                 Roo.callback(cb);
31122             },
31123             scope: this,
31124             block: true
31125         });
31126     },
31127     
31128     slideInIf : function(e){
31129         if(!e.within(this.el)){
31130             this.slideIn();
31131         }
31132     },
31133
31134     animateCollapse : function(){
31135         this.beforeSlide();
31136         this.el.setStyle("z-index", 20000);
31137         var anchor = this.getSlideAnchor();
31138         this.el.slideOut(anchor, {
31139             callback : function(){
31140                 this.el.setStyle("z-index", "");
31141                 this.collapsedEl.slideIn(anchor, {duration:.3});
31142                 this.afterSlide();
31143                 this.el.setLocation(-10000,-10000);
31144                 this.el.hide();
31145                 this.fireEvent("collapsed", this);
31146             },
31147             scope: this,
31148             block: true
31149         });
31150     },
31151
31152     animateExpand : function(){
31153         this.beforeSlide();
31154         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31155         this.el.setStyle("z-index", 20000);
31156         this.collapsedEl.hide({
31157             duration:.1
31158         });
31159         this.el.slideIn(this.getSlideAnchor(), {
31160             callback : function(){
31161                 this.el.setStyle("z-index", "");
31162                 this.afterSlide();
31163                 if(this.split){
31164                     this.split.el.show();
31165                 }
31166                 this.fireEvent("invalidated", this);
31167                 this.fireEvent("expanded", this);
31168             },
31169             scope: this,
31170             block: true
31171         });
31172     },
31173
31174     anchors : {
31175         "west" : "left",
31176         "east" : "right",
31177         "north" : "top",
31178         "south" : "bottom"
31179     },
31180
31181     sanchors : {
31182         "west" : "l",
31183         "east" : "r",
31184         "north" : "t",
31185         "south" : "b"
31186     },
31187
31188     canchors : {
31189         "west" : "tl-tr",
31190         "east" : "tr-tl",
31191         "north" : "tl-bl",
31192         "south" : "bl-tl"
31193     },
31194
31195     getAnchor : function(){
31196         return this.anchors[this.position];
31197     },
31198
31199     getCollapseAnchor : function(){
31200         return this.canchors[this.position];
31201     },
31202
31203     getSlideAnchor : function(){
31204         return this.sanchors[this.position];
31205     },
31206
31207     getAlignAdj : function(){
31208         var cm = this.cmargins;
31209         switch(this.position){
31210             case "west":
31211                 return [0, 0];
31212             break;
31213             case "east":
31214                 return [0, 0];
31215             break;
31216             case "north":
31217                 return [0, 0];
31218             break;
31219             case "south":
31220                 return [0, 0];
31221             break;
31222         }
31223     },
31224
31225     getExpandAdj : function(){
31226         var c = this.collapsedEl, cm = this.cmargins;
31227         switch(this.position){
31228             case "west":
31229                 return [-(cm.right+c.getWidth()+cm.left), 0];
31230             break;
31231             case "east":
31232                 return [cm.right+c.getWidth()+cm.left, 0];
31233             break;
31234             case "north":
31235                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31236             break;
31237             case "south":
31238                 return [0, cm.top+cm.bottom+c.getHeight()];
31239             break;
31240         }
31241     }
31242 });/*
31243  * Based on:
31244  * Ext JS Library 1.1.1
31245  * Copyright(c) 2006-2007, Ext JS, LLC.
31246  *
31247  * Originally Released Under LGPL - original licence link has changed is not relivant.
31248  *
31249  * Fork - LGPL
31250  * <script type="text/javascript">
31251  */
31252 /*
31253  * These classes are private internal classes
31254  */
31255 Roo.CenterLayoutRegion = function(mgr, config){
31256     Roo.LayoutRegion.call(this, mgr, config, "center");
31257     this.visible = true;
31258     this.minWidth = config.minWidth || 20;
31259     this.minHeight = config.minHeight || 20;
31260 };
31261
31262 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31263     hide : function(){
31264         // center panel can't be hidden
31265     },
31266     
31267     show : function(){
31268         // center panel can't be hidden
31269     },
31270     
31271     getMinWidth: function(){
31272         return this.minWidth;
31273     },
31274     
31275     getMinHeight: function(){
31276         return this.minHeight;
31277     }
31278 });
31279
31280
31281 Roo.NorthLayoutRegion = function(mgr, config){
31282     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31283     if(this.split){
31284         this.split.placement = Roo.SplitBar.TOP;
31285         this.split.orientation = Roo.SplitBar.VERTICAL;
31286         this.split.el.addClass("x-layout-split-v");
31287     }
31288     var size = config.initialSize || config.height;
31289     if(typeof size != "undefined"){
31290         this.el.setHeight(size);
31291     }
31292 };
31293 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31294     orientation: Roo.SplitBar.VERTICAL,
31295     getBox : function(){
31296         if(this.collapsed){
31297             return this.collapsedEl.getBox();
31298         }
31299         var box = this.el.getBox();
31300         if(this.split){
31301             box.height += this.split.el.getHeight();
31302         }
31303         return box;
31304     },
31305     
31306     updateBox : function(box){
31307         if(this.split && !this.collapsed){
31308             box.height -= this.split.el.getHeight();
31309             this.split.el.setLeft(box.x);
31310             this.split.el.setTop(box.y+box.height);
31311             this.split.el.setWidth(box.width);
31312         }
31313         if(this.collapsed){
31314             this.updateBody(box.width, null);
31315         }
31316         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31317     }
31318 });
31319
31320 Roo.SouthLayoutRegion = function(mgr, config){
31321     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31322     if(this.split){
31323         this.split.placement = Roo.SplitBar.BOTTOM;
31324         this.split.orientation = Roo.SplitBar.VERTICAL;
31325         this.split.el.addClass("x-layout-split-v");
31326     }
31327     var size = config.initialSize || config.height;
31328     if(typeof size != "undefined"){
31329         this.el.setHeight(size);
31330     }
31331 };
31332 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31333     orientation: Roo.SplitBar.VERTICAL,
31334     getBox : function(){
31335         if(this.collapsed){
31336             return this.collapsedEl.getBox();
31337         }
31338         var box = this.el.getBox();
31339         if(this.split){
31340             var sh = this.split.el.getHeight();
31341             box.height += sh;
31342             box.y -= sh;
31343         }
31344         return box;
31345     },
31346     
31347     updateBox : function(box){
31348         if(this.split && !this.collapsed){
31349             var sh = this.split.el.getHeight();
31350             box.height -= sh;
31351             box.y += sh;
31352             this.split.el.setLeft(box.x);
31353             this.split.el.setTop(box.y-sh);
31354             this.split.el.setWidth(box.width);
31355         }
31356         if(this.collapsed){
31357             this.updateBody(box.width, null);
31358         }
31359         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31360     }
31361 });
31362
31363 Roo.EastLayoutRegion = function(mgr, config){
31364     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31365     if(this.split){
31366         this.split.placement = Roo.SplitBar.RIGHT;
31367         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31368         this.split.el.addClass("x-layout-split-h");
31369     }
31370     var size = config.initialSize || config.width;
31371     if(typeof size != "undefined"){
31372         this.el.setWidth(size);
31373     }
31374 };
31375 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31376     orientation: Roo.SplitBar.HORIZONTAL,
31377     getBox : function(){
31378         if(this.collapsed){
31379             return this.collapsedEl.getBox();
31380         }
31381         var box = this.el.getBox();
31382         if(this.split){
31383             var sw = this.split.el.getWidth();
31384             box.width += sw;
31385             box.x -= sw;
31386         }
31387         return box;
31388     },
31389
31390     updateBox : function(box){
31391         if(this.split && !this.collapsed){
31392             var sw = this.split.el.getWidth();
31393             box.width -= sw;
31394             this.split.el.setLeft(box.x);
31395             this.split.el.setTop(box.y);
31396             this.split.el.setHeight(box.height);
31397             box.x += sw;
31398         }
31399         if(this.collapsed){
31400             this.updateBody(null, box.height);
31401         }
31402         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31403     }
31404 });
31405
31406 Roo.WestLayoutRegion = function(mgr, config){
31407     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31408     if(this.split){
31409         this.split.placement = Roo.SplitBar.LEFT;
31410         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31411         this.split.el.addClass("x-layout-split-h");
31412     }
31413     var size = config.initialSize || config.width;
31414     if(typeof size != "undefined"){
31415         this.el.setWidth(size);
31416     }
31417 };
31418 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31419     orientation: Roo.SplitBar.HORIZONTAL,
31420     getBox : function(){
31421         if(this.collapsed){
31422             return this.collapsedEl.getBox();
31423         }
31424         var box = this.el.getBox();
31425         if(this.split){
31426             box.width += this.split.el.getWidth();
31427         }
31428         return box;
31429     },
31430     
31431     updateBox : function(box){
31432         if(this.split && !this.collapsed){
31433             var sw = this.split.el.getWidth();
31434             box.width -= sw;
31435             this.split.el.setLeft(box.x+box.width);
31436             this.split.el.setTop(box.y);
31437             this.split.el.setHeight(box.height);
31438         }
31439         if(this.collapsed){
31440             this.updateBody(null, box.height);
31441         }
31442         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31443     }
31444 });
31445 /*
31446  * Based on:
31447  * Ext JS Library 1.1.1
31448  * Copyright(c) 2006-2007, Ext JS, LLC.
31449  *
31450  * Originally Released Under LGPL - original licence link has changed is not relivant.
31451  *
31452  * Fork - LGPL
31453  * <script type="text/javascript">
31454  */
31455  
31456  
31457 /*
31458  * Private internal class for reading and applying state
31459  */
31460 Roo.LayoutStateManager = function(layout){
31461      // default empty state
31462      this.state = {
31463         north: {},
31464         south: {},
31465         east: {},
31466         west: {}       
31467     };
31468 };
31469
31470 Roo.LayoutStateManager.prototype = {
31471     init : function(layout, provider){
31472         this.provider = provider;
31473         var state = provider.get(layout.id+"-layout-state");
31474         if(state){
31475             var wasUpdating = layout.isUpdating();
31476             if(!wasUpdating){
31477                 layout.beginUpdate();
31478             }
31479             for(var key in state){
31480                 if(typeof state[key] != "function"){
31481                     var rstate = state[key];
31482                     var r = layout.getRegion(key);
31483                     if(r && rstate){
31484                         if(rstate.size){
31485                             r.resizeTo(rstate.size);
31486                         }
31487                         if(rstate.collapsed == true){
31488                             r.collapse(true);
31489                         }else{
31490                             r.expand(null, true);
31491                         }
31492                     }
31493                 }
31494             }
31495             if(!wasUpdating){
31496                 layout.endUpdate();
31497             }
31498             this.state = state; 
31499         }
31500         this.layout = layout;
31501         layout.on("regionresized", this.onRegionResized, this);
31502         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31503         layout.on("regionexpanded", this.onRegionExpanded, this);
31504     },
31505     
31506     storeState : function(){
31507         this.provider.set(this.layout.id+"-layout-state", this.state);
31508     },
31509     
31510     onRegionResized : function(region, newSize){
31511         this.state[region.getPosition()].size = newSize;
31512         this.storeState();
31513     },
31514     
31515     onRegionCollapsed : function(region){
31516         this.state[region.getPosition()].collapsed = true;
31517         this.storeState();
31518     },
31519     
31520     onRegionExpanded : function(region){
31521         this.state[region.getPosition()].collapsed = false;
31522         this.storeState();
31523     }
31524 };/*
31525  * Based on:
31526  * Ext JS Library 1.1.1
31527  * Copyright(c) 2006-2007, Ext JS, LLC.
31528  *
31529  * Originally Released Under LGPL - original licence link has changed is not relivant.
31530  *
31531  * Fork - LGPL
31532  * <script type="text/javascript">
31533  */
31534 /**
31535  * @class Roo.ContentPanel
31536  * @extends Roo.util.Observable
31537  * A basic ContentPanel element.
31538  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31539  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31540  * @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
31541  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31542  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31543  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31544  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31545  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31546  * @cfg {String} title          The title for this panel
31547  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31548  * @cfg {String} url            Calls {@link #setUrl} with this value
31549  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31550  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31551  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31552  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31553
31554  * @constructor
31555  * Create a new ContentPanel.
31556  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31557  * @param {String/Object} config A string to set only the title or a config object
31558  * @param {String} content (optional) Set the HTML content for this panel
31559  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31560  */
31561 Roo.ContentPanel = function(el, config, content){
31562     
31563      
31564     /*
31565     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31566         config = el;
31567         el = Roo.id();
31568     }
31569     if (config && config.parentLayout) { 
31570         el = config.parentLayout.el.createChild(); 
31571     }
31572     */
31573     if(el.autoCreate){ // xtype is available if this is called from factory
31574         config = el;
31575         el = Roo.id();
31576     }
31577     this.el = Roo.get(el);
31578     if(!this.el && config && config.autoCreate){
31579         if(typeof config.autoCreate == "object"){
31580             if(!config.autoCreate.id){
31581                 config.autoCreate.id = config.id||el;
31582             }
31583             this.el = Roo.DomHelper.append(document.body,
31584                         config.autoCreate, true);
31585         }else{
31586             this.el = Roo.DomHelper.append(document.body,
31587                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31588         }
31589     }
31590     this.closable = false;
31591     this.loaded = false;
31592     this.active = false;
31593     if(typeof config == "string"){
31594         this.title = config;
31595     }else{
31596         Roo.apply(this, config);
31597     }
31598     
31599     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31600         this.wrapEl = this.el.wrap();
31601         this.toolbar.container = this.el.insertSibling(false, 'before');
31602         this.toolbar = new Roo.Toolbar(this.toolbar);
31603     }
31604     
31605     // xtype created footer. - not sure if will work as we normally have to render first..
31606     if (this.footer && !this.footer.el && this.footer.xtype) {
31607         if (!this.wrapEl) {
31608             this.wrapEl = this.el.wrap();
31609         }
31610     
31611         this.footer.container = this.wrapEl.createChild();
31612          
31613         this.footer = Roo.factory(this.footer, Roo);
31614         
31615     }
31616     
31617     if(this.resizeEl){
31618         this.resizeEl = Roo.get(this.resizeEl, true);
31619     }else{
31620         this.resizeEl = this.el;
31621     }
31622     // handle view.xtype
31623     
31624  
31625     
31626     
31627     this.addEvents({
31628         /**
31629          * @event activate
31630          * Fires when this panel is activated. 
31631          * @param {Roo.ContentPanel} this
31632          */
31633         "activate" : true,
31634         /**
31635          * @event deactivate
31636          * Fires when this panel is activated. 
31637          * @param {Roo.ContentPanel} this
31638          */
31639         "deactivate" : true,
31640
31641         /**
31642          * @event resize
31643          * Fires when this panel is resized if fitToFrame is true.
31644          * @param {Roo.ContentPanel} this
31645          * @param {Number} width The width after any component adjustments
31646          * @param {Number} height The height after any component adjustments
31647          */
31648         "resize" : true,
31649         
31650          /**
31651          * @event render
31652          * Fires when this tab is created
31653          * @param {Roo.ContentPanel} this
31654          */
31655         "render" : true
31656          
31657         
31658     });
31659     
31660
31661     
31662     
31663     if(this.autoScroll){
31664         this.resizeEl.setStyle("overflow", "auto");
31665     } else {
31666         // fix randome scrolling
31667         this.el.on('scroll', function() {
31668             Roo.log('fix random scolling');
31669             this.scrollTo('top',0); 
31670         });
31671     }
31672     content = content || this.content;
31673     if(content){
31674         this.setContent(content);
31675     }
31676     if(config && config.url){
31677         this.setUrl(this.url, this.params, this.loadOnce);
31678     }
31679     
31680     
31681     
31682     Roo.ContentPanel.superclass.constructor.call(this);
31683     
31684     if (this.view && typeof(this.view.xtype) != 'undefined') {
31685         this.view.el = this.el.appendChild(document.createElement("div"));
31686         this.view = Roo.factory(this.view); 
31687         this.view.render  &&  this.view.render(false, '');  
31688     }
31689     
31690     
31691     this.fireEvent('render', this);
31692 };
31693
31694 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31695     tabTip:'',
31696     setRegion : function(region){
31697         this.region = region;
31698         if(region){
31699            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31700         }else{
31701            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31702         } 
31703     },
31704     
31705     /**
31706      * Returns the toolbar for this Panel if one was configured. 
31707      * @return {Roo.Toolbar} 
31708      */
31709     getToolbar : function(){
31710         return this.toolbar;
31711     },
31712     
31713     setActiveState : function(active){
31714         this.active = active;
31715         if(!active){
31716             this.fireEvent("deactivate", this);
31717         }else{
31718             this.fireEvent("activate", this);
31719         }
31720     },
31721     /**
31722      * Updates this panel's element
31723      * @param {String} content The new content
31724      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31725     */
31726     setContent : function(content, loadScripts){
31727         this.el.update(content, loadScripts);
31728     },
31729
31730     ignoreResize : function(w, h){
31731         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31732             return true;
31733         }else{
31734             this.lastSize = {width: w, height: h};
31735             return false;
31736         }
31737     },
31738     /**
31739      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31740      * @return {Roo.UpdateManager} The UpdateManager
31741      */
31742     getUpdateManager : function(){
31743         return this.el.getUpdateManager();
31744     },
31745      /**
31746      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31747      * @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:
31748 <pre><code>
31749 panel.load({
31750     url: "your-url.php",
31751     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31752     callback: yourFunction,
31753     scope: yourObject, //(optional scope)
31754     discardUrl: false,
31755     nocache: false,
31756     text: "Loading...",
31757     timeout: 30,
31758     scripts: false
31759 });
31760 </code></pre>
31761      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31762      * 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.
31763      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
31764      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31765      * @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.
31766      * @return {Roo.ContentPanel} this
31767      */
31768     load : function(){
31769         var um = this.el.getUpdateManager();
31770         um.update.apply(um, arguments);
31771         return this;
31772     },
31773
31774
31775     /**
31776      * 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.
31777      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31778      * @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)
31779      * @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)
31780      * @return {Roo.UpdateManager} The UpdateManager
31781      */
31782     setUrl : function(url, params, loadOnce){
31783         if(this.refreshDelegate){
31784             this.removeListener("activate", this.refreshDelegate);
31785         }
31786         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31787         this.on("activate", this.refreshDelegate);
31788         return this.el.getUpdateManager();
31789     },
31790     
31791     _handleRefresh : function(url, params, loadOnce){
31792         if(!loadOnce || !this.loaded){
31793             var updater = this.el.getUpdateManager();
31794             updater.update(url, params, this._setLoaded.createDelegate(this));
31795         }
31796     },
31797     
31798     _setLoaded : function(){
31799         this.loaded = true;
31800     }, 
31801     
31802     /**
31803      * Returns this panel's id
31804      * @return {String} 
31805      */
31806     getId : function(){
31807         return this.el.id;
31808     },
31809     
31810     /** 
31811      * Returns this panel's element - used by regiosn to add.
31812      * @return {Roo.Element} 
31813      */
31814     getEl : function(){
31815         return this.wrapEl || this.el;
31816     },
31817     
31818     adjustForComponents : function(width, height)
31819     {
31820         //Roo.log('adjustForComponents ');
31821         if(this.resizeEl != this.el){
31822             width -= this.el.getFrameWidth('lr');
31823             height -= this.el.getFrameWidth('tb');
31824         }
31825         if(this.toolbar){
31826             var te = this.toolbar.getEl();
31827             height -= te.getHeight();
31828             te.setWidth(width);
31829         }
31830         if(this.footer){
31831             var te = this.footer.getEl();
31832             //Roo.log("footer:" + te.getHeight());
31833             
31834             height -= te.getHeight();
31835             te.setWidth(width);
31836         }
31837         
31838         
31839         if(this.adjustments){
31840             width += this.adjustments[0];
31841             height += this.adjustments[1];
31842         }
31843         return {"width": width, "height": height};
31844     },
31845     
31846     setSize : function(width, height){
31847         if(this.fitToFrame && !this.ignoreResize(width, height)){
31848             if(this.fitContainer && this.resizeEl != this.el){
31849                 this.el.setSize(width, height);
31850             }
31851             var size = this.adjustForComponents(width, height);
31852             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31853             this.fireEvent('resize', this, size.width, size.height);
31854         }
31855     },
31856     
31857     /**
31858      * Returns this panel's title
31859      * @return {String} 
31860      */
31861     getTitle : function(){
31862         return this.title;
31863     },
31864     
31865     /**
31866      * Set this panel's title
31867      * @param {String} title
31868      */
31869     setTitle : function(title){
31870         this.title = title;
31871         if(this.region){
31872             this.region.updatePanelTitle(this, title);
31873         }
31874     },
31875     
31876     /**
31877      * Returns true is this panel was configured to be closable
31878      * @return {Boolean} 
31879      */
31880     isClosable : function(){
31881         return this.closable;
31882     },
31883     
31884     beforeSlide : function(){
31885         this.el.clip();
31886         this.resizeEl.clip();
31887     },
31888     
31889     afterSlide : function(){
31890         this.el.unclip();
31891         this.resizeEl.unclip();
31892     },
31893     
31894     /**
31895      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31896      *   Will fail silently if the {@link #setUrl} method has not been called.
31897      *   This does not activate the panel, just updates its content.
31898      */
31899     refresh : function(){
31900         if(this.refreshDelegate){
31901            this.loaded = false;
31902            this.refreshDelegate();
31903         }
31904     },
31905     
31906     /**
31907      * Destroys this panel
31908      */
31909     destroy : function(){
31910         this.el.removeAllListeners();
31911         var tempEl = document.createElement("span");
31912         tempEl.appendChild(this.el.dom);
31913         tempEl.innerHTML = "";
31914         this.el.remove();
31915         this.el = null;
31916     },
31917     
31918     /**
31919      * form - if the content panel contains a form - this is a reference to it.
31920      * @type {Roo.form.Form}
31921      */
31922     form : false,
31923     /**
31924      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31925      *    This contains a reference to it.
31926      * @type {Roo.View}
31927      */
31928     view : false,
31929     
31930       /**
31931      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31932      * <pre><code>
31933
31934 layout.addxtype({
31935        xtype : 'Form',
31936        items: [ .... ]
31937    }
31938 );
31939
31940 </code></pre>
31941      * @param {Object} cfg Xtype definition of item to add.
31942      */
31943     
31944     addxtype : function(cfg) {
31945         // add form..
31946         if (cfg.xtype.match(/^Form$/)) {
31947             
31948             var el;
31949             //if (this.footer) {
31950             //    el = this.footer.container.insertSibling(false, 'before');
31951             //} else {
31952                 el = this.el.createChild();
31953             //}
31954
31955             this.form = new  Roo.form.Form(cfg);
31956             
31957             
31958             if ( this.form.allItems.length) {
31959                 this.form.render(el.dom);
31960             }
31961             return this.form;
31962         }
31963         // should only have one of theses..
31964         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31965             // views.. should not be just added - used named prop 'view''
31966             
31967             cfg.el = this.el.appendChild(document.createElement("div"));
31968             // factory?
31969             
31970             var ret = new Roo.factory(cfg);
31971              
31972              ret.render && ret.render(false, ''); // render blank..
31973             this.view = ret;
31974             return ret;
31975         }
31976         return false;
31977     }
31978 });
31979
31980 /**
31981  * @class Roo.GridPanel
31982  * @extends Roo.ContentPanel
31983  * @constructor
31984  * Create a new GridPanel.
31985  * @param {Roo.grid.Grid} grid The grid for this panel
31986  * @param {String/Object} config A string to set only the panel's title, or a config object
31987  */
31988 Roo.GridPanel = function(grid, config){
31989     
31990   
31991     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31992         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31993         
31994     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31995     
31996     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31997     
31998     if(this.toolbar){
31999         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32000     }
32001     // xtype created footer. - not sure if will work as we normally have to render first..
32002     if (this.footer && !this.footer.el && this.footer.xtype) {
32003         
32004         this.footer.container = this.grid.getView().getFooterPanel(true);
32005         this.footer.dataSource = this.grid.dataSource;
32006         this.footer = Roo.factory(this.footer, Roo);
32007         
32008     }
32009     
32010     grid.monitorWindowResize = false; // turn off autosizing
32011     grid.autoHeight = false;
32012     grid.autoWidth = false;
32013     this.grid = grid;
32014     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32015 };
32016
32017 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32018     getId : function(){
32019         return this.grid.id;
32020     },
32021     
32022     /**
32023      * Returns the grid for this panel
32024      * @return {Roo.grid.Grid} 
32025      */
32026     getGrid : function(){
32027         return this.grid;    
32028     },
32029     
32030     setSize : function(width, height){
32031         if(!this.ignoreResize(width, height)){
32032             var grid = this.grid;
32033             var size = this.adjustForComponents(width, height);
32034             grid.getGridEl().setSize(size.width, size.height);
32035             grid.autoSize();
32036         }
32037     },
32038     
32039     beforeSlide : function(){
32040         this.grid.getView().scroller.clip();
32041     },
32042     
32043     afterSlide : function(){
32044         this.grid.getView().scroller.unclip();
32045     },
32046     
32047     destroy : function(){
32048         this.grid.destroy();
32049         delete this.grid;
32050         Roo.GridPanel.superclass.destroy.call(this); 
32051     }
32052 });
32053
32054
32055 /**
32056  * @class Roo.NestedLayoutPanel
32057  * @extends Roo.ContentPanel
32058  * @constructor
32059  * Create a new NestedLayoutPanel.
32060  * 
32061  * 
32062  * @param {Roo.BorderLayout} layout The layout for this panel
32063  * @param {String/Object} config A string to set only the title or a config object
32064  */
32065 Roo.NestedLayoutPanel = function(layout, config)
32066 {
32067     // construct with only one argument..
32068     /* FIXME - implement nicer consturctors
32069     if (layout.layout) {
32070         config = layout;
32071         layout = config.layout;
32072         delete config.layout;
32073     }
32074     if (layout.xtype && !layout.getEl) {
32075         // then layout needs constructing..
32076         layout = Roo.factory(layout, Roo);
32077     }
32078     */
32079     
32080     
32081     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32082     
32083     layout.monitorWindowResize = false; // turn off autosizing
32084     this.layout = layout;
32085     this.layout.getEl().addClass("x-layout-nested-layout");
32086     
32087     
32088     
32089     
32090 };
32091
32092 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32093
32094     setSize : function(width, height){
32095         if(!this.ignoreResize(width, height)){
32096             var size = this.adjustForComponents(width, height);
32097             var el = this.layout.getEl();
32098             el.setSize(size.width, size.height);
32099             var touch = el.dom.offsetWidth;
32100             this.layout.layout();
32101             // ie requires a double layout on the first pass
32102             if(Roo.isIE && !this.initialized){
32103                 this.initialized = true;
32104                 this.layout.layout();
32105             }
32106         }
32107     },
32108     
32109     // activate all subpanels if not currently active..
32110     
32111     setActiveState : function(active){
32112         this.active = active;
32113         if(!active){
32114             this.fireEvent("deactivate", this);
32115             return;
32116         }
32117         
32118         this.fireEvent("activate", this);
32119         // not sure if this should happen before or after..
32120         if (!this.layout) {
32121             return; // should not happen..
32122         }
32123         var reg = false;
32124         for (var r in this.layout.regions) {
32125             reg = this.layout.getRegion(r);
32126             if (reg.getActivePanel()) {
32127                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32128                 reg.setActivePanel(reg.getActivePanel());
32129                 continue;
32130             }
32131             if (!reg.panels.length) {
32132                 continue;
32133             }
32134             reg.showPanel(reg.getPanel(0));
32135         }
32136         
32137         
32138         
32139         
32140     },
32141     
32142     /**
32143      * Returns the nested BorderLayout for this panel
32144      * @return {Roo.BorderLayout} 
32145      */
32146     getLayout : function(){
32147         return this.layout;
32148     },
32149     
32150      /**
32151      * Adds a xtype elements to the layout of the nested panel
32152      * <pre><code>
32153
32154 panel.addxtype({
32155        xtype : 'ContentPanel',
32156        region: 'west',
32157        items: [ .... ]
32158    }
32159 );
32160
32161 panel.addxtype({
32162         xtype : 'NestedLayoutPanel',
32163         region: 'west',
32164         layout: {
32165            center: { },
32166            west: { }   
32167         },
32168         items : [ ... list of content panels or nested layout panels.. ]
32169    }
32170 );
32171 </code></pre>
32172      * @param {Object} cfg Xtype definition of item to add.
32173      */
32174     addxtype : function(cfg) {
32175         return this.layout.addxtype(cfg);
32176     
32177     }
32178 });
32179
32180 Roo.ScrollPanel = function(el, config, content){
32181     config = config || {};
32182     config.fitToFrame = true;
32183     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32184     
32185     this.el.dom.style.overflow = "hidden";
32186     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32187     this.el.removeClass("x-layout-inactive-content");
32188     this.el.on("mousewheel", this.onWheel, this);
32189
32190     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32191     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32192     up.unselectable(); down.unselectable();
32193     up.on("click", this.scrollUp, this);
32194     down.on("click", this.scrollDown, this);
32195     up.addClassOnOver("x-scroller-btn-over");
32196     down.addClassOnOver("x-scroller-btn-over");
32197     up.addClassOnClick("x-scroller-btn-click");
32198     down.addClassOnClick("x-scroller-btn-click");
32199     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32200
32201     this.resizeEl = this.el;
32202     this.el = wrap; this.up = up; this.down = down;
32203 };
32204
32205 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32206     increment : 100,
32207     wheelIncrement : 5,
32208     scrollUp : function(){
32209         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32210     },
32211
32212     scrollDown : function(){
32213         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32214     },
32215
32216     afterScroll : function(){
32217         var el = this.resizeEl;
32218         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32219         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32220         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32221     },
32222
32223     setSize : function(){
32224         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32225         this.afterScroll();
32226     },
32227
32228     onWheel : function(e){
32229         var d = e.getWheelDelta();
32230         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32231         this.afterScroll();
32232         e.stopEvent();
32233     },
32234
32235     setContent : function(content, loadScripts){
32236         this.resizeEl.update(content, loadScripts);
32237     }
32238
32239 });
32240
32241
32242
32243
32244
32245
32246
32247
32248
32249 /**
32250  * @class Roo.TreePanel
32251  * @extends Roo.ContentPanel
32252  * @constructor
32253  * Create a new TreePanel. - defaults to fit/scoll contents.
32254  * @param {String/Object} config A string to set only the panel's title, or a config object
32255  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32256  */
32257 Roo.TreePanel = function(config){
32258     var el = config.el;
32259     var tree = config.tree;
32260     delete config.tree; 
32261     delete config.el; // hopefull!
32262     
32263     // wrapper for IE7 strict & safari scroll issue
32264     
32265     var treeEl = el.createChild();
32266     config.resizeEl = treeEl;
32267     
32268     
32269     
32270     Roo.TreePanel.superclass.constructor.call(this, el, config);
32271  
32272  
32273     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32274     //console.log(tree);
32275     this.on('activate', function()
32276     {
32277         if (this.tree.rendered) {
32278             return;
32279         }
32280         //console.log('render tree');
32281         this.tree.render();
32282     });
32283     // this should not be needed.. - it's actually the 'el' that resizes?
32284     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32285     
32286     //this.on('resize',  function (cp, w, h) {
32287     //        this.tree.innerCt.setWidth(w);
32288     //        this.tree.innerCt.setHeight(h);
32289     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32290     //});
32291
32292         
32293     
32294 };
32295
32296 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32297     fitToFrame : true,
32298     autoScroll : true
32299 });
32300
32301
32302
32303
32304
32305
32306
32307
32308
32309
32310
32311 /*
32312  * Based on:
32313  * Ext JS Library 1.1.1
32314  * Copyright(c) 2006-2007, Ext JS, LLC.
32315  *
32316  * Originally Released Under LGPL - original licence link has changed is not relivant.
32317  *
32318  * Fork - LGPL
32319  * <script type="text/javascript">
32320  */
32321  
32322
32323 /**
32324  * @class Roo.ReaderLayout
32325  * @extends Roo.BorderLayout
32326  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32327  * center region containing two nested regions (a top one for a list view and one for item preview below),
32328  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32329  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32330  * expedites the setup of the overall layout and regions for this common application style.
32331  * Example:
32332  <pre><code>
32333 var reader = new Roo.ReaderLayout();
32334 var CP = Roo.ContentPanel;  // shortcut for adding
32335
32336 reader.beginUpdate();
32337 reader.add("north", new CP("north", "North"));
32338 reader.add("west", new CP("west", {title: "West"}));
32339 reader.add("east", new CP("east", {title: "East"}));
32340
32341 reader.regions.listView.add(new CP("listView", "List"));
32342 reader.regions.preview.add(new CP("preview", "Preview"));
32343 reader.endUpdate();
32344 </code></pre>
32345 * @constructor
32346 * Create a new ReaderLayout
32347 * @param {Object} config Configuration options
32348 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32349 * document.body if omitted)
32350 */
32351 Roo.ReaderLayout = function(config, renderTo){
32352     var c = config || {size:{}};
32353     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32354         north: c.north !== false ? Roo.apply({
32355             split:false,
32356             initialSize: 32,
32357             titlebar: false
32358         }, c.north) : false,
32359         west: c.west !== false ? Roo.apply({
32360             split:true,
32361             initialSize: 200,
32362             minSize: 175,
32363             maxSize: 400,
32364             titlebar: true,
32365             collapsible: true,
32366             animate: true,
32367             margins:{left:5,right:0,bottom:5,top:5},
32368             cmargins:{left:5,right:5,bottom:5,top:5}
32369         }, c.west) : false,
32370         east: c.east !== false ? Roo.apply({
32371             split:true,
32372             initialSize: 200,
32373             minSize: 175,
32374             maxSize: 400,
32375             titlebar: true,
32376             collapsible: true,
32377             animate: true,
32378             margins:{left:0,right:5,bottom:5,top:5},
32379             cmargins:{left:5,right:5,bottom:5,top:5}
32380         }, c.east) : false,
32381         center: Roo.apply({
32382             tabPosition: 'top',
32383             autoScroll:false,
32384             closeOnTab: true,
32385             titlebar:false,
32386             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32387         }, c.center)
32388     });
32389
32390     this.el.addClass('x-reader');
32391
32392     this.beginUpdate();
32393
32394     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32395         south: c.preview !== false ? Roo.apply({
32396             split:true,
32397             initialSize: 200,
32398             minSize: 100,
32399             autoScroll:true,
32400             collapsible:true,
32401             titlebar: true,
32402             cmargins:{top:5,left:0, right:0, bottom:0}
32403         }, c.preview) : false,
32404         center: Roo.apply({
32405             autoScroll:false,
32406             titlebar:false,
32407             minHeight:200
32408         }, c.listView)
32409     });
32410     this.add('center', new Roo.NestedLayoutPanel(inner,
32411             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32412
32413     this.endUpdate();
32414
32415     this.regions.preview = inner.getRegion('south');
32416     this.regions.listView = inner.getRegion('center');
32417 };
32418
32419 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32420  * Based on:
32421  * Ext JS Library 1.1.1
32422  * Copyright(c) 2006-2007, Ext JS, LLC.
32423  *
32424  * Originally Released Under LGPL - original licence link has changed is not relivant.
32425  *
32426  * Fork - LGPL
32427  * <script type="text/javascript">
32428  */
32429  
32430 /**
32431  * @class Roo.grid.Grid
32432  * @extends Roo.util.Observable
32433  * This class represents the primary interface of a component based grid control.
32434  * <br><br>Usage:<pre><code>
32435  var grid = new Roo.grid.Grid("my-container-id", {
32436      ds: myDataStore,
32437      cm: myColModel,
32438      selModel: mySelectionModel,
32439      autoSizeColumns: true,
32440      monitorWindowResize: false,
32441      trackMouseOver: true
32442  });
32443  // set any options
32444  grid.render();
32445  * </code></pre>
32446  * <b>Common Problems:</b><br/>
32447  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32448  * element will correct this<br/>
32449  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32450  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32451  * are unpredictable.<br/>
32452  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32453  * grid to calculate dimensions/offsets.<br/>
32454   * @constructor
32455  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32456  * The container MUST have some type of size defined for the grid to fill. The container will be
32457  * automatically set to position relative if it isn't already.
32458  * @param {Object} config A config object that sets properties on this grid.
32459  */
32460 Roo.grid.Grid = function(container, config){
32461         // initialize the container
32462         this.container = Roo.get(container);
32463         this.container.update("");
32464         this.container.setStyle("overflow", "hidden");
32465     this.container.addClass('x-grid-container');
32466
32467     this.id = this.container.id;
32468
32469     Roo.apply(this, config);
32470     // check and correct shorthanded configs
32471     if(this.ds){
32472         this.dataSource = this.ds;
32473         delete this.ds;
32474     }
32475     if(this.cm){
32476         this.colModel = this.cm;
32477         delete this.cm;
32478     }
32479     if(this.sm){
32480         this.selModel = this.sm;
32481         delete this.sm;
32482     }
32483
32484     if (this.selModel) {
32485         this.selModel = Roo.factory(this.selModel, Roo.grid);
32486         this.sm = this.selModel;
32487         this.sm.xmodule = this.xmodule || false;
32488     }
32489     if (typeof(this.colModel.config) == 'undefined') {
32490         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32491         this.cm = this.colModel;
32492         this.cm.xmodule = this.xmodule || false;
32493     }
32494     if (this.dataSource) {
32495         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32496         this.ds = this.dataSource;
32497         this.ds.xmodule = this.xmodule || false;
32498          
32499     }
32500     
32501     
32502     
32503     if(this.width){
32504         this.container.setWidth(this.width);
32505     }
32506
32507     if(this.height){
32508         this.container.setHeight(this.height);
32509     }
32510     /** @private */
32511         this.addEvents({
32512         // raw events
32513         /**
32514          * @event click
32515          * The raw click event for the entire grid.
32516          * @param {Roo.EventObject} e
32517          */
32518         "click" : true,
32519         /**
32520          * @event dblclick
32521          * The raw dblclick event for the entire grid.
32522          * @param {Roo.EventObject} e
32523          */
32524         "dblclick" : true,
32525         /**
32526          * @event contextmenu
32527          * The raw contextmenu event for the entire grid.
32528          * @param {Roo.EventObject} e
32529          */
32530         "contextmenu" : true,
32531         /**
32532          * @event mousedown
32533          * The raw mousedown event for the entire grid.
32534          * @param {Roo.EventObject} e
32535          */
32536         "mousedown" : true,
32537         /**
32538          * @event mouseup
32539          * The raw mouseup event for the entire grid.
32540          * @param {Roo.EventObject} e
32541          */
32542         "mouseup" : true,
32543         /**
32544          * @event mouseover
32545          * The raw mouseover event for the entire grid.
32546          * @param {Roo.EventObject} e
32547          */
32548         "mouseover" : true,
32549         /**
32550          * @event mouseout
32551          * The raw mouseout event for the entire grid.
32552          * @param {Roo.EventObject} e
32553          */
32554         "mouseout" : true,
32555         /**
32556          * @event keypress
32557          * The raw keypress event for the entire grid.
32558          * @param {Roo.EventObject} e
32559          */
32560         "keypress" : true,
32561         /**
32562          * @event keydown
32563          * The raw keydown event for the entire grid.
32564          * @param {Roo.EventObject} e
32565          */
32566         "keydown" : true,
32567
32568         // custom events
32569
32570         /**
32571          * @event cellclick
32572          * Fires when a cell is clicked
32573          * @param {Grid} this
32574          * @param {Number} rowIndex
32575          * @param {Number} columnIndex
32576          * @param {Roo.EventObject} e
32577          */
32578         "cellclick" : true,
32579         /**
32580          * @event celldblclick
32581          * Fires when a cell is double clicked
32582          * @param {Grid} this
32583          * @param {Number} rowIndex
32584          * @param {Number} columnIndex
32585          * @param {Roo.EventObject} e
32586          */
32587         "celldblclick" : true,
32588         /**
32589          * @event rowclick
32590          * Fires when a row is clicked
32591          * @param {Grid} this
32592          * @param {Number} rowIndex
32593          * @param {Roo.EventObject} e
32594          */
32595         "rowclick" : true,
32596         /**
32597          * @event rowdblclick
32598          * Fires when a row is double clicked
32599          * @param {Grid} this
32600          * @param {Number} rowIndex
32601          * @param {Roo.EventObject} e
32602          */
32603         "rowdblclick" : true,
32604         /**
32605          * @event headerclick
32606          * Fires when a header is clicked
32607          * @param {Grid} this
32608          * @param {Number} columnIndex
32609          * @param {Roo.EventObject} e
32610          */
32611         "headerclick" : true,
32612         /**
32613          * @event headerdblclick
32614          * Fires when a header cell is double clicked
32615          * @param {Grid} this
32616          * @param {Number} columnIndex
32617          * @param {Roo.EventObject} e
32618          */
32619         "headerdblclick" : true,
32620         /**
32621          * @event rowcontextmenu
32622          * Fires when a row is right clicked
32623          * @param {Grid} this
32624          * @param {Number} rowIndex
32625          * @param {Roo.EventObject} e
32626          */
32627         "rowcontextmenu" : true,
32628         /**
32629          * @event cellcontextmenu
32630          * Fires when a cell is right clicked
32631          * @param {Grid} this
32632          * @param {Number} rowIndex
32633          * @param {Number} cellIndex
32634          * @param {Roo.EventObject} e
32635          */
32636          "cellcontextmenu" : true,
32637         /**
32638          * @event headercontextmenu
32639          * Fires when a header is right clicked
32640          * @param {Grid} this
32641          * @param {Number} columnIndex
32642          * @param {Roo.EventObject} e
32643          */
32644         "headercontextmenu" : true,
32645         /**
32646          * @event bodyscroll
32647          * Fires when the body element is scrolled
32648          * @param {Number} scrollLeft
32649          * @param {Number} scrollTop
32650          */
32651         "bodyscroll" : true,
32652         /**
32653          * @event columnresize
32654          * Fires when the user resizes a column
32655          * @param {Number} columnIndex
32656          * @param {Number} newSize
32657          */
32658         "columnresize" : true,
32659         /**
32660          * @event columnmove
32661          * Fires when the user moves a column
32662          * @param {Number} oldIndex
32663          * @param {Number} newIndex
32664          */
32665         "columnmove" : true,
32666         /**
32667          * @event startdrag
32668          * Fires when row(s) start being dragged
32669          * @param {Grid} this
32670          * @param {Roo.GridDD} dd The drag drop object
32671          * @param {event} e The raw browser event
32672          */
32673         "startdrag" : true,
32674         /**
32675          * @event enddrag
32676          * Fires when a drag operation is complete
32677          * @param {Grid} this
32678          * @param {Roo.GridDD} dd The drag drop object
32679          * @param {event} e The raw browser event
32680          */
32681         "enddrag" : true,
32682         /**
32683          * @event dragdrop
32684          * Fires when dragged row(s) are dropped on a valid DD target
32685          * @param {Grid} this
32686          * @param {Roo.GridDD} dd The drag drop object
32687          * @param {String} targetId The target drag drop object
32688          * @param {event} e The raw browser event
32689          */
32690         "dragdrop" : true,
32691         /**
32692          * @event dragover
32693          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32694          * @param {Grid} this
32695          * @param {Roo.GridDD} dd The drag drop object
32696          * @param {String} targetId The target drag drop object
32697          * @param {event} e The raw browser event
32698          */
32699         "dragover" : true,
32700         /**
32701          * @event dragenter
32702          *  Fires when the dragged row(s) first cross another DD target while being dragged
32703          * @param {Grid} this
32704          * @param {Roo.GridDD} dd The drag drop object
32705          * @param {String} targetId The target drag drop object
32706          * @param {event} e The raw browser event
32707          */
32708         "dragenter" : true,
32709         /**
32710          * @event dragout
32711          * Fires when the dragged row(s) leave another DD target while being dragged
32712          * @param {Grid} this
32713          * @param {Roo.GridDD} dd The drag drop object
32714          * @param {String} targetId The target drag drop object
32715          * @param {event} e The raw browser event
32716          */
32717         "dragout" : true,
32718         /**
32719          * @event rowclass
32720          * Fires when a row is rendered, so you can change add a style to it.
32721          * @param {GridView} gridview   The grid view
32722          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32723          */
32724         'rowclass' : true,
32725
32726         /**
32727          * @event render
32728          * Fires when the grid is rendered
32729          * @param {Grid} grid
32730          */
32731         'render' : true
32732     });
32733
32734     Roo.grid.Grid.superclass.constructor.call(this);
32735 };
32736 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32737     
32738     /**
32739      * @cfg {String} ddGroup - drag drop group.
32740      */
32741
32742     /**
32743      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32744      */
32745     minColumnWidth : 25,
32746
32747     /**
32748      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32749      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32750      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32751      */
32752     autoSizeColumns : false,
32753
32754     /**
32755      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32756      */
32757     autoSizeHeaders : true,
32758
32759     /**
32760      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32761      */
32762     monitorWindowResize : true,
32763
32764     /**
32765      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32766      * rows measured to get a columns size. Default is 0 (all rows).
32767      */
32768     maxRowsToMeasure : 0,
32769
32770     /**
32771      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32772      */
32773     trackMouseOver : true,
32774
32775     /**
32776     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32777     */
32778     
32779     /**
32780     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32781     */
32782     enableDragDrop : false,
32783     
32784     /**
32785     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32786     */
32787     enableColumnMove : true,
32788     
32789     /**
32790     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32791     */
32792     enableColumnHide : true,
32793     
32794     /**
32795     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32796     */
32797     enableRowHeightSync : false,
32798     
32799     /**
32800     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32801     */
32802     stripeRows : true,
32803     
32804     /**
32805     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32806     */
32807     autoHeight : false,
32808
32809     /**
32810      * @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.
32811      */
32812     autoExpandColumn : false,
32813
32814     /**
32815     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32816     * Default is 50.
32817     */
32818     autoExpandMin : 50,
32819
32820     /**
32821     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32822     */
32823     autoExpandMax : 1000,
32824
32825     /**
32826     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32827     */
32828     view : null,
32829
32830     /**
32831     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32832     */
32833     loadMask : false,
32834     /**
32835     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32836     */
32837     dropTarget: false,
32838     
32839    
32840     
32841     // private
32842     rendered : false,
32843
32844     /**
32845     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32846     * of a fixed width. Default is false.
32847     */
32848     /**
32849     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32850     */
32851     /**
32852      * Called once after all setup has been completed and the grid is ready to be rendered.
32853      * @return {Roo.grid.Grid} this
32854      */
32855     render : function()
32856     {
32857         var c = this.container;
32858         // try to detect autoHeight/width mode
32859         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32860             this.autoHeight = true;
32861         }
32862         var view = this.getView();
32863         view.init(this);
32864
32865         c.on("click", this.onClick, this);
32866         c.on("dblclick", this.onDblClick, this);
32867         c.on("contextmenu", this.onContextMenu, this);
32868         c.on("keydown", this.onKeyDown, this);
32869         if (Roo.isTouch) {
32870             c.on("touchstart", this.onTouchStart, this);
32871         }
32872
32873         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32874
32875         this.getSelectionModel().init(this);
32876
32877         view.render();
32878
32879         if(this.loadMask){
32880             this.loadMask = new Roo.LoadMask(this.container,
32881                     Roo.apply({store:this.dataSource}, this.loadMask));
32882         }
32883         
32884         
32885         if (this.toolbar && this.toolbar.xtype) {
32886             this.toolbar.container = this.getView().getHeaderPanel(true);
32887             this.toolbar = new Roo.Toolbar(this.toolbar);
32888         }
32889         if (this.footer && this.footer.xtype) {
32890             this.footer.dataSource = this.getDataSource();
32891             this.footer.container = this.getView().getFooterPanel(true);
32892             this.footer = Roo.factory(this.footer, Roo);
32893         }
32894         if (this.dropTarget && this.dropTarget.xtype) {
32895             delete this.dropTarget.xtype;
32896             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32897         }
32898         
32899         
32900         this.rendered = true;
32901         this.fireEvent('render', this);
32902         return this;
32903     },
32904
32905         /**
32906          * Reconfigures the grid to use a different Store and Column Model.
32907          * The View will be bound to the new objects and refreshed.
32908          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32909          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32910          */
32911     reconfigure : function(dataSource, colModel){
32912         if(this.loadMask){
32913             this.loadMask.destroy();
32914             this.loadMask = new Roo.LoadMask(this.container,
32915                     Roo.apply({store:dataSource}, this.loadMask));
32916         }
32917         this.view.bind(dataSource, colModel);
32918         this.dataSource = dataSource;
32919         this.colModel = colModel;
32920         this.view.refresh(true);
32921     },
32922
32923     // private
32924     onKeyDown : function(e){
32925         this.fireEvent("keydown", e);
32926     },
32927
32928     /**
32929      * Destroy this grid.
32930      * @param {Boolean} removeEl True to remove the element
32931      */
32932     destroy : function(removeEl, keepListeners){
32933         if(this.loadMask){
32934             this.loadMask.destroy();
32935         }
32936         var c = this.container;
32937         c.removeAllListeners();
32938         this.view.destroy();
32939         this.colModel.purgeListeners();
32940         if(!keepListeners){
32941             this.purgeListeners();
32942         }
32943         c.update("");
32944         if(removeEl === true){
32945             c.remove();
32946         }
32947     },
32948
32949     // private
32950     processEvent : function(name, e){
32951         // does this fire select???
32952         //Roo.log('grid:processEvent '  + name);
32953         
32954         if (name != 'touchstart' ) {
32955             this.fireEvent(name, e);    
32956         }
32957         
32958         var t = e.getTarget();
32959         var v = this.view;
32960         var header = v.findHeaderIndex(t);
32961         if(header !== false){
32962             var ename = name == 'touchstart' ? 'click' : name;
32963              
32964             this.fireEvent("header" + ename, this, header, e);
32965         }else{
32966             var row = v.findRowIndex(t);
32967             var cell = v.findCellIndex(t);
32968             if (name == 'touchstart') {
32969                 // first touch is always a click.
32970                 // hopefull this happens after selection is updated.?
32971                 name = false;
32972                 
32973                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32974                     var cs = this.selModel.getSelectedCell();
32975                     if (row == cs[0] && cell == cs[1]){
32976                         name = 'dblclick';
32977                     }
32978                 }
32979                 if (typeof(this.selModel.getSelections) != 'undefined') {
32980                     var cs = this.selModel.getSelections();
32981                     var ds = this.dataSource;
32982                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32983                         name = 'dblclick';
32984                     }
32985                 }
32986                 if (!name) {
32987                     return;
32988                 }
32989             }
32990             
32991             
32992             if(row !== false){
32993                 this.fireEvent("row" + name, this, row, e);
32994                 if(cell !== false){
32995                     this.fireEvent("cell" + name, this, row, cell, e);
32996                 }
32997             }
32998         }
32999     },
33000
33001     // private
33002     onClick : function(e){
33003         this.processEvent("click", e);
33004     },
33005    // private
33006     onTouchStart : function(e){
33007         this.processEvent("touchstart", e);
33008     },
33009
33010     // private
33011     onContextMenu : function(e, t){
33012         this.processEvent("contextmenu", e);
33013     },
33014
33015     // private
33016     onDblClick : function(e){
33017         this.processEvent("dblclick", e);
33018     },
33019
33020     // private
33021     walkCells : function(row, col, step, fn, scope){
33022         var cm = this.colModel, clen = cm.getColumnCount();
33023         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33024         if(step < 0){
33025             if(col < 0){
33026                 row--;
33027                 first = false;
33028             }
33029             while(row >= 0){
33030                 if(!first){
33031                     col = clen-1;
33032                 }
33033                 first = false;
33034                 while(col >= 0){
33035                     if(fn.call(scope || this, row, col, cm) === true){
33036                         return [row, col];
33037                     }
33038                     col--;
33039                 }
33040                 row--;
33041             }
33042         } else {
33043             if(col >= clen){
33044                 row++;
33045                 first = false;
33046             }
33047             while(row < rlen){
33048                 if(!first){
33049                     col = 0;
33050                 }
33051                 first = false;
33052                 while(col < clen){
33053                     if(fn.call(scope || this, row, col, cm) === true){
33054                         return [row, col];
33055                     }
33056                     col++;
33057                 }
33058                 row++;
33059             }
33060         }
33061         return null;
33062     },
33063
33064     // private
33065     getSelections : function(){
33066         return this.selModel.getSelections();
33067     },
33068
33069     /**
33070      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33071      * but if manual update is required this method will initiate it.
33072      */
33073     autoSize : function(){
33074         if(this.rendered){
33075             this.view.layout();
33076             if(this.view.adjustForScroll){
33077                 this.view.adjustForScroll();
33078             }
33079         }
33080     },
33081
33082     /**
33083      * Returns the grid's underlying element.
33084      * @return {Element} The element
33085      */
33086     getGridEl : function(){
33087         return this.container;
33088     },
33089
33090     // private for compatibility, overridden by editor grid
33091     stopEditing : function(){},
33092
33093     /**
33094      * Returns the grid's SelectionModel.
33095      * @return {SelectionModel}
33096      */
33097     getSelectionModel : function(){
33098         if(!this.selModel){
33099             this.selModel = new Roo.grid.RowSelectionModel();
33100         }
33101         return this.selModel;
33102     },
33103
33104     /**
33105      * Returns the grid's DataSource.
33106      * @return {DataSource}
33107      */
33108     getDataSource : function(){
33109         return this.dataSource;
33110     },
33111
33112     /**
33113      * Returns the grid's ColumnModel.
33114      * @return {ColumnModel}
33115      */
33116     getColumnModel : function(){
33117         return this.colModel;
33118     },
33119
33120     /**
33121      * Returns the grid's GridView object.
33122      * @return {GridView}
33123      */
33124     getView : function(){
33125         if(!this.view){
33126             this.view = new Roo.grid.GridView(this.viewConfig);
33127         }
33128         return this.view;
33129     },
33130     /**
33131      * Called to get grid's drag proxy text, by default returns this.ddText.
33132      * @return {String}
33133      */
33134     getDragDropText : function(){
33135         var count = this.selModel.getCount();
33136         return String.format(this.ddText, count, count == 1 ? '' : 's');
33137     }
33138 });
33139 /**
33140  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33141  * %0 is replaced with the number of selected rows.
33142  * @type String
33143  */
33144 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33145  * Based on:
33146  * Ext JS Library 1.1.1
33147  * Copyright(c) 2006-2007, Ext JS, LLC.
33148  *
33149  * Originally Released Under LGPL - original licence link has changed is not relivant.
33150  *
33151  * Fork - LGPL
33152  * <script type="text/javascript">
33153  */
33154  
33155 Roo.grid.AbstractGridView = function(){
33156         this.grid = null;
33157         
33158         this.events = {
33159             "beforerowremoved" : true,
33160             "beforerowsinserted" : true,
33161             "beforerefresh" : true,
33162             "rowremoved" : true,
33163             "rowsinserted" : true,
33164             "rowupdated" : true,
33165             "refresh" : true
33166         };
33167     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33168 };
33169
33170 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33171     rowClass : "x-grid-row",
33172     cellClass : "x-grid-cell",
33173     tdClass : "x-grid-td",
33174     hdClass : "x-grid-hd",
33175     splitClass : "x-grid-hd-split",
33176     
33177     init: function(grid){
33178         this.grid = grid;
33179                 var cid = this.grid.getGridEl().id;
33180         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33181         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33182         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33183         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33184         },
33185         
33186     getColumnRenderers : function(){
33187         var renderers = [];
33188         var cm = this.grid.colModel;
33189         var colCount = cm.getColumnCount();
33190         for(var i = 0; i < colCount; i++){
33191             renderers[i] = cm.getRenderer(i);
33192         }
33193         return renderers;
33194     },
33195     
33196     getColumnIds : function(){
33197         var ids = [];
33198         var cm = this.grid.colModel;
33199         var colCount = cm.getColumnCount();
33200         for(var i = 0; i < colCount; i++){
33201             ids[i] = cm.getColumnId(i);
33202         }
33203         return ids;
33204     },
33205     
33206     getDataIndexes : function(){
33207         if(!this.indexMap){
33208             this.indexMap = this.buildIndexMap();
33209         }
33210         return this.indexMap.colToData;
33211     },
33212     
33213     getColumnIndexByDataIndex : function(dataIndex){
33214         if(!this.indexMap){
33215             this.indexMap = this.buildIndexMap();
33216         }
33217         return this.indexMap.dataToCol[dataIndex];
33218     },
33219     
33220     /**
33221      * Set a css style for a column dynamically. 
33222      * @param {Number} colIndex The index of the column
33223      * @param {String} name The css property name
33224      * @param {String} value The css value
33225      */
33226     setCSSStyle : function(colIndex, name, value){
33227         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33228         Roo.util.CSS.updateRule(selector, name, value);
33229     },
33230     
33231     generateRules : function(cm){
33232         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33233         Roo.util.CSS.removeStyleSheet(rulesId);
33234         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33235             var cid = cm.getColumnId(i);
33236             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33237                          this.tdSelector, cid, " {\n}\n",
33238                          this.hdSelector, cid, " {\n}\n",
33239                          this.splitSelector, cid, " {\n}\n");
33240         }
33241         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33242     }
33243 });/*
33244  * Based on:
33245  * Ext JS Library 1.1.1
33246  * Copyright(c) 2006-2007, Ext JS, LLC.
33247  *
33248  * Originally Released Under LGPL - original licence link has changed is not relivant.
33249  *
33250  * Fork - LGPL
33251  * <script type="text/javascript">
33252  */
33253
33254 // private
33255 // This is a support class used internally by the Grid components
33256 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33257     this.grid = grid;
33258     this.view = grid.getView();
33259     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33260     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33261     if(hd2){
33262         this.setHandleElId(Roo.id(hd));
33263         this.setOuterHandleElId(Roo.id(hd2));
33264     }
33265     this.scroll = false;
33266 };
33267 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33268     maxDragWidth: 120,
33269     getDragData : function(e){
33270         var t = Roo.lib.Event.getTarget(e);
33271         var h = this.view.findHeaderCell(t);
33272         if(h){
33273             return {ddel: h.firstChild, header:h};
33274         }
33275         return false;
33276     },
33277
33278     onInitDrag : function(e){
33279         this.view.headersDisabled = true;
33280         var clone = this.dragData.ddel.cloneNode(true);
33281         clone.id = Roo.id();
33282         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33283         this.proxy.update(clone);
33284         return true;
33285     },
33286
33287     afterValidDrop : function(){
33288         var v = this.view;
33289         setTimeout(function(){
33290             v.headersDisabled = false;
33291         }, 50);
33292     },
33293
33294     afterInvalidDrop : function(){
33295         var v = this.view;
33296         setTimeout(function(){
33297             v.headersDisabled = false;
33298         }, 50);
33299     }
33300 });
33301 /*
33302  * Based on:
33303  * Ext JS Library 1.1.1
33304  * Copyright(c) 2006-2007, Ext JS, LLC.
33305  *
33306  * Originally Released Under LGPL - original licence link has changed is not relivant.
33307  *
33308  * Fork - LGPL
33309  * <script type="text/javascript">
33310  */
33311 // private
33312 // This is a support class used internally by the Grid components
33313 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33314     this.grid = grid;
33315     this.view = grid.getView();
33316     // split the proxies so they don't interfere with mouse events
33317     this.proxyTop = Roo.DomHelper.append(document.body, {
33318         cls:"col-move-top", html:"&#160;"
33319     }, true);
33320     this.proxyBottom = Roo.DomHelper.append(document.body, {
33321         cls:"col-move-bottom", html:"&#160;"
33322     }, true);
33323     this.proxyTop.hide = this.proxyBottom.hide = function(){
33324         this.setLeftTop(-100,-100);
33325         this.setStyle("visibility", "hidden");
33326     };
33327     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33328     // temporarily disabled
33329     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33330     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33331 };
33332 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33333     proxyOffsets : [-4, -9],
33334     fly: Roo.Element.fly,
33335
33336     getTargetFromEvent : function(e){
33337         var t = Roo.lib.Event.getTarget(e);
33338         var cindex = this.view.findCellIndex(t);
33339         if(cindex !== false){
33340             return this.view.getHeaderCell(cindex);
33341         }
33342         return null;
33343     },
33344
33345     nextVisible : function(h){
33346         var v = this.view, cm = this.grid.colModel;
33347         h = h.nextSibling;
33348         while(h){
33349             if(!cm.isHidden(v.getCellIndex(h))){
33350                 return h;
33351             }
33352             h = h.nextSibling;
33353         }
33354         return null;
33355     },
33356
33357     prevVisible : function(h){
33358         var v = this.view, cm = this.grid.colModel;
33359         h = h.prevSibling;
33360         while(h){
33361             if(!cm.isHidden(v.getCellIndex(h))){
33362                 return h;
33363             }
33364             h = h.prevSibling;
33365         }
33366         return null;
33367     },
33368
33369     positionIndicator : function(h, n, e){
33370         var x = Roo.lib.Event.getPageX(e);
33371         var r = Roo.lib.Dom.getRegion(n.firstChild);
33372         var px, pt, py = r.top + this.proxyOffsets[1];
33373         if((r.right - x) <= (r.right-r.left)/2){
33374             px = r.right+this.view.borderWidth;
33375             pt = "after";
33376         }else{
33377             px = r.left;
33378             pt = "before";
33379         }
33380         var oldIndex = this.view.getCellIndex(h);
33381         var newIndex = this.view.getCellIndex(n);
33382
33383         if(this.grid.colModel.isFixed(newIndex)){
33384             return false;
33385         }
33386
33387         var locked = this.grid.colModel.isLocked(newIndex);
33388
33389         if(pt == "after"){
33390             newIndex++;
33391         }
33392         if(oldIndex < newIndex){
33393             newIndex--;
33394         }
33395         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33396             return false;
33397         }
33398         px +=  this.proxyOffsets[0];
33399         this.proxyTop.setLeftTop(px, py);
33400         this.proxyTop.show();
33401         if(!this.bottomOffset){
33402             this.bottomOffset = this.view.mainHd.getHeight();
33403         }
33404         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33405         this.proxyBottom.show();
33406         return pt;
33407     },
33408
33409     onNodeEnter : function(n, dd, e, data){
33410         if(data.header != n){
33411             this.positionIndicator(data.header, n, e);
33412         }
33413     },
33414
33415     onNodeOver : function(n, dd, e, data){
33416         var result = false;
33417         if(data.header != n){
33418             result = this.positionIndicator(data.header, n, e);
33419         }
33420         if(!result){
33421             this.proxyTop.hide();
33422             this.proxyBottom.hide();
33423         }
33424         return result ? this.dropAllowed : this.dropNotAllowed;
33425     },
33426
33427     onNodeOut : function(n, dd, e, data){
33428         this.proxyTop.hide();
33429         this.proxyBottom.hide();
33430     },
33431
33432     onNodeDrop : function(n, dd, e, data){
33433         var h = data.header;
33434         if(h != n){
33435             var cm = this.grid.colModel;
33436             var x = Roo.lib.Event.getPageX(e);
33437             var r = Roo.lib.Dom.getRegion(n.firstChild);
33438             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33439             var oldIndex = this.view.getCellIndex(h);
33440             var newIndex = this.view.getCellIndex(n);
33441             var locked = cm.isLocked(newIndex);
33442             if(pt == "after"){
33443                 newIndex++;
33444             }
33445             if(oldIndex < newIndex){
33446                 newIndex--;
33447             }
33448             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33449                 return false;
33450             }
33451             cm.setLocked(oldIndex, locked, true);
33452             cm.moveColumn(oldIndex, newIndex);
33453             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33454             return true;
33455         }
33456         return false;
33457     }
33458 });
33459 /*
33460  * Based on:
33461  * Ext JS Library 1.1.1
33462  * Copyright(c) 2006-2007, Ext JS, LLC.
33463  *
33464  * Originally Released Under LGPL - original licence link has changed is not relivant.
33465  *
33466  * Fork - LGPL
33467  * <script type="text/javascript">
33468  */
33469   
33470 /**
33471  * @class Roo.grid.GridView
33472  * @extends Roo.util.Observable
33473  *
33474  * @constructor
33475  * @param {Object} config
33476  */
33477 Roo.grid.GridView = function(config){
33478     Roo.grid.GridView.superclass.constructor.call(this);
33479     this.el = null;
33480
33481     Roo.apply(this, config);
33482 };
33483
33484 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33485
33486     unselectable :  'unselectable="on"',
33487     unselectableCls :  'x-unselectable',
33488     
33489     
33490     rowClass : "x-grid-row",
33491
33492     cellClass : "x-grid-col",
33493
33494     tdClass : "x-grid-td",
33495
33496     hdClass : "x-grid-hd",
33497
33498     splitClass : "x-grid-split",
33499
33500     sortClasses : ["sort-asc", "sort-desc"],
33501
33502     enableMoveAnim : false,
33503
33504     hlColor: "C3DAF9",
33505
33506     dh : Roo.DomHelper,
33507
33508     fly : Roo.Element.fly,
33509
33510     css : Roo.util.CSS,
33511
33512     borderWidth: 1,
33513
33514     splitOffset: 3,
33515
33516     scrollIncrement : 22,
33517
33518     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33519
33520     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33521
33522     bind : function(ds, cm){
33523         if(this.ds){
33524             this.ds.un("load", this.onLoad, this);
33525             this.ds.un("datachanged", this.onDataChange, this);
33526             this.ds.un("add", this.onAdd, this);
33527             this.ds.un("remove", this.onRemove, this);
33528             this.ds.un("update", this.onUpdate, this);
33529             this.ds.un("clear", this.onClear, this);
33530         }
33531         if(ds){
33532             ds.on("load", this.onLoad, this);
33533             ds.on("datachanged", this.onDataChange, this);
33534             ds.on("add", this.onAdd, this);
33535             ds.on("remove", this.onRemove, this);
33536             ds.on("update", this.onUpdate, this);
33537             ds.on("clear", this.onClear, this);
33538         }
33539         this.ds = ds;
33540
33541         if(this.cm){
33542             this.cm.un("widthchange", this.onColWidthChange, this);
33543             this.cm.un("headerchange", this.onHeaderChange, this);
33544             this.cm.un("hiddenchange", this.onHiddenChange, this);
33545             this.cm.un("columnmoved", this.onColumnMove, this);
33546             this.cm.un("columnlockchange", this.onColumnLock, this);
33547         }
33548         if(cm){
33549             this.generateRules(cm);
33550             cm.on("widthchange", this.onColWidthChange, this);
33551             cm.on("headerchange", this.onHeaderChange, this);
33552             cm.on("hiddenchange", this.onHiddenChange, this);
33553             cm.on("columnmoved", this.onColumnMove, this);
33554             cm.on("columnlockchange", this.onColumnLock, this);
33555         }
33556         this.cm = cm;
33557     },
33558
33559     init: function(grid){
33560         Roo.grid.GridView.superclass.init.call(this, grid);
33561
33562         this.bind(grid.dataSource, grid.colModel);
33563
33564         grid.on("headerclick", this.handleHeaderClick, this);
33565
33566         if(grid.trackMouseOver){
33567             grid.on("mouseover", this.onRowOver, this);
33568             grid.on("mouseout", this.onRowOut, this);
33569         }
33570         grid.cancelTextSelection = function(){};
33571         this.gridId = grid.id;
33572
33573         var tpls = this.templates || {};
33574
33575         if(!tpls.master){
33576             tpls.master = new Roo.Template(
33577                '<div class="x-grid" hidefocus="true">',
33578                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33579                   '<div class="x-grid-topbar"></div>',
33580                   '<div class="x-grid-scroller"><div></div></div>',
33581                   '<div class="x-grid-locked">',
33582                       '<div class="x-grid-header">{lockedHeader}</div>',
33583                       '<div class="x-grid-body">{lockedBody}</div>',
33584                   "</div>",
33585                   '<div class="x-grid-viewport">',
33586                       '<div class="x-grid-header">{header}</div>',
33587                       '<div class="x-grid-body">{body}</div>',
33588                   "</div>",
33589                   '<div class="x-grid-bottombar"></div>',
33590                  
33591                   '<div class="x-grid-resize-proxy">&#160;</div>',
33592                "</div>"
33593             );
33594             tpls.master.disableformats = true;
33595         }
33596
33597         if(!tpls.header){
33598             tpls.header = new Roo.Template(
33599                '<table border="0" cellspacing="0" cellpadding="0">',
33600                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33601                "</table>{splits}"
33602             );
33603             tpls.header.disableformats = true;
33604         }
33605         tpls.header.compile();
33606
33607         if(!tpls.hcell){
33608             tpls.hcell = new Roo.Template(
33609                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33610                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33611                 "</div></td>"
33612              );
33613              tpls.hcell.disableFormats = true;
33614         }
33615         tpls.hcell.compile();
33616
33617         if(!tpls.hsplit){
33618             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33619                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33620             tpls.hsplit.disableFormats = true;
33621         }
33622         tpls.hsplit.compile();
33623
33624         if(!tpls.body){
33625             tpls.body = new Roo.Template(
33626                '<table border="0" cellspacing="0" cellpadding="0">',
33627                "<tbody>{rows}</tbody>",
33628                "</table>"
33629             );
33630             tpls.body.disableFormats = true;
33631         }
33632         tpls.body.compile();
33633
33634         if(!tpls.row){
33635             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33636             tpls.row.disableFormats = true;
33637         }
33638         tpls.row.compile();
33639
33640         if(!tpls.cell){
33641             tpls.cell = new Roo.Template(
33642                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33643                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33644                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33645                 "</td>"
33646             );
33647             tpls.cell.disableFormats = true;
33648         }
33649         tpls.cell.compile();
33650
33651         this.templates = tpls;
33652     },
33653
33654     // remap these for backwards compat
33655     onColWidthChange : function(){
33656         this.updateColumns.apply(this, arguments);
33657     },
33658     onHeaderChange : function(){
33659         this.updateHeaders.apply(this, arguments);
33660     }, 
33661     onHiddenChange : function(){
33662         this.handleHiddenChange.apply(this, arguments);
33663     },
33664     onColumnMove : function(){
33665         this.handleColumnMove.apply(this, arguments);
33666     },
33667     onColumnLock : function(){
33668         this.handleLockChange.apply(this, arguments);
33669     },
33670
33671     onDataChange : function(){
33672         this.refresh();
33673         this.updateHeaderSortState();
33674     },
33675
33676     onClear : function(){
33677         this.refresh();
33678     },
33679
33680     onUpdate : function(ds, record){
33681         this.refreshRow(record);
33682     },
33683
33684     refreshRow : function(record){
33685         var ds = this.ds, index;
33686         if(typeof record == 'number'){
33687             index = record;
33688             record = ds.getAt(index);
33689         }else{
33690             index = ds.indexOf(record);
33691         }
33692         this.insertRows(ds, index, index, true);
33693         this.onRemove(ds, record, index+1, true);
33694         this.syncRowHeights(index, index);
33695         this.layout();
33696         this.fireEvent("rowupdated", this, index, record);
33697     },
33698
33699     onAdd : function(ds, records, index){
33700         this.insertRows(ds, index, index + (records.length-1));
33701     },
33702
33703     onRemove : function(ds, record, index, isUpdate){
33704         if(isUpdate !== true){
33705             this.fireEvent("beforerowremoved", this, index, record);
33706         }
33707         var bt = this.getBodyTable(), lt = this.getLockedTable();
33708         if(bt.rows[index]){
33709             bt.firstChild.removeChild(bt.rows[index]);
33710         }
33711         if(lt.rows[index]){
33712             lt.firstChild.removeChild(lt.rows[index]);
33713         }
33714         if(isUpdate !== true){
33715             this.stripeRows(index);
33716             this.syncRowHeights(index, index);
33717             this.layout();
33718             this.fireEvent("rowremoved", this, index, record);
33719         }
33720     },
33721
33722     onLoad : function(){
33723         this.scrollToTop();
33724     },
33725
33726     /**
33727      * Scrolls the grid to the top
33728      */
33729     scrollToTop : function(){
33730         if(this.scroller){
33731             this.scroller.dom.scrollTop = 0;
33732             this.syncScroll();
33733         }
33734     },
33735
33736     /**
33737      * Gets a panel in the header of the grid that can be used for toolbars etc.
33738      * After modifying the contents of this panel a call to grid.autoSize() may be
33739      * required to register any changes in size.
33740      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33741      * @return Roo.Element
33742      */
33743     getHeaderPanel : function(doShow){
33744         if(doShow){
33745             this.headerPanel.show();
33746         }
33747         return this.headerPanel;
33748     },
33749
33750     /**
33751      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33752      * After modifying the contents of this panel a call to grid.autoSize() may be
33753      * required to register any changes in size.
33754      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33755      * @return Roo.Element
33756      */
33757     getFooterPanel : function(doShow){
33758         if(doShow){
33759             this.footerPanel.show();
33760         }
33761         return this.footerPanel;
33762     },
33763
33764     initElements : function(){
33765         var E = Roo.Element;
33766         var el = this.grid.getGridEl().dom.firstChild;
33767         var cs = el.childNodes;
33768
33769         this.el = new E(el);
33770         
33771          this.focusEl = new E(el.firstChild);
33772         this.focusEl.swallowEvent("click", true);
33773         
33774         this.headerPanel = new E(cs[1]);
33775         this.headerPanel.enableDisplayMode("block");
33776
33777         this.scroller = new E(cs[2]);
33778         this.scrollSizer = new E(this.scroller.dom.firstChild);
33779
33780         this.lockedWrap = new E(cs[3]);
33781         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33782         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33783
33784         this.mainWrap = new E(cs[4]);
33785         this.mainHd = new E(this.mainWrap.dom.firstChild);
33786         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33787
33788         this.footerPanel = new E(cs[5]);
33789         this.footerPanel.enableDisplayMode("block");
33790
33791         this.resizeProxy = new E(cs[6]);
33792
33793         this.headerSelector = String.format(
33794            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33795            this.lockedHd.id, this.mainHd.id
33796         );
33797
33798         this.splitterSelector = String.format(
33799            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33800            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33801         );
33802     },
33803     idToCssName : function(s)
33804     {
33805         return s.replace(/[^a-z0-9]+/ig, '-');
33806     },
33807
33808     getHeaderCell : function(index){
33809         return Roo.DomQuery.select(this.headerSelector)[index];
33810     },
33811
33812     getHeaderCellMeasure : function(index){
33813         return this.getHeaderCell(index).firstChild;
33814     },
33815
33816     getHeaderCellText : function(index){
33817         return this.getHeaderCell(index).firstChild.firstChild;
33818     },
33819
33820     getLockedTable : function(){
33821         return this.lockedBody.dom.firstChild;
33822     },
33823
33824     getBodyTable : function(){
33825         return this.mainBody.dom.firstChild;
33826     },
33827
33828     getLockedRow : function(index){
33829         return this.getLockedTable().rows[index];
33830     },
33831
33832     getRow : function(index){
33833         return this.getBodyTable().rows[index];
33834     },
33835
33836     getRowComposite : function(index){
33837         if(!this.rowEl){
33838             this.rowEl = new Roo.CompositeElementLite();
33839         }
33840         var els = [], lrow, mrow;
33841         if(lrow = this.getLockedRow(index)){
33842             els.push(lrow);
33843         }
33844         if(mrow = this.getRow(index)){
33845             els.push(mrow);
33846         }
33847         this.rowEl.elements = els;
33848         return this.rowEl;
33849     },
33850     /**
33851      * Gets the 'td' of the cell
33852      * 
33853      * @param {Integer} rowIndex row to select
33854      * @param {Integer} colIndex column to select
33855      * 
33856      * @return {Object} 
33857      */
33858     getCell : function(rowIndex, colIndex){
33859         var locked = this.cm.getLockedCount();
33860         var source;
33861         if(colIndex < locked){
33862             source = this.lockedBody.dom.firstChild;
33863         }else{
33864             source = this.mainBody.dom.firstChild;
33865             colIndex -= locked;
33866         }
33867         return source.rows[rowIndex].childNodes[colIndex];
33868     },
33869
33870     getCellText : function(rowIndex, colIndex){
33871         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33872     },
33873
33874     getCellBox : function(cell){
33875         var b = this.fly(cell).getBox();
33876         if(Roo.isOpera){ // opera fails to report the Y
33877             b.y = cell.offsetTop + this.mainBody.getY();
33878         }
33879         return b;
33880     },
33881
33882     getCellIndex : function(cell){
33883         var id = String(cell.className).match(this.cellRE);
33884         if(id){
33885             return parseInt(id[1], 10);
33886         }
33887         return 0;
33888     },
33889
33890     findHeaderIndex : function(n){
33891         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33892         return r ? this.getCellIndex(r) : false;
33893     },
33894
33895     findHeaderCell : function(n){
33896         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33897         return r ? r : false;
33898     },
33899
33900     findRowIndex : function(n){
33901         if(!n){
33902             return false;
33903         }
33904         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33905         return r ? r.rowIndex : false;
33906     },
33907
33908     findCellIndex : function(node){
33909         var stop = this.el.dom;
33910         while(node && node != stop){
33911             if(this.findRE.test(node.className)){
33912                 return this.getCellIndex(node);
33913             }
33914             node = node.parentNode;
33915         }
33916         return false;
33917     },
33918
33919     getColumnId : function(index){
33920         return this.cm.getColumnId(index);
33921     },
33922
33923     getSplitters : function()
33924     {
33925         if(this.splitterSelector){
33926            return Roo.DomQuery.select(this.splitterSelector);
33927         }else{
33928             return null;
33929       }
33930     },
33931
33932     getSplitter : function(index){
33933         return this.getSplitters()[index];
33934     },
33935
33936     onRowOver : function(e, t){
33937         var row;
33938         if((row = this.findRowIndex(t)) !== false){
33939             this.getRowComposite(row).addClass("x-grid-row-over");
33940         }
33941     },
33942
33943     onRowOut : function(e, t){
33944         var row;
33945         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33946             this.getRowComposite(row).removeClass("x-grid-row-over");
33947         }
33948     },
33949
33950     renderHeaders : function(){
33951         var cm = this.cm;
33952         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33953         var cb = [], lb = [], sb = [], lsb = [], p = {};
33954         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33955             p.cellId = "x-grid-hd-0-" + i;
33956             p.splitId = "x-grid-csplit-0-" + i;
33957             p.id = cm.getColumnId(i);
33958             p.value = cm.getColumnHeader(i) || "";
33959             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33960             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33961             if(!cm.isLocked(i)){
33962                 cb[cb.length] = ct.apply(p);
33963                 sb[sb.length] = st.apply(p);
33964             }else{
33965                 lb[lb.length] = ct.apply(p);
33966                 lsb[lsb.length] = st.apply(p);
33967             }
33968         }
33969         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33970                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33971     },
33972
33973     updateHeaders : function(){
33974         var html = this.renderHeaders();
33975         this.lockedHd.update(html[0]);
33976         this.mainHd.update(html[1]);
33977     },
33978
33979     /**
33980      * Focuses the specified row.
33981      * @param {Number} row The row index
33982      */
33983     focusRow : function(row)
33984     {
33985         //Roo.log('GridView.focusRow');
33986         var x = this.scroller.dom.scrollLeft;
33987         this.focusCell(row, 0, false);
33988         this.scroller.dom.scrollLeft = x;
33989     },
33990
33991     /**
33992      * Focuses the specified cell.
33993      * @param {Number} row The row index
33994      * @param {Number} col The column index
33995      * @param {Boolean} hscroll false to disable horizontal scrolling
33996      */
33997     focusCell : function(row, col, hscroll)
33998     {
33999         //Roo.log('GridView.focusCell');
34000         var el = this.ensureVisible(row, col, hscroll);
34001         this.focusEl.alignTo(el, "tl-tl");
34002         if(Roo.isGecko){
34003             this.focusEl.focus();
34004         }else{
34005             this.focusEl.focus.defer(1, this.focusEl);
34006         }
34007     },
34008
34009     /**
34010      * Scrolls the specified cell into view
34011      * @param {Number} row The row index
34012      * @param {Number} col The column index
34013      * @param {Boolean} hscroll false to disable horizontal scrolling
34014      */
34015     ensureVisible : function(row, col, hscroll)
34016     {
34017         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34018         //return null; //disable for testing.
34019         if(typeof row != "number"){
34020             row = row.rowIndex;
34021         }
34022         if(row < 0 && row >= this.ds.getCount()){
34023             return  null;
34024         }
34025         col = (col !== undefined ? col : 0);
34026         var cm = this.grid.colModel;
34027         while(cm.isHidden(col)){
34028             col++;
34029         }
34030
34031         var el = this.getCell(row, col);
34032         if(!el){
34033             return null;
34034         }
34035         var c = this.scroller.dom;
34036
34037         var ctop = parseInt(el.offsetTop, 10);
34038         var cleft = parseInt(el.offsetLeft, 10);
34039         var cbot = ctop + el.offsetHeight;
34040         var cright = cleft + el.offsetWidth;
34041         
34042         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34043         var stop = parseInt(c.scrollTop, 10);
34044         var sleft = parseInt(c.scrollLeft, 10);
34045         var sbot = stop + ch;
34046         var sright = sleft + c.clientWidth;
34047         /*
34048         Roo.log('GridView.ensureVisible:' +
34049                 ' ctop:' + ctop +
34050                 ' c.clientHeight:' + c.clientHeight +
34051                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34052                 ' stop:' + stop +
34053                 ' cbot:' + cbot +
34054                 ' sbot:' + sbot +
34055                 ' ch:' + ch  
34056                 );
34057         */
34058         if(ctop < stop){
34059              c.scrollTop = ctop;
34060             //Roo.log("set scrolltop to ctop DISABLE?");
34061         }else if(cbot > sbot){
34062             //Roo.log("set scrolltop to cbot-ch");
34063             c.scrollTop = cbot-ch;
34064         }
34065         
34066         if(hscroll !== false){
34067             if(cleft < sleft){
34068                 c.scrollLeft = cleft;
34069             }else if(cright > sright){
34070                 c.scrollLeft = cright-c.clientWidth;
34071             }
34072         }
34073          
34074         return el;
34075     },
34076
34077     updateColumns : function(){
34078         this.grid.stopEditing();
34079         var cm = this.grid.colModel, colIds = this.getColumnIds();
34080         //var totalWidth = cm.getTotalWidth();
34081         var pos = 0;
34082         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34083             //if(cm.isHidden(i)) continue;
34084             var w = cm.getColumnWidth(i);
34085             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34086             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34087         }
34088         this.updateSplitters();
34089     },
34090
34091     generateRules : function(cm){
34092         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34093         Roo.util.CSS.removeStyleSheet(rulesId);
34094         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34095             var cid = cm.getColumnId(i);
34096             var align = '';
34097             if(cm.config[i].align){
34098                 align = 'text-align:'+cm.config[i].align+';';
34099             }
34100             var hidden = '';
34101             if(cm.isHidden(i)){
34102                 hidden = 'display:none;';
34103             }
34104             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34105             ruleBuf.push(
34106                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34107                     this.hdSelector, cid, " {\n", align, width, "}\n",
34108                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34109                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34110         }
34111         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34112     },
34113
34114     updateSplitters : function(){
34115         var cm = this.cm, s = this.getSplitters();
34116         if(s){ // splitters not created yet
34117             var pos = 0, locked = true;
34118             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34119                 if(cm.isHidden(i)) {
34120                     continue;
34121                 }
34122                 var w = cm.getColumnWidth(i); // make sure it's a number
34123                 if(!cm.isLocked(i) && locked){
34124                     pos = 0;
34125                     locked = false;
34126                 }
34127                 pos += w;
34128                 s[i].style.left = (pos-this.splitOffset) + "px";
34129             }
34130         }
34131     },
34132
34133     handleHiddenChange : function(colModel, colIndex, hidden){
34134         if(hidden){
34135             this.hideColumn(colIndex);
34136         }else{
34137             this.unhideColumn(colIndex);
34138         }
34139     },
34140
34141     hideColumn : function(colIndex){
34142         var cid = this.getColumnId(colIndex);
34143         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34144         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34145         if(Roo.isSafari){
34146             this.updateHeaders();
34147         }
34148         this.updateSplitters();
34149         this.layout();
34150     },
34151
34152     unhideColumn : function(colIndex){
34153         var cid = this.getColumnId(colIndex);
34154         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34155         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34156
34157         if(Roo.isSafari){
34158             this.updateHeaders();
34159         }
34160         this.updateSplitters();
34161         this.layout();
34162     },
34163
34164     insertRows : function(dm, firstRow, lastRow, isUpdate){
34165         if(firstRow == 0 && lastRow == dm.getCount()-1){
34166             this.refresh();
34167         }else{
34168             if(!isUpdate){
34169                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34170             }
34171             var s = this.getScrollState();
34172             var markup = this.renderRows(firstRow, lastRow);
34173             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34174             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34175             this.restoreScroll(s);
34176             if(!isUpdate){
34177                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34178                 this.syncRowHeights(firstRow, lastRow);
34179                 this.stripeRows(firstRow);
34180                 this.layout();
34181             }
34182         }
34183     },
34184
34185     bufferRows : function(markup, target, index){
34186         var before = null, trows = target.rows, tbody = target.tBodies[0];
34187         if(index < trows.length){
34188             before = trows[index];
34189         }
34190         var b = document.createElement("div");
34191         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34192         var rows = b.firstChild.rows;
34193         for(var i = 0, len = rows.length; i < len; i++){
34194             if(before){
34195                 tbody.insertBefore(rows[0], before);
34196             }else{
34197                 tbody.appendChild(rows[0]);
34198             }
34199         }
34200         b.innerHTML = "";
34201         b = null;
34202     },
34203
34204     deleteRows : function(dm, firstRow, lastRow){
34205         if(dm.getRowCount()<1){
34206             this.fireEvent("beforerefresh", this);
34207             this.mainBody.update("");
34208             this.lockedBody.update("");
34209             this.fireEvent("refresh", this);
34210         }else{
34211             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34212             var bt = this.getBodyTable();
34213             var tbody = bt.firstChild;
34214             var rows = bt.rows;
34215             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34216                 tbody.removeChild(rows[firstRow]);
34217             }
34218             this.stripeRows(firstRow);
34219             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34220         }
34221     },
34222
34223     updateRows : function(dataSource, firstRow, lastRow){
34224         var s = this.getScrollState();
34225         this.refresh();
34226         this.restoreScroll(s);
34227     },
34228
34229     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34230         if(!noRefresh){
34231            this.refresh();
34232         }
34233         this.updateHeaderSortState();
34234     },
34235
34236     getScrollState : function(){
34237         
34238         var sb = this.scroller.dom;
34239         return {left: sb.scrollLeft, top: sb.scrollTop};
34240     },
34241
34242     stripeRows : function(startRow){
34243         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34244             return;
34245         }
34246         startRow = startRow || 0;
34247         var rows = this.getBodyTable().rows;
34248         var lrows = this.getLockedTable().rows;
34249         var cls = ' x-grid-row-alt ';
34250         for(var i = startRow, len = rows.length; i < len; i++){
34251             var row = rows[i], lrow = lrows[i];
34252             var isAlt = ((i+1) % 2 == 0);
34253             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34254             if(isAlt == hasAlt){
34255                 continue;
34256             }
34257             if(isAlt){
34258                 row.className += " x-grid-row-alt";
34259             }else{
34260                 row.className = row.className.replace("x-grid-row-alt", "");
34261             }
34262             if(lrow){
34263                 lrow.className = row.className;
34264             }
34265         }
34266     },
34267
34268     restoreScroll : function(state){
34269         //Roo.log('GridView.restoreScroll');
34270         var sb = this.scroller.dom;
34271         sb.scrollLeft = state.left;
34272         sb.scrollTop = state.top;
34273         this.syncScroll();
34274     },
34275
34276     syncScroll : function(){
34277         //Roo.log('GridView.syncScroll');
34278         var sb = this.scroller.dom;
34279         var sh = this.mainHd.dom;
34280         var bs = this.mainBody.dom;
34281         var lv = this.lockedBody.dom;
34282         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34283         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34284     },
34285
34286     handleScroll : function(e){
34287         this.syncScroll();
34288         var sb = this.scroller.dom;
34289         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34290         e.stopEvent();
34291     },
34292
34293     handleWheel : function(e){
34294         var d = e.getWheelDelta();
34295         this.scroller.dom.scrollTop -= d*22;
34296         // set this here to prevent jumpy scrolling on large tables
34297         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34298         e.stopEvent();
34299     },
34300
34301     renderRows : function(startRow, endRow){
34302         // pull in all the crap needed to render rows
34303         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34304         var colCount = cm.getColumnCount();
34305
34306         if(ds.getCount() < 1){
34307             return ["", ""];
34308         }
34309
34310         // build a map for all the columns
34311         var cs = [];
34312         for(var i = 0; i < colCount; i++){
34313             var name = cm.getDataIndex(i);
34314             cs[i] = {
34315                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34316                 renderer : cm.getRenderer(i),
34317                 id : cm.getColumnId(i),
34318                 locked : cm.isLocked(i),
34319                 has_editor : cm.isCellEditable(i)
34320             };
34321         }
34322
34323         startRow = startRow || 0;
34324         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34325
34326         // records to render
34327         var rs = ds.getRange(startRow, endRow);
34328
34329         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34330     },
34331
34332     // As much as I hate to duplicate code, this was branched because FireFox really hates
34333     // [].join("") on strings. The performance difference was substantial enough to
34334     // branch this function
34335     doRender : Roo.isGecko ?
34336             function(cs, rs, ds, startRow, colCount, stripe){
34337                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34338                 // buffers
34339                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34340                 
34341                 var hasListener = this.grid.hasListener('rowclass');
34342                 var rowcfg = {};
34343                 for(var j = 0, len = rs.length; j < len; j++){
34344                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34345                     for(var i = 0; i < colCount; i++){
34346                         c = cs[i];
34347                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34348                         p.id = c.id;
34349                         p.css = p.attr = "";
34350                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34351                         if(p.value == undefined || p.value === "") {
34352                             p.value = "&#160;";
34353                         }
34354                         if(c.has_editor){
34355                             p.css += ' x-grid-editable-cell';
34356                         }
34357                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34358                             p.css +=  ' x-grid-dirty-cell';
34359                         }
34360                         var markup = ct.apply(p);
34361                         if(!c.locked){
34362                             cb+= markup;
34363                         }else{
34364                             lcb+= markup;
34365                         }
34366                     }
34367                     var alt = [];
34368                     if(stripe && ((rowIndex+1) % 2 == 0)){
34369                         alt.push("x-grid-row-alt")
34370                     }
34371                     if(r.dirty){
34372                         alt.push(  " x-grid-dirty-row");
34373                     }
34374                     rp.cells = lcb;
34375                     if(this.getRowClass){
34376                         alt.push(this.getRowClass(r, rowIndex));
34377                     }
34378                     if (hasListener) {
34379                         rowcfg = {
34380                              
34381                             record: r,
34382                             rowIndex : rowIndex,
34383                             rowClass : ''
34384                         };
34385                         this.grid.fireEvent('rowclass', this, rowcfg);
34386                         alt.push(rowcfg.rowClass);
34387                     }
34388                     rp.alt = alt.join(" ");
34389                     lbuf+= rt.apply(rp);
34390                     rp.cells = cb;
34391                     buf+=  rt.apply(rp);
34392                 }
34393                 return [lbuf, buf];
34394             } :
34395             function(cs, rs, ds, startRow, colCount, stripe){
34396                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34397                 // buffers
34398                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34399                 var hasListener = this.grid.hasListener('rowclass');
34400  
34401                 var rowcfg = {};
34402                 for(var j = 0, len = rs.length; j < len; j++){
34403                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34404                     for(var i = 0; i < colCount; i++){
34405                         c = cs[i];
34406                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34407                         p.id = c.id;
34408                         p.css = p.attr = "";
34409                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34410                         if(p.value == undefined || p.value === "") {
34411                             p.value = "&#160;";
34412                         }
34413                         //Roo.log(c);
34414                          if(c.has_editor){
34415                             p.css += ' x-grid-editable-cell';
34416                         }
34417                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34418                             p.css += ' x-grid-dirty-cell' 
34419                         }
34420                         
34421                         var markup = ct.apply(p);
34422                         if(!c.locked){
34423                             cb[cb.length] = markup;
34424                         }else{
34425                             lcb[lcb.length] = markup;
34426                         }
34427                     }
34428                     var alt = [];
34429                     if(stripe && ((rowIndex+1) % 2 == 0)){
34430                         alt.push( "x-grid-row-alt");
34431                     }
34432                     if(r.dirty){
34433                         alt.push(" x-grid-dirty-row");
34434                     }
34435                     rp.cells = lcb;
34436                     if(this.getRowClass){
34437                         alt.push( this.getRowClass(r, rowIndex));
34438                     }
34439                     if (hasListener) {
34440                         rowcfg = {
34441                              
34442                             record: r,
34443                             rowIndex : rowIndex,
34444                             rowClass : ''
34445                         };
34446                         this.grid.fireEvent('rowclass', this, rowcfg);
34447                         alt.push(rowcfg.rowClass);
34448                     }
34449                     
34450                     rp.alt = alt.join(" ");
34451                     rp.cells = lcb.join("");
34452                     lbuf[lbuf.length] = rt.apply(rp);
34453                     rp.cells = cb.join("");
34454                     buf[buf.length] =  rt.apply(rp);
34455                 }
34456                 return [lbuf.join(""), buf.join("")];
34457             },
34458
34459     renderBody : function(){
34460         var markup = this.renderRows();
34461         var bt = this.templates.body;
34462         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34463     },
34464
34465     /**
34466      * Refreshes the grid
34467      * @param {Boolean} headersToo
34468      */
34469     refresh : function(headersToo){
34470         this.fireEvent("beforerefresh", this);
34471         this.grid.stopEditing();
34472         var result = this.renderBody();
34473         this.lockedBody.update(result[0]);
34474         this.mainBody.update(result[1]);
34475         if(headersToo === true){
34476             this.updateHeaders();
34477             this.updateColumns();
34478             this.updateSplitters();
34479             this.updateHeaderSortState();
34480         }
34481         this.syncRowHeights();
34482         this.layout();
34483         this.fireEvent("refresh", this);
34484     },
34485
34486     handleColumnMove : function(cm, oldIndex, newIndex){
34487         this.indexMap = null;
34488         var s = this.getScrollState();
34489         this.refresh(true);
34490         this.restoreScroll(s);
34491         this.afterMove(newIndex);
34492     },
34493
34494     afterMove : function(colIndex){
34495         if(this.enableMoveAnim && Roo.enableFx){
34496             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34497         }
34498         // if multisort - fix sortOrder, and reload..
34499         if (this.grid.dataSource.multiSort) {
34500             // the we can call sort again..
34501             var dm = this.grid.dataSource;
34502             var cm = this.grid.colModel;
34503             var so = [];
34504             for(var i = 0; i < cm.config.length; i++ ) {
34505                 
34506                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34507                     continue; // dont' bother, it's not in sort list or being set.
34508                 }
34509                 
34510                 so.push(cm.config[i].dataIndex);
34511             };
34512             dm.sortOrder = so;
34513             dm.load(dm.lastOptions);
34514             
34515             
34516         }
34517         
34518     },
34519
34520     updateCell : function(dm, rowIndex, dataIndex){
34521         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34522         if(typeof colIndex == "undefined"){ // not present in grid
34523             return;
34524         }
34525         var cm = this.grid.colModel;
34526         var cell = this.getCell(rowIndex, colIndex);
34527         var cellText = this.getCellText(rowIndex, colIndex);
34528
34529         var p = {
34530             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34531             id : cm.getColumnId(colIndex),
34532             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34533         };
34534         var renderer = cm.getRenderer(colIndex);
34535         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34536         if(typeof val == "undefined" || val === "") {
34537             val = "&#160;";
34538         }
34539         cellText.innerHTML = val;
34540         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34541         this.syncRowHeights(rowIndex, rowIndex);
34542     },
34543
34544     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34545         var maxWidth = 0;
34546         if(this.grid.autoSizeHeaders){
34547             var h = this.getHeaderCellMeasure(colIndex);
34548             maxWidth = Math.max(maxWidth, h.scrollWidth);
34549         }
34550         var tb, index;
34551         if(this.cm.isLocked(colIndex)){
34552             tb = this.getLockedTable();
34553             index = colIndex;
34554         }else{
34555             tb = this.getBodyTable();
34556             index = colIndex - this.cm.getLockedCount();
34557         }
34558         if(tb && tb.rows){
34559             var rows = tb.rows;
34560             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34561             for(var i = 0; i < stopIndex; i++){
34562                 var cell = rows[i].childNodes[index].firstChild;
34563                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34564             }
34565         }
34566         return maxWidth + /*margin for error in IE*/ 5;
34567     },
34568     /**
34569      * Autofit a column to its content.
34570      * @param {Number} colIndex
34571      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34572      */
34573      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34574          if(this.cm.isHidden(colIndex)){
34575              return; // can't calc a hidden column
34576          }
34577         if(forceMinSize){
34578             var cid = this.cm.getColumnId(colIndex);
34579             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34580            if(this.grid.autoSizeHeaders){
34581                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34582            }
34583         }
34584         var newWidth = this.calcColumnWidth(colIndex);
34585         this.cm.setColumnWidth(colIndex,
34586             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34587         if(!suppressEvent){
34588             this.grid.fireEvent("columnresize", colIndex, newWidth);
34589         }
34590     },
34591
34592     /**
34593      * Autofits all columns to their content and then expands to fit any extra space in the grid
34594      */
34595      autoSizeColumns : function(){
34596         var cm = this.grid.colModel;
34597         var colCount = cm.getColumnCount();
34598         for(var i = 0; i < colCount; i++){
34599             this.autoSizeColumn(i, true, true);
34600         }
34601         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34602             this.fitColumns();
34603         }else{
34604             this.updateColumns();
34605             this.layout();
34606         }
34607     },
34608
34609     /**
34610      * Autofits all columns to the grid's width proportionate with their current size
34611      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34612      */
34613     fitColumns : function(reserveScrollSpace){
34614         var cm = this.grid.colModel;
34615         var colCount = cm.getColumnCount();
34616         var cols = [];
34617         var width = 0;
34618         var i, w;
34619         for (i = 0; i < colCount; i++){
34620             if(!cm.isHidden(i) && !cm.isFixed(i)){
34621                 w = cm.getColumnWidth(i);
34622                 cols.push(i);
34623                 cols.push(w);
34624                 width += w;
34625             }
34626         }
34627         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34628         if(reserveScrollSpace){
34629             avail -= 17;
34630         }
34631         var frac = (avail - cm.getTotalWidth())/width;
34632         while (cols.length){
34633             w = cols.pop();
34634             i = cols.pop();
34635             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34636         }
34637         this.updateColumns();
34638         this.layout();
34639     },
34640
34641     onRowSelect : function(rowIndex){
34642         var row = this.getRowComposite(rowIndex);
34643         row.addClass("x-grid-row-selected");
34644     },
34645
34646     onRowDeselect : function(rowIndex){
34647         var row = this.getRowComposite(rowIndex);
34648         row.removeClass("x-grid-row-selected");
34649     },
34650
34651     onCellSelect : function(row, col){
34652         var cell = this.getCell(row, col);
34653         if(cell){
34654             Roo.fly(cell).addClass("x-grid-cell-selected");
34655         }
34656     },
34657
34658     onCellDeselect : function(row, col){
34659         var cell = this.getCell(row, col);
34660         if(cell){
34661             Roo.fly(cell).removeClass("x-grid-cell-selected");
34662         }
34663     },
34664
34665     updateHeaderSortState : function(){
34666         
34667         // sort state can be single { field: xxx, direction : yyy}
34668         // or   { xxx=>ASC , yyy : DESC ..... }
34669         
34670         var mstate = {};
34671         if (!this.ds.multiSort) { 
34672             var state = this.ds.getSortState();
34673             if(!state){
34674                 return;
34675             }
34676             mstate[state.field] = state.direction;
34677             // FIXME... - this is not used here.. but might be elsewhere..
34678             this.sortState = state;
34679             
34680         } else {
34681             mstate = this.ds.sortToggle;
34682         }
34683         //remove existing sort classes..
34684         
34685         var sc = this.sortClasses;
34686         var hds = this.el.select(this.headerSelector).removeClass(sc);
34687         
34688         for(var f in mstate) {
34689         
34690             var sortColumn = this.cm.findColumnIndex(f);
34691             
34692             if(sortColumn != -1){
34693                 var sortDir = mstate[f];        
34694                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34695             }
34696         }
34697         
34698          
34699         
34700     },
34701
34702
34703     handleHeaderClick : function(g, index,e){
34704         
34705         Roo.log("header click");
34706         
34707         if (Roo.isTouch) {
34708             // touch events on header are handled by context
34709             this.handleHdCtx(g,index,e);
34710             return;
34711         }
34712         
34713         
34714         if(this.headersDisabled){
34715             return;
34716         }
34717         var dm = g.dataSource, cm = g.colModel;
34718         if(!cm.isSortable(index)){
34719             return;
34720         }
34721         g.stopEditing();
34722         
34723         if (dm.multiSort) {
34724             // update the sortOrder
34725             var so = [];
34726             for(var i = 0; i < cm.config.length; i++ ) {
34727                 
34728                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34729                     continue; // dont' bother, it's not in sort list or being set.
34730                 }
34731                 
34732                 so.push(cm.config[i].dataIndex);
34733             };
34734             dm.sortOrder = so;
34735         }
34736         
34737         
34738         dm.sort(cm.getDataIndex(index));
34739     },
34740
34741
34742     destroy : function(){
34743         if(this.colMenu){
34744             this.colMenu.removeAll();
34745             Roo.menu.MenuMgr.unregister(this.colMenu);
34746             this.colMenu.getEl().remove();
34747             delete this.colMenu;
34748         }
34749         if(this.hmenu){
34750             this.hmenu.removeAll();
34751             Roo.menu.MenuMgr.unregister(this.hmenu);
34752             this.hmenu.getEl().remove();
34753             delete this.hmenu;
34754         }
34755         if(this.grid.enableColumnMove){
34756             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34757             if(dds){
34758                 for(var dd in dds){
34759                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34760                         var elid = dds[dd].dragElId;
34761                         dds[dd].unreg();
34762                         Roo.get(elid).remove();
34763                     } else if(dds[dd].config.isTarget){
34764                         dds[dd].proxyTop.remove();
34765                         dds[dd].proxyBottom.remove();
34766                         dds[dd].unreg();
34767                     }
34768                     if(Roo.dd.DDM.locationCache[dd]){
34769                         delete Roo.dd.DDM.locationCache[dd];
34770                     }
34771                 }
34772                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34773             }
34774         }
34775         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34776         this.bind(null, null);
34777         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34778     },
34779
34780     handleLockChange : function(){
34781         this.refresh(true);
34782     },
34783
34784     onDenyColumnLock : function(){
34785
34786     },
34787
34788     onDenyColumnHide : function(){
34789
34790     },
34791
34792     handleHdMenuClick : function(item){
34793         var index = this.hdCtxIndex;
34794         var cm = this.cm, ds = this.ds;
34795         switch(item.id){
34796             case "asc":
34797                 ds.sort(cm.getDataIndex(index), "ASC");
34798                 break;
34799             case "desc":
34800                 ds.sort(cm.getDataIndex(index), "DESC");
34801                 break;
34802             case "lock":
34803                 var lc = cm.getLockedCount();
34804                 if(cm.getColumnCount(true) <= lc+1){
34805                     this.onDenyColumnLock();
34806                     return;
34807                 }
34808                 if(lc != index){
34809                     cm.setLocked(index, true, true);
34810                     cm.moveColumn(index, lc);
34811                     this.grid.fireEvent("columnmove", index, lc);
34812                 }else{
34813                     cm.setLocked(index, true);
34814                 }
34815             break;
34816             case "unlock":
34817                 var lc = cm.getLockedCount();
34818                 if((lc-1) != index){
34819                     cm.setLocked(index, false, true);
34820                     cm.moveColumn(index, lc-1);
34821                     this.grid.fireEvent("columnmove", index, lc-1);
34822                 }else{
34823                     cm.setLocked(index, false);
34824                 }
34825             break;
34826             case 'wider': // used to expand cols on touch..
34827             case 'narrow':
34828                 var cw = cm.getColumnWidth(index);
34829                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34830                 cw = Math.max(0, cw);
34831                 cw = Math.min(cw,4000);
34832                 cm.setColumnWidth(index, cw);
34833                 break;
34834                 
34835             default:
34836                 index = cm.getIndexById(item.id.substr(4));
34837                 if(index != -1){
34838                     if(item.checked && cm.getColumnCount(true) <= 1){
34839                         this.onDenyColumnHide();
34840                         return false;
34841                     }
34842                     cm.setHidden(index, item.checked);
34843                 }
34844         }
34845         return true;
34846     },
34847
34848     beforeColMenuShow : function(){
34849         var cm = this.cm,  colCount = cm.getColumnCount();
34850         this.colMenu.removeAll();
34851         for(var i = 0; i < colCount; i++){
34852             this.colMenu.add(new Roo.menu.CheckItem({
34853                 id: "col-"+cm.getColumnId(i),
34854                 text: cm.getColumnHeader(i),
34855                 checked: !cm.isHidden(i),
34856                 hideOnClick:false
34857             }));
34858         }
34859     },
34860
34861     handleHdCtx : function(g, index, e){
34862         e.stopEvent();
34863         var hd = this.getHeaderCell(index);
34864         this.hdCtxIndex = index;
34865         var ms = this.hmenu.items, cm = this.cm;
34866         ms.get("asc").setDisabled(!cm.isSortable(index));
34867         ms.get("desc").setDisabled(!cm.isSortable(index));
34868         if(this.grid.enableColLock !== false){
34869             ms.get("lock").setDisabled(cm.isLocked(index));
34870             ms.get("unlock").setDisabled(!cm.isLocked(index));
34871         }
34872         this.hmenu.show(hd, "tl-bl");
34873     },
34874
34875     handleHdOver : function(e){
34876         var hd = this.findHeaderCell(e.getTarget());
34877         if(hd && !this.headersDisabled){
34878             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34879                this.fly(hd).addClass("x-grid-hd-over");
34880             }
34881         }
34882     },
34883
34884     handleHdOut : function(e){
34885         var hd = this.findHeaderCell(e.getTarget());
34886         if(hd){
34887             this.fly(hd).removeClass("x-grid-hd-over");
34888         }
34889     },
34890
34891     handleSplitDblClick : function(e, t){
34892         var i = this.getCellIndex(t);
34893         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34894             this.autoSizeColumn(i, true);
34895             this.layout();
34896         }
34897     },
34898
34899     render : function(){
34900
34901         var cm = this.cm;
34902         var colCount = cm.getColumnCount();
34903
34904         if(this.grid.monitorWindowResize === true){
34905             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34906         }
34907         var header = this.renderHeaders();
34908         var body = this.templates.body.apply({rows:""});
34909         var html = this.templates.master.apply({
34910             lockedBody: body,
34911             body: body,
34912             lockedHeader: header[0],
34913             header: header[1]
34914         });
34915
34916         //this.updateColumns();
34917
34918         this.grid.getGridEl().dom.innerHTML = html;
34919
34920         this.initElements();
34921         
34922         // a kludge to fix the random scolling effect in webkit
34923         this.el.on("scroll", function() {
34924             this.el.dom.scrollTop=0; // hopefully not recursive..
34925         },this);
34926
34927         this.scroller.on("scroll", this.handleScroll, this);
34928         this.lockedBody.on("mousewheel", this.handleWheel, this);
34929         this.mainBody.on("mousewheel", this.handleWheel, this);
34930
34931         this.mainHd.on("mouseover", this.handleHdOver, this);
34932         this.mainHd.on("mouseout", this.handleHdOut, this);
34933         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34934                 {delegate: "."+this.splitClass});
34935
34936         this.lockedHd.on("mouseover", this.handleHdOver, this);
34937         this.lockedHd.on("mouseout", this.handleHdOut, this);
34938         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34939                 {delegate: "."+this.splitClass});
34940
34941         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34942             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34943         }
34944
34945         this.updateSplitters();
34946
34947         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34948             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34949             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34950         }
34951
34952         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34953             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34954             this.hmenu.add(
34955                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34956                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34957             );
34958             if(this.grid.enableColLock !== false){
34959                 this.hmenu.add('-',
34960                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34961                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34962                 );
34963             }
34964             if (Roo.isTouch) {
34965                  this.hmenu.add('-',
34966                     {id:"wider", text: this.columnsWiderText},
34967                     {id:"narrow", text: this.columnsNarrowText }
34968                 );
34969                 
34970                  
34971             }
34972             
34973             if(this.grid.enableColumnHide !== false){
34974
34975                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34976                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34977                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34978
34979                 this.hmenu.add('-',
34980                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34981                 );
34982             }
34983             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34984
34985             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34986         }
34987
34988         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34989             this.dd = new Roo.grid.GridDragZone(this.grid, {
34990                 ddGroup : this.grid.ddGroup || 'GridDD'
34991             });
34992             
34993         }
34994
34995         /*
34996         for(var i = 0; i < colCount; i++){
34997             if(cm.isHidden(i)){
34998                 this.hideColumn(i);
34999             }
35000             if(cm.config[i].align){
35001                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35002                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35003             }
35004         }*/
35005         
35006         this.updateHeaderSortState();
35007
35008         this.beforeInitialResize();
35009         this.layout(true);
35010
35011         // two part rendering gives faster view to the user
35012         this.renderPhase2.defer(1, this);
35013     },
35014
35015     renderPhase2 : function(){
35016         // render the rows now
35017         this.refresh();
35018         if(this.grid.autoSizeColumns){
35019             this.autoSizeColumns();
35020         }
35021     },
35022
35023     beforeInitialResize : function(){
35024
35025     },
35026
35027     onColumnSplitterMoved : function(i, w){
35028         this.userResized = true;
35029         var cm = this.grid.colModel;
35030         cm.setColumnWidth(i, w, true);
35031         var cid = cm.getColumnId(i);
35032         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35033         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35034         this.updateSplitters();
35035         this.layout();
35036         this.grid.fireEvent("columnresize", i, w);
35037     },
35038
35039     syncRowHeights : function(startIndex, endIndex){
35040         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35041             startIndex = startIndex || 0;
35042             var mrows = this.getBodyTable().rows;
35043             var lrows = this.getLockedTable().rows;
35044             var len = mrows.length-1;
35045             endIndex = Math.min(endIndex || len, len);
35046             for(var i = startIndex; i <= endIndex; i++){
35047                 var m = mrows[i], l = lrows[i];
35048                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35049                 m.style.height = l.style.height = h + "px";
35050             }
35051         }
35052     },
35053
35054     layout : function(initialRender, is2ndPass){
35055         var g = this.grid;
35056         var auto = g.autoHeight;
35057         var scrollOffset = 16;
35058         var c = g.getGridEl(), cm = this.cm,
35059                 expandCol = g.autoExpandColumn,
35060                 gv = this;
35061         //c.beginMeasure();
35062
35063         if(!c.dom.offsetWidth){ // display:none?
35064             if(initialRender){
35065                 this.lockedWrap.show();
35066                 this.mainWrap.show();
35067             }
35068             return;
35069         }
35070
35071         var hasLock = this.cm.isLocked(0);
35072
35073         var tbh = this.headerPanel.getHeight();
35074         var bbh = this.footerPanel.getHeight();
35075
35076         if(auto){
35077             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35078             var newHeight = ch + c.getBorderWidth("tb");
35079             if(g.maxHeight){
35080                 newHeight = Math.min(g.maxHeight, newHeight);
35081             }
35082             c.setHeight(newHeight);
35083         }
35084
35085         if(g.autoWidth){
35086             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35087         }
35088
35089         var s = this.scroller;
35090
35091         var csize = c.getSize(true);
35092
35093         this.el.setSize(csize.width, csize.height);
35094
35095         this.headerPanel.setWidth(csize.width);
35096         this.footerPanel.setWidth(csize.width);
35097
35098         var hdHeight = this.mainHd.getHeight();
35099         var vw = csize.width;
35100         var vh = csize.height - (tbh + bbh);
35101
35102         s.setSize(vw, vh);
35103
35104         var bt = this.getBodyTable();
35105         
35106         if(cm.getLockedCount() == cm.config.length){
35107             bt = this.getLockedTable();
35108         }
35109         
35110         var ltWidth = hasLock ?
35111                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35112
35113         var scrollHeight = bt.offsetHeight;
35114         var scrollWidth = ltWidth + bt.offsetWidth;
35115         var vscroll = false, hscroll = false;
35116
35117         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35118
35119         var lw = this.lockedWrap, mw = this.mainWrap;
35120         var lb = this.lockedBody, mb = this.mainBody;
35121
35122         setTimeout(function(){
35123             var t = s.dom.offsetTop;
35124             var w = s.dom.clientWidth,
35125                 h = s.dom.clientHeight;
35126
35127             lw.setTop(t);
35128             lw.setSize(ltWidth, h);
35129
35130             mw.setLeftTop(ltWidth, t);
35131             mw.setSize(w-ltWidth, h);
35132
35133             lb.setHeight(h-hdHeight);
35134             mb.setHeight(h-hdHeight);
35135
35136             if(is2ndPass !== true && !gv.userResized && expandCol){
35137                 // high speed resize without full column calculation
35138                 
35139                 var ci = cm.getIndexById(expandCol);
35140                 if (ci < 0) {
35141                     ci = cm.findColumnIndex(expandCol);
35142                 }
35143                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35144                 var expandId = cm.getColumnId(ci);
35145                 var  tw = cm.getTotalWidth(false);
35146                 var currentWidth = cm.getColumnWidth(ci);
35147                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35148                 if(currentWidth != cw){
35149                     cm.setColumnWidth(ci, cw, true);
35150                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35151                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35152                     gv.updateSplitters();
35153                     gv.layout(false, true);
35154                 }
35155             }
35156
35157             if(initialRender){
35158                 lw.show();
35159                 mw.show();
35160             }
35161             //c.endMeasure();
35162         }, 10);
35163     },
35164
35165     onWindowResize : function(){
35166         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35167             return;
35168         }
35169         this.layout();
35170     },
35171
35172     appendFooter : function(parentEl){
35173         return null;
35174     },
35175
35176     sortAscText : "Sort Ascending",
35177     sortDescText : "Sort Descending",
35178     lockText : "Lock Column",
35179     unlockText : "Unlock Column",
35180     columnsText : "Columns",
35181  
35182     columnsWiderText : "Wider",
35183     columnsNarrowText : "Thinner"
35184 });
35185
35186
35187 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35188     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35189     this.proxy.el.addClass('x-grid3-col-dd');
35190 };
35191
35192 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35193     handleMouseDown : function(e){
35194
35195     },
35196
35197     callHandleMouseDown : function(e){
35198         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35199     }
35200 });
35201 /*
35202  * Based on:
35203  * Ext JS Library 1.1.1
35204  * Copyright(c) 2006-2007, Ext JS, LLC.
35205  *
35206  * Originally Released Under LGPL - original licence link has changed is not relivant.
35207  *
35208  * Fork - LGPL
35209  * <script type="text/javascript">
35210  */
35211  
35212 // private
35213 // This is a support class used internally by the Grid components
35214 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35215     this.grid = grid;
35216     this.view = grid.getView();
35217     this.proxy = this.view.resizeProxy;
35218     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35219         "gridSplitters" + this.grid.getGridEl().id, {
35220         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35221     });
35222     this.setHandleElId(Roo.id(hd));
35223     this.setOuterHandleElId(Roo.id(hd2));
35224     this.scroll = false;
35225 };
35226 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35227     fly: Roo.Element.fly,
35228
35229     b4StartDrag : function(x, y){
35230         this.view.headersDisabled = true;
35231         this.proxy.setHeight(this.view.mainWrap.getHeight());
35232         var w = this.cm.getColumnWidth(this.cellIndex);
35233         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35234         this.resetConstraints();
35235         this.setXConstraint(minw, 1000);
35236         this.setYConstraint(0, 0);
35237         this.minX = x - minw;
35238         this.maxX = x + 1000;
35239         this.startPos = x;
35240         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35241     },
35242
35243
35244     handleMouseDown : function(e){
35245         ev = Roo.EventObject.setEvent(e);
35246         var t = this.fly(ev.getTarget());
35247         if(t.hasClass("x-grid-split")){
35248             this.cellIndex = this.view.getCellIndex(t.dom);
35249             this.split = t.dom;
35250             this.cm = this.grid.colModel;
35251             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35252                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35253             }
35254         }
35255     },
35256
35257     endDrag : function(e){
35258         this.view.headersDisabled = false;
35259         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35260         var diff = endX - this.startPos;
35261         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35262     },
35263
35264     autoOffset : function(){
35265         this.setDelta(0,0);
35266     }
35267 });/*
35268  * Based on:
35269  * Ext JS Library 1.1.1
35270  * Copyright(c) 2006-2007, Ext JS, LLC.
35271  *
35272  * Originally Released Under LGPL - original licence link has changed is not relivant.
35273  *
35274  * Fork - LGPL
35275  * <script type="text/javascript">
35276  */
35277  
35278 // private
35279 // This is a support class used internally by the Grid components
35280 Roo.grid.GridDragZone = function(grid, config){
35281     this.view = grid.getView();
35282     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35283     if(this.view.lockedBody){
35284         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35285         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35286     }
35287     this.scroll = false;
35288     this.grid = grid;
35289     this.ddel = document.createElement('div');
35290     this.ddel.className = 'x-grid-dd-wrap';
35291 };
35292
35293 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35294     ddGroup : "GridDD",
35295
35296     getDragData : function(e){
35297         var t = Roo.lib.Event.getTarget(e);
35298         var rowIndex = this.view.findRowIndex(t);
35299         var sm = this.grid.selModel;
35300             
35301         //Roo.log(rowIndex);
35302         
35303         if (sm.getSelectedCell) {
35304             // cell selection..
35305             if (!sm.getSelectedCell()) {
35306                 return false;
35307             }
35308             if (rowIndex != sm.getSelectedCell()[0]) {
35309                 return false;
35310             }
35311         
35312         }
35313         
35314         if(rowIndex !== false){
35315             
35316             // if editorgrid.. 
35317             
35318             
35319             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35320                
35321             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35322               //  
35323             //}
35324             if (e.hasModifier()){
35325                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35326             }
35327             
35328             Roo.log("getDragData");
35329             
35330             return {
35331                 grid: this.grid,
35332                 ddel: this.ddel,
35333                 rowIndex: rowIndex,
35334                 selections:sm.getSelections ? sm.getSelections() : (
35335                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35336                 )
35337             };
35338         }
35339         return false;
35340     },
35341
35342     onInitDrag : function(e){
35343         var data = this.dragData;
35344         this.ddel.innerHTML = this.grid.getDragDropText();
35345         this.proxy.update(this.ddel);
35346         // fire start drag?
35347     },
35348
35349     afterRepair : function(){
35350         this.dragging = false;
35351     },
35352
35353     getRepairXY : function(e, data){
35354         return false;
35355     },
35356
35357     onEndDrag : function(data, e){
35358         // fire end drag?
35359     },
35360
35361     onValidDrop : function(dd, e, id){
35362         // fire drag drop?
35363         this.hideProxy();
35364     },
35365
35366     beforeInvalidDrop : function(e, id){
35367
35368     }
35369 });/*
35370  * Based on:
35371  * Ext JS Library 1.1.1
35372  * Copyright(c) 2006-2007, Ext JS, LLC.
35373  *
35374  * Originally Released Under LGPL - original licence link has changed is not relivant.
35375  *
35376  * Fork - LGPL
35377  * <script type="text/javascript">
35378  */
35379  
35380
35381 /**
35382  * @class Roo.grid.ColumnModel
35383  * @extends Roo.util.Observable
35384  * This is the default implementation of a ColumnModel used by the Grid. It defines
35385  * the columns in the grid.
35386  * <br>Usage:<br>
35387  <pre><code>
35388  var colModel = new Roo.grid.ColumnModel([
35389         {header: "Ticker", width: 60, sortable: true, locked: true},
35390         {header: "Company Name", width: 150, sortable: true},
35391         {header: "Market Cap.", width: 100, sortable: true},
35392         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35393         {header: "Employees", width: 100, sortable: true, resizable: false}
35394  ]);
35395  </code></pre>
35396  * <p>
35397  
35398  * The config options listed for this class are options which may appear in each
35399  * individual column definition.
35400  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35401  * @constructor
35402  * @param {Object} config An Array of column config objects. See this class's
35403  * config objects for details.
35404 */
35405 Roo.grid.ColumnModel = function(config){
35406         /**
35407      * The config passed into the constructor
35408      */
35409     this.config = config;
35410     this.lookup = {};
35411
35412     // if no id, create one
35413     // if the column does not have a dataIndex mapping,
35414     // map it to the order it is in the config
35415     for(var i = 0, len = config.length; i < len; i++){
35416         var c = config[i];
35417         if(typeof c.dataIndex == "undefined"){
35418             c.dataIndex = i;
35419         }
35420         if(typeof c.renderer == "string"){
35421             c.renderer = Roo.util.Format[c.renderer];
35422         }
35423         if(typeof c.id == "undefined"){
35424             c.id = Roo.id();
35425         }
35426         if(c.editor && c.editor.xtype){
35427             c.editor  = Roo.factory(c.editor, Roo.grid);
35428         }
35429         if(c.editor && c.editor.isFormField){
35430             c.editor = new Roo.grid.GridEditor(c.editor);
35431         }
35432         this.lookup[c.id] = c;
35433     }
35434
35435     /**
35436      * The width of columns which have no width specified (defaults to 100)
35437      * @type Number
35438      */
35439     this.defaultWidth = 100;
35440
35441     /**
35442      * Default sortable of columns which have no sortable specified (defaults to false)
35443      * @type Boolean
35444      */
35445     this.defaultSortable = false;
35446
35447     this.addEvents({
35448         /**
35449              * @event widthchange
35450              * Fires when the width of a column changes.
35451              * @param {ColumnModel} this
35452              * @param {Number} columnIndex The column index
35453              * @param {Number} newWidth The new width
35454              */
35455             "widthchange": true,
35456         /**
35457              * @event headerchange
35458              * Fires when the text of a header changes.
35459              * @param {ColumnModel} this
35460              * @param {Number} columnIndex The column index
35461              * @param {Number} newText The new header text
35462              */
35463             "headerchange": true,
35464         /**
35465              * @event hiddenchange
35466              * Fires when a column is hidden or "unhidden".
35467              * @param {ColumnModel} this
35468              * @param {Number} columnIndex The column index
35469              * @param {Boolean} hidden true if hidden, false otherwise
35470              */
35471             "hiddenchange": true,
35472             /**
35473          * @event columnmoved
35474          * Fires when a column is moved.
35475          * @param {ColumnModel} this
35476          * @param {Number} oldIndex
35477          * @param {Number} newIndex
35478          */
35479         "columnmoved" : true,
35480         /**
35481          * @event columlockchange
35482          * Fires when a column's locked state is changed
35483          * @param {ColumnModel} this
35484          * @param {Number} colIndex
35485          * @param {Boolean} locked true if locked
35486          */
35487         "columnlockchange" : true
35488     });
35489     Roo.grid.ColumnModel.superclass.constructor.call(this);
35490 };
35491 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35492     /**
35493      * @cfg {String} header The header text to display in the Grid view.
35494      */
35495     /**
35496      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35497      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35498      * specified, the column's index is used as an index into the Record's data Array.
35499      */
35500     /**
35501      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35502      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35503      */
35504     /**
35505      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35506      * Defaults to the value of the {@link #defaultSortable} property.
35507      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35508      */
35509     /**
35510      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35511      */
35512     /**
35513      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35514      */
35515     /**
35516      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35517      */
35518     /**
35519      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35520      */
35521     /**
35522      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35523      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35524      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35525      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35526      */
35527        /**
35528      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35529      */
35530     /**
35531      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35532      */
35533     /**
35534      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35535      */
35536     /**
35537      * @cfg {String} cursor (Optional)
35538      */
35539     /**
35540      * @cfg {String} tooltip (Optional)
35541      */
35542     /**
35543      * @cfg {Number} xs (Optional)
35544      */
35545     /**
35546      * @cfg {Number} sm (Optional)
35547      */
35548     /**
35549      * @cfg {Number} md (Optional)
35550      */
35551     /**
35552      * @cfg {Number} lg (Optional)
35553      */
35554     /**
35555      * Returns the id of the column at the specified index.
35556      * @param {Number} index The column index
35557      * @return {String} the id
35558      */
35559     getColumnId : function(index){
35560         return this.config[index].id;
35561     },
35562
35563     /**
35564      * Returns the column for a specified id.
35565      * @param {String} id The column id
35566      * @return {Object} the column
35567      */
35568     getColumnById : function(id){
35569         return this.lookup[id];
35570     },
35571
35572     
35573     /**
35574      * Returns the column for a specified dataIndex.
35575      * @param {String} dataIndex The column dataIndex
35576      * @return {Object|Boolean} the column or false if not found
35577      */
35578     getColumnByDataIndex: function(dataIndex){
35579         var index = this.findColumnIndex(dataIndex);
35580         return index > -1 ? this.config[index] : false;
35581     },
35582     
35583     /**
35584      * Returns the index for a specified column id.
35585      * @param {String} id The column id
35586      * @return {Number} the index, or -1 if not found
35587      */
35588     getIndexById : function(id){
35589         for(var i = 0, len = this.config.length; i < len; i++){
35590             if(this.config[i].id == id){
35591                 return i;
35592             }
35593         }
35594         return -1;
35595     },
35596     
35597     /**
35598      * Returns the index for a specified column dataIndex.
35599      * @param {String} dataIndex The column dataIndex
35600      * @return {Number} the index, or -1 if not found
35601      */
35602     
35603     findColumnIndex : function(dataIndex){
35604         for(var i = 0, len = this.config.length; i < len; i++){
35605             if(this.config[i].dataIndex == dataIndex){
35606                 return i;
35607             }
35608         }
35609         return -1;
35610     },
35611     
35612     
35613     moveColumn : function(oldIndex, newIndex){
35614         var c = this.config[oldIndex];
35615         this.config.splice(oldIndex, 1);
35616         this.config.splice(newIndex, 0, c);
35617         this.dataMap = null;
35618         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35619     },
35620
35621     isLocked : function(colIndex){
35622         return this.config[colIndex].locked === true;
35623     },
35624
35625     setLocked : function(colIndex, value, suppressEvent){
35626         if(this.isLocked(colIndex) == value){
35627             return;
35628         }
35629         this.config[colIndex].locked = value;
35630         if(!suppressEvent){
35631             this.fireEvent("columnlockchange", this, colIndex, value);
35632         }
35633     },
35634
35635     getTotalLockedWidth : function(){
35636         var totalWidth = 0;
35637         for(var i = 0; i < this.config.length; i++){
35638             if(this.isLocked(i) && !this.isHidden(i)){
35639                 this.totalWidth += this.getColumnWidth(i);
35640             }
35641         }
35642         return totalWidth;
35643     },
35644
35645     getLockedCount : function(){
35646         for(var i = 0, len = this.config.length; i < len; i++){
35647             if(!this.isLocked(i)){
35648                 return i;
35649             }
35650         }
35651         
35652         return this.config.length;
35653     },
35654
35655     /**
35656      * Returns the number of columns.
35657      * @return {Number}
35658      */
35659     getColumnCount : function(visibleOnly){
35660         if(visibleOnly === true){
35661             var c = 0;
35662             for(var i = 0, len = this.config.length; i < len; i++){
35663                 if(!this.isHidden(i)){
35664                     c++;
35665                 }
35666             }
35667             return c;
35668         }
35669         return this.config.length;
35670     },
35671
35672     /**
35673      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35674      * @param {Function} fn
35675      * @param {Object} scope (optional)
35676      * @return {Array} result
35677      */
35678     getColumnsBy : function(fn, scope){
35679         var r = [];
35680         for(var i = 0, len = this.config.length; i < len; i++){
35681             var c = this.config[i];
35682             if(fn.call(scope||this, c, i) === true){
35683                 r[r.length] = c;
35684             }
35685         }
35686         return r;
35687     },
35688
35689     /**
35690      * Returns true if the specified column is sortable.
35691      * @param {Number} col The column index
35692      * @return {Boolean}
35693      */
35694     isSortable : function(col){
35695         if(typeof this.config[col].sortable == "undefined"){
35696             return this.defaultSortable;
35697         }
35698         return this.config[col].sortable;
35699     },
35700
35701     /**
35702      * Returns the rendering (formatting) function defined for the column.
35703      * @param {Number} col The column index.
35704      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35705      */
35706     getRenderer : function(col){
35707         if(!this.config[col].renderer){
35708             return Roo.grid.ColumnModel.defaultRenderer;
35709         }
35710         return this.config[col].renderer;
35711     },
35712
35713     /**
35714      * Sets the rendering (formatting) function for a column.
35715      * @param {Number} col The column index
35716      * @param {Function} fn The function to use to process the cell's raw data
35717      * to return HTML markup for the grid view. The render function is called with
35718      * the following parameters:<ul>
35719      * <li>Data value.</li>
35720      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35721      * <li>css A CSS style string to apply to the table cell.</li>
35722      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35723      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35724      * <li>Row index</li>
35725      * <li>Column index</li>
35726      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35727      */
35728     setRenderer : function(col, fn){
35729         this.config[col].renderer = fn;
35730     },
35731
35732     /**
35733      * Returns the width for the specified column.
35734      * @param {Number} col The column index
35735      * @return {Number}
35736      */
35737     getColumnWidth : function(col){
35738         return this.config[col].width * 1 || this.defaultWidth;
35739     },
35740
35741     /**
35742      * Sets the width for a column.
35743      * @param {Number} col The column index
35744      * @param {Number} width The new width
35745      */
35746     setColumnWidth : function(col, width, suppressEvent){
35747         this.config[col].width = width;
35748         this.totalWidth = null;
35749         if(!suppressEvent){
35750              this.fireEvent("widthchange", this, col, width);
35751         }
35752     },
35753
35754     /**
35755      * Returns the total width of all columns.
35756      * @param {Boolean} includeHidden True to include hidden column widths
35757      * @return {Number}
35758      */
35759     getTotalWidth : function(includeHidden){
35760         if(!this.totalWidth){
35761             this.totalWidth = 0;
35762             for(var i = 0, len = this.config.length; i < len; i++){
35763                 if(includeHidden || !this.isHidden(i)){
35764                     this.totalWidth += this.getColumnWidth(i);
35765                 }
35766             }
35767         }
35768         return this.totalWidth;
35769     },
35770
35771     /**
35772      * Returns the header for the specified column.
35773      * @param {Number} col The column index
35774      * @return {String}
35775      */
35776     getColumnHeader : function(col){
35777         return this.config[col].header;
35778     },
35779
35780     /**
35781      * Sets the header for a column.
35782      * @param {Number} col The column index
35783      * @param {String} header The new header
35784      */
35785     setColumnHeader : function(col, header){
35786         this.config[col].header = header;
35787         this.fireEvent("headerchange", this, col, header);
35788     },
35789
35790     /**
35791      * Returns the tooltip for the specified column.
35792      * @param {Number} col The column index
35793      * @return {String}
35794      */
35795     getColumnTooltip : function(col){
35796             return this.config[col].tooltip;
35797     },
35798     /**
35799      * Sets the tooltip for a column.
35800      * @param {Number} col The column index
35801      * @param {String} tooltip The new tooltip
35802      */
35803     setColumnTooltip : function(col, tooltip){
35804             this.config[col].tooltip = tooltip;
35805     },
35806
35807     /**
35808      * Returns the dataIndex for the specified column.
35809      * @param {Number} col The column index
35810      * @return {Number}
35811      */
35812     getDataIndex : function(col){
35813         return this.config[col].dataIndex;
35814     },
35815
35816     /**
35817      * Sets the dataIndex for a column.
35818      * @param {Number} col The column index
35819      * @param {Number} dataIndex The new dataIndex
35820      */
35821     setDataIndex : function(col, dataIndex){
35822         this.config[col].dataIndex = dataIndex;
35823     },
35824
35825     
35826     
35827     /**
35828      * Returns true if the cell is editable.
35829      * @param {Number} colIndex The column index
35830      * @param {Number} rowIndex The row index - this is nto actually used..?
35831      * @return {Boolean}
35832      */
35833     isCellEditable : function(colIndex, rowIndex){
35834         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35835     },
35836
35837     /**
35838      * Returns the editor defined for the cell/column.
35839      * return false or null to disable editing.
35840      * @param {Number} colIndex The column index
35841      * @param {Number} rowIndex The row index
35842      * @return {Object}
35843      */
35844     getCellEditor : function(colIndex, rowIndex){
35845         return this.config[colIndex].editor;
35846     },
35847
35848     /**
35849      * Sets if a column is editable.
35850      * @param {Number} col The column index
35851      * @param {Boolean} editable True if the column is editable
35852      */
35853     setEditable : function(col, editable){
35854         this.config[col].editable = editable;
35855     },
35856
35857
35858     /**
35859      * Returns true if the column is hidden.
35860      * @param {Number} colIndex The column index
35861      * @return {Boolean}
35862      */
35863     isHidden : function(colIndex){
35864         return this.config[colIndex].hidden;
35865     },
35866
35867
35868     /**
35869      * Returns true if the column width cannot be changed
35870      */
35871     isFixed : function(colIndex){
35872         return this.config[colIndex].fixed;
35873     },
35874
35875     /**
35876      * Returns true if the column can be resized
35877      * @return {Boolean}
35878      */
35879     isResizable : function(colIndex){
35880         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35881     },
35882     /**
35883      * Sets if a column is hidden.
35884      * @param {Number} colIndex The column index
35885      * @param {Boolean} hidden True if the column is hidden
35886      */
35887     setHidden : function(colIndex, hidden){
35888         this.config[colIndex].hidden = hidden;
35889         this.totalWidth = null;
35890         this.fireEvent("hiddenchange", this, colIndex, hidden);
35891     },
35892
35893     /**
35894      * Sets the editor for a column.
35895      * @param {Number} col The column index
35896      * @param {Object} editor The editor object
35897      */
35898     setEditor : function(col, editor){
35899         this.config[col].editor = editor;
35900     }
35901 });
35902
35903 Roo.grid.ColumnModel.defaultRenderer = function(value)
35904 {
35905     if(typeof value == "object") {
35906         return value;
35907     }
35908         if(typeof value == "string" && value.length < 1){
35909             return "&#160;";
35910         }
35911     
35912         return String.format("{0}", value);
35913 };
35914
35915 // Alias for backwards compatibility
35916 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35917 /*
35918  * Based on:
35919  * Ext JS Library 1.1.1
35920  * Copyright(c) 2006-2007, Ext JS, LLC.
35921  *
35922  * Originally Released Under LGPL - original licence link has changed is not relivant.
35923  *
35924  * Fork - LGPL
35925  * <script type="text/javascript">
35926  */
35927
35928 /**
35929  * @class Roo.grid.AbstractSelectionModel
35930  * @extends Roo.util.Observable
35931  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35932  * implemented by descendant classes.  This class should not be directly instantiated.
35933  * @constructor
35934  */
35935 Roo.grid.AbstractSelectionModel = function(){
35936     this.locked = false;
35937     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35938 };
35939
35940 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35941     /** @ignore Called by the grid automatically. Do not call directly. */
35942     init : function(grid){
35943         this.grid = grid;
35944         this.initEvents();
35945     },
35946
35947     /**
35948      * Locks the selections.
35949      */
35950     lock : function(){
35951         this.locked = true;
35952     },
35953
35954     /**
35955      * Unlocks the selections.
35956      */
35957     unlock : function(){
35958         this.locked = false;
35959     },
35960
35961     /**
35962      * Returns true if the selections are locked.
35963      * @return {Boolean}
35964      */
35965     isLocked : function(){
35966         return this.locked;
35967     }
35968 });/*
35969  * Based on:
35970  * Ext JS Library 1.1.1
35971  * Copyright(c) 2006-2007, Ext JS, LLC.
35972  *
35973  * Originally Released Under LGPL - original licence link has changed is not relivant.
35974  *
35975  * Fork - LGPL
35976  * <script type="text/javascript">
35977  */
35978 /**
35979  * @extends Roo.grid.AbstractSelectionModel
35980  * @class Roo.grid.RowSelectionModel
35981  * The default SelectionModel used by {@link Roo.grid.Grid}.
35982  * It supports multiple selections and keyboard selection/navigation. 
35983  * @constructor
35984  * @param {Object} config
35985  */
35986 Roo.grid.RowSelectionModel = function(config){
35987     Roo.apply(this, config);
35988     this.selections = new Roo.util.MixedCollection(false, function(o){
35989         return o.id;
35990     });
35991
35992     this.last = false;
35993     this.lastActive = false;
35994
35995     this.addEvents({
35996         /**
35997              * @event selectionchange
35998              * Fires when the selection changes
35999              * @param {SelectionModel} this
36000              */
36001             "selectionchange" : true,
36002         /**
36003              * @event afterselectionchange
36004              * Fires after the selection changes (eg. by key press or clicking)
36005              * @param {SelectionModel} this
36006              */
36007             "afterselectionchange" : true,
36008         /**
36009              * @event beforerowselect
36010              * Fires when a row is selected being selected, return false to cancel.
36011              * @param {SelectionModel} this
36012              * @param {Number} rowIndex The selected index
36013              * @param {Boolean} keepExisting False if other selections will be cleared
36014              */
36015             "beforerowselect" : true,
36016         /**
36017              * @event rowselect
36018              * Fires when a row is selected.
36019              * @param {SelectionModel} this
36020              * @param {Number} rowIndex The selected index
36021              * @param {Roo.data.Record} r The record
36022              */
36023             "rowselect" : true,
36024         /**
36025              * @event rowdeselect
36026              * Fires when a row is deselected.
36027              * @param {SelectionModel} this
36028              * @param {Number} rowIndex The selected index
36029              */
36030         "rowdeselect" : true
36031     });
36032     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36033     this.locked = false;
36034 };
36035
36036 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36037     /**
36038      * @cfg {Boolean} singleSelect
36039      * True to allow selection of only one row at a time (defaults to false)
36040      */
36041     singleSelect : false,
36042
36043     // private
36044     initEvents : function(){
36045
36046         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36047             this.grid.on("mousedown", this.handleMouseDown, this);
36048         }else{ // allow click to work like normal
36049             this.grid.on("rowclick", this.handleDragableRowClick, this);
36050         }
36051
36052         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36053             "up" : function(e){
36054                 if(!e.shiftKey){
36055                     this.selectPrevious(e.shiftKey);
36056                 }else if(this.last !== false && this.lastActive !== false){
36057                     var last = this.last;
36058                     this.selectRange(this.last,  this.lastActive-1);
36059                     this.grid.getView().focusRow(this.lastActive);
36060                     if(last !== false){
36061                         this.last = last;
36062                     }
36063                 }else{
36064                     this.selectFirstRow();
36065                 }
36066                 this.fireEvent("afterselectionchange", this);
36067             },
36068             "down" : function(e){
36069                 if(!e.shiftKey){
36070                     this.selectNext(e.shiftKey);
36071                 }else if(this.last !== false && this.lastActive !== false){
36072                     var last = this.last;
36073                     this.selectRange(this.last,  this.lastActive+1);
36074                     this.grid.getView().focusRow(this.lastActive);
36075                     if(last !== false){
36076                         this.last = last;
36077                     }
36078                 }else{
36079                     this.selectFirstRow();
36080                 }
36081                 this.fireEvent("afterselectionchange", this);
36082             },
36083             scope: this
36084         });
36085
36086         var view = this.grid.view;
36087         view.on("refresh", this.onRefresh, this);
36088         view.on("rowupdated", this.onRowUpdated, this);
36089         view.on("rowremoved", this.onRemove, this);
36090     },
36091
36092     // private
36093     onRefresh : function(){
36094         var ds = this.grid.dataSource, i, v = this.grid.view;
36095         var s = this.selections;
36096         s.each(function(r){
36097             if((i = ds.indexOfId(r.id)) != -1){
36098                 v.onRowSelect(i);
36099                 s.add(ds.getAt(i)); // updating the selection relate data
36100             }else{
36101                 s.remove(r);
36102             }
36103         });
36104     },
36105
36106     // private
36107     onRemove : function(v, index, r){
36108         this.selections.remove(r);
36109     },
36110
36111     // private
36112     onRowUpdated : function(v, index, r){
36113         if(this.isSelected(r)){
36114             v.onRowSelect(index);
36115         }
36116     },
36117
36118     /**
36119      * Select records.
36120      * @param {Array} records The records to select
36121      * @param {Boolean} keepExisting (optional) True to keep existing selections
36122      */
36123     selectRecords : function(records, keepExisting){
36124         if(!keepExisting){
36125             this.clearSelections();
36126         }
36127         var ds = this.grid.dataSource;
36128         for(var i = 0, len = records.length; i < len; i++){
36129             this.selectRow(ds.indexOf(records[i]), true);
36130         }
36131     },
36132
36133     /**
36134      * Gets the number of selected rows.
36135      * @return {Number}
36136      */
36137     getCount : function(){
36138         return this.selections.length;
36139     },
36140
36141     /**
36142      * Selects the first row in the grid.
36143      */
36144     selectFirstRow : function(){
36145         this.selectRow(0);
36146     },
36147
36148     /**
36149      * Select the last row.
36150      * @param {Boolean} keepExisting (optional) True to keep existing selections
36151      */
36152     selectLastRow : function(keepExisting){
36153         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36154     },
36155
36156     /**
36157      * Selects the row immediately following the last selected row.
36158      * @param {Boolean} keepExisting (optional) True to keep existing selections
36159      */
36160     selectNext : function(keepExisting){
36161         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36162             this.selectRow(this.last+1, keepExisting);
36163             this.grid.getView().focusRow(this.last);
36164         }
36165     },
36166
36167     /**
36168      * Selects the row that precedes the last selected row.
36169      * @param {Boolean} keepExisting (optional) True to keep existing selections
36170      */
36171     selectPrevious : function(keepExisting){
36172         if(this.last){
36173             this.selectRow(this.last-1, keepExisting);
36174             this.grid.getView().focusRow(this.last);
36175         }
36176     },
36177
36178     /**
36179      * Returns the selected records
36180      * @return {Array} Array of selected records
36181      */
36182     getSelections : function(){
36183         return [].concat(this.selections.items);
36184     },
36185
36186     /**
36187      * Returns the first selected record.
36188      * @return {Record}
36189      */
36190     getSelected : function(){
36191         return this.selections.itemAt(0);
36192     },
36193
36194
36195     /**
36196      * Clears all selections.
36197      */
36198     clearSelections : function(fast){
36199         if(this.locked) {
36200             return;
36201         }
36202         if(fast !== true){
36203             var ds = this.grid.dataSource;
36204             var s = this.selections;
36205             s.each(function(r){
36206                 this.deselectRow(ds.indexOfId(r.id));
36207             }, this);
36208             s.clear();
36209         }else{
36210             this.selections.clear();
36211         }
36212         this.last = false;
36213     },
36214
36215
36216     /**
36217      * Selects all rows.
36218      */
36219     selectAll : function(){
36220         if(this.locked) {
36221             return;
36222         }
36223         this.selections.clear();
36224         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36225             this.selectRow(i, true);
36226         }
36227     },
36228
36229     /**
36230      * Returns True if there is a selection.
36231      * @return {Boolean}
36232      */
36233     hasSelection : function(){
36234         return this.selections.length > 0;
36235     },
36236
36237     /**
36238      * Returns True if the specified row is selected.
36239      * @param {Number/Record} record The record or index of the record to check
36240      * @return {Boolean}
36241      */
36242     isSelected : function(index){
36243         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36244         return (r && this.selections.key(r.id) ? true : false);
36245     },
36246
36247     /**
36248      * Returns True if the specified record id is selected.
36249      * @param {String} id The id of record to check
36250      * @return {Boolean}
36251      */
36252     isIdSelected : function(id){
36253         return (this.selections.key(id) ? true : false);
36254     },
36255
36256     // private
36257     handleMouseDown : function(e, t){
36258         var view = this.grid.getView(), rowIndex;
36259         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36260             return;
36261         };
36262         if(e.shiftKey && this.last !== false){
36263             var last = this.last;
36264             this.selectRange(last, rowIndex, e.ctrlKey);
36265             this.last = last; // reset the last
36266             view.focusRow(rowIndex);
36267         }else{
36268             var isSelected = this.isSelected(rowIndex);
36269             if(e.button !== 0 && isSelected){
36270                 view.focusRow(rowIndex);
36271             }else if(e.ctrlKey && isSelected){
36272                 this.deselectRow(rowIndex);
36273             }else if(!isSelected){
36274                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36275                 view.focusRow(rowIndex);
36276             }
36277         }
36278         this.fireEvent("afterselectionchange", this);
36279     },
36280     // private
36281     handleDragableRowClick :  function(grid, rowIndex, e) 
36282     {
36283         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36284             this.selectRow(rowIndex, false);
36285             grid.view.focusRow(rowIndex);
36286              this.fireEvent("afterselectionchange", this);
36287         }
36288     },
36289     
36290     /**
36291      * Selects multiple rows.
36292      * @param {Array} rows Array of the indexes of the row to select
36293      * @param {Boolean} keepExisting (optional) True to keep existing selections
36294      */
36295     selectRows : function(rows, keepExisting){
36296         if(!keepExisting){
36297             this.clearSelections();
36298         }
36299         for(var i = 0, len = rows.length; i < len; i++){
36300             this.selectRow(rows[i], true);
36301         }
36302     },
36303
36304     /**
36305      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36306      * @param {Number} startRow The index of the first row in the range
36307      * @param {Number} endRow The index of the last row in the range
36308      * @param {Boolean} keepExisting (optional) True to retain existing selections
36309      */
36310     selectRange : function(startRow, endRow, keepExisting){
36311         if(this.locked) {
36312             return;
36313         }
36314         if(!keepExisting){
36315             this.clearSelections();
36316         }
36317         if(startRow <= endRow){
36318             for(var i = startRow; i <= endRow; i++){
36319                 this.selectRow(i, true);
36320             }
36321         }else{
36322             for(var i = startRow; i >= endRow; i--){
36323                 this.selectRow(i, true);
36324             }
36325         }
36326     },
36327
36328     /**
36329      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36330      * @param {Number} startRow The index of the first row in the range
36331      * @param {Number} endRow The index of the last row in the range
36332      */
36333     deselectRange : function(startRow, endRow, preventViewNotify){
36334         if(this.locked) {
36335             return;
36336         }
36337         for(var i = startRow; i <= endRow; i++){
36338             this.deselectRow(i, preventViewNotify);
36339         }
36340     },
36341
36342     /**
36343      * Selects a row.
36344      * @param {Number} row The index of the row to select
36345      * @param {Boolean} keepExisting (optional) True to keep existing selections
36346      */
36347     selectRow : function(index, keepExisting, preventViewNotify){
36348         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36349             return;
36350         }
36351         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36352             if(!keepExisting || this.singleSelect){
36353                 this.clearSelections();
36354             }
36355             var r = this.grid.dataSource.getAt(index);
36356             this.selections.add(r);
36357             this.last = this.lastActive = index;
36358             if(!preventViewNotify){
36359                 this.grid.getView().onRowSelect(index);
36360             }
36361             this.fireEvent("rowselect", this, index, r);
36362             this.fireEvent("selectionchange", this);
36363         }
36364     },
36365
36366     /**
36367      * Deselects a row.
36368      * @param {Number} row The index of the row to deselect
36369      */
36370     deselectRow : function(index, preventViewNotify){
36371         if(this.locked) {
36372             return;
36373         }
36374         if(this.last == index){
36375             this.last = false;
36376         }
36377         if(this.lastActive == index){
36378             this.lastActive = false;
36379         }
36380         var r = this.grid.dataSource.getAt(index);
36381         this.selections.remove(r);
36382         if(!preventViewNotify){
36383             this.grid.getView().onRowDeselect(index);
36384         }
36385         this.fireEvent("rowdeselect", this, index);
36386         this.fireEvent("selectionchange", this);
36387     },
36388
36389     // private
36390     restoreLast : function(){
36391         if(this._last){
36392             this.last = this._last;
36393         }
36394     },
36395
36396     // private
36397     acceptsNav : function(row, col, cm){
36398         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36399     },
36400
36401     // private
36402     onEditorKey : function(field, e){
36403         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36404         if(k == e.TAB){
36405             e.stopEvent();
36406             ed.completeEdit();
36407             if(e.shiftKey){
36408                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36409             }else{
36410                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36411             }
36412         }else if(k == e.ENTER && !e.ctrlKey){
36413             e.stopEvent();
36414             ed.completeEdit();
36415             if(e.shiftKey){
36416                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36417             }else{
36418                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36419             }
36420         }else if(k == e.ESC){
36421             ed.cancelEdit();
36422         }
36423         if(newCell){
36424             g.startEditing(newCell[0], newCell[1]);
36425         }
36426     }
36427 });/*
36428  * Based on:
36429  * Ext JS Library 1.1.1
36430  * Copyright(c) 2006-2007, Ext JS, LLC.
36431  *
36432  * Originally Released Under LGPL - original licence link has changed is not relivant.
36433  *
36434  * Fork - LGPL
36435  * <script type="text/javascript">
36436  */
36437 /**
36438  * @class Roo.grid.CellSelectionModel
36439  * @extends Roo.grid.AbstractSelectionModel
36440  * This class provides the basic implementation for cell selection in a grid.
36441  * @constructor
36442  * @param {Object} config The object containing the configuration of this model.
36443  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36444  */
36445 Roo.grid.CellSelectionModel = function(config){
36446     Roo.apply(this, config);
36447
36448     this.selection = null;
36449
36450     this.addEvents({
36451         /**
36452              * @event beforerowselect
36453              * Fires before a cell is selected.
36454              * @param {SelectionModel} this
36455              * @param {Number} rowIndex The selected row index
36456              * @param {Number} colIndex The selected cell index
36457              */
36458             "beforecellselect" : true,
36459         /**
36460              * @event cellselect
36461              * Fires when a cell is selected.
36462              * @param {SelectionModel} this
36463              * @param {Number} rowIndex The selected row index
36464              * @param {Number} colIndex The selected cell index
36465              */
36466             "cellselect" : true,
36467         /**
36468              * @event selectionchange
36469              * Fires when the active selection changes.
36470              * @param {SelectionModel} this
36471              * @param {Object} selection null for no selection or an object (o) with two properties
36472                 <ul>
36473                 <li>o.record: the record object for the row the selection is in</li>
36474                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36475                 </ul>
36476              */
36477             "selectionchange" : true,
36478         /**
36479              * @event tabend
36480              * Fires when the tab (or enter) was pressed on the last editable cell
36481              * You can use this to trigger add new row.
36482              * @param {SelectionModel} this
36483              */
36484             "tabend" : true,
36485          /**
36486              * @event beforeeditnext
36487              * Fires before the next editable sell is made active
36488              * You can use this to skip to another cell or fire the tabend
36489              *    if you set cell to false
36490              * @param {Object} eventdata object : { cell : [ row, col ] } 
36491              */
36492             "beforeeditnext" : true
36493     });
36494     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36495 };
36496
36497 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36498     
36499     enter_is_tab: false,
36500
36501     /** @ignore */
36502     initEvents : function(){
36503         this.grid.on("mousedown", this.handleMouseDown, this);
36504         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36505         var view = this.grid.view;
36506         view.on("refresh", this.onViewChange, this);
36507         view.on("rowupdated", this.onRowUpdated, this);
36508         view.on("beforerowremoved", this.clearSelections, this);
36509         view.on("beforerowsinserted", this.clearSelections, this);
36510         if(this.grid.isEditor){
36511             this.grid.on("beforeedit", this.beforeEdit,  this);
36512         }
36513     },
36514
36515         //private
36516     beforeEdit : function(e){
36517         this.select(e.row, e.column, false, true, e.record);
36518     },
36519
36520         //private
36521     onRowUpdated : function(v, index, r){
36522         if(this.selection && this.selection.record == r){
36523             v.onCellSelect(index, this.selection.cell[1]);
36524         }
36525     },
36526
36527         //private
36528     onViewChange : function(){
36529         this.clearSelections(true);
36530     },
36531
36532         /**
36533          * Returns the currently selected cell,.
36534          * @return {Array} The selected cell (row, column) or null if none selected.
36535          */
36536     getSelectedCell : function(){
36537         return this.selection ? this.selection.cell : null;
36538     },
36539
36540     /**
36541      * Clears all selections.
36542      * @param {Boolean} true to prevent the gridview from being notified about the change.
36543      */
36544     clearSelections : function(preventNotify){
36545         var s = this.selection;
36546         if(s){
36547             if(preventNotify !== true){
36548                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36549             }
36550             this.selection = null;
36551             this.fireEvent("selectionchange", this, null);
36552         }
36553     },
36554
36555     /**
36556      * Returns true if there is a selection.
36557      * @return {Boolean}
36558      */
36559     hasSelection : function(){
36560         return this.selection ? true : false;
36561     },
36562
36563     /** @ignore */
36564     handleMouseDown : function(e, t){
36565         var v = this.grid.getView();
36566         if(this.isLocked()){
36567             return;
36568         };
36569         var row = v.findRowIndex(t);
36570         var cell = v.findCellIndex(t);
36571         if(row !== false && cell !== false){
36572             this.select(row, cell);
36573         }
36574     },
36575
36576     /**
36577      * Selects a cell.
36578      * @param {Number} rowIndex
36579      * @param {Number} collIndex
36580      */
36581     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36582         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36583             this.clearSelections();
36584             r = r || this.grid.dataSource.getAt(rowIndex);
36585             this.selection = {
36586                 record : r,
36587                 cell : [rowIndex, colIndex]
36588             };
36589             if(!preventViewNotify){
36590                 var v = this.grid.getView();
36591                 v.onCellSelect(rowIndex, colIndex);
36592                 if(preventFocus !== true){
36593                     v.focusCell(rowIndex, colIndex);
36594                 }
36595             }
36596             this.fireEvent("cellselect", this, rowIndex, colIndex);
36597             this.fireEvent("selectionchange", this, this.selection);
36598         }
36599     },
36600
36601         //private
36602     isSelectable : function(rowIndex, colIndex, cm){
36603         return !cm.isHidden(colIndex);
36604     },
36605
36606     /** @ignore */
36607     handleKeyDown : function(e){
36608         //Roo.log('Cell Sel Model handleKeyDown');
36609         if(!e.isNavKeyPress()){
36610             return;
36611         }
36612         var g = this.grid, s = this.selection;
36613         if(!s){
36614             e.stopEvent();
36615             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36616             if(cell){
36617                 this.select(cell[0], cell[1]);
36618             }
36619             return;
36620         }
36621         var sm = this;
36622         var walk = function(row, col, step){
36623             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36624         };
36625         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36626         var newCell;
36627
36628       
36629
36630         switch(k){
36631             case e.TAB:
36632                 // handled by onEditorKey
36633                 if (g.isEditor && g.editing) {
36634                     return;
36635                 }
36636                 if(e.shiftKey) {
36637                     newCell = walk(r, c-1, -1);
36638                 } else {
36639                     newCell = walk(r, c+1, 1);
36640                 }
36641                 break;
36642             
36643             case e.DOWN:
36644                newCell = walk(r+1, c, 1);
36645                 break;
36646             
36647             case e.UP:
36648                 newCell = walk(r-1, c, -1);
36649                 break;
36650             
36651             case e.RIGHT:
36652                 newCell = walk(r, c+1, 1);
36653                 break;
36654             
36655             case e.LEFT:
36656                 newCell = walk(r, c-1, -1);
36657                 break;
36658             
36659             case e.ENTER:
36660                 
36661                 if(g.isEditor && !g.editing){
36662                    g.startEditing(r, c);
36663                    e.stopEvent();
36664                    return;
36665                 }
36666                 
36667                 
36668              break;
36669         };
36670         if(newCell){
36671             this.select(newCell[0], newCell[1]);
36672             e.stopEvent();
36673             
36674         }
36675     },
36676
36677     acceptsNav : function(row, col, cm){
36678         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36679     },
36680     /**
36681      * Selects a cell.
36682      * @param {Number} field (not used) - as it's normally used as a listener
36683      * @param {Number} e - event - fake it by using
36684      *
36685      * var e = Roo.EventObjectImpl.prototype;
36686      * e.keyCode = e.TAB
36687      *
36688      * 
36689      */
36690     onEditorKey : function(field, e){
36691         
36692         var k = e.getKey(),
36693             newCell,
36694             g = this.grid,
36695             ed = g.activeEditor,
36696             forward = false;
36697         ///Roo.log('onEditorKey' + k);
36698         
36699         
36700         if (this.enter_is_tab && k == e.ENTER) {
36701             k = e.TAB;
36702         }
36703         
36704         if(k == e.TAB){
36705             if(e.shiftKey){
36706                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36707             }else{
36708                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36709                 forward = true;
36710             }
36711             
36712             e.stopEvent();
36713             
36714         } else if(k == e.ENTER &&  !e.ctrlKey){
36715             ed.completeEdit();
36716             e.stopEvent();
36717             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36718         
36719                 } else if(k == e.ESC){
36720             ed.cancelEdit();
36721         }
36722                 
36723         if (newCell) {
36724             var ecall = { cell : newCell, forward : forward };
36725             this.fireEvent('beforeeditnext', ecall );
36726             newCell = ecall.cell;
36727                         forward = ecall.forward;
36728         }
36729                 
36730         if(newCell){
36731             //Roo.log('next cell after edit');
36732             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36733         } else if (forward) {
36734             // tabbed past last
36735             this.fireEvent.defer(100, this, ['tabend',this]);
36736         }
36737     }
36738 });/*
36739  * Based on:
36740  * Ext JS Library 1.1.1
36741  * Copyright(c) 2006-2007, Ext JS, LLC.
36742  *
36743  * Originally Released Under LGPL - original licence link has changed is not relivant.
36744  *
36745  * Fork - LGPL
36746  * <script type="text/javascript">
36747  */
36748  
36749 /**
36750  * @class Roo.grid.EditorGrid
36751  * @extends Roo.grid.Grid
36752  * Class for creating and editable grid.
36753  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36754  * The container MUST have some type of size defined for the grid to fill. The container will be 
36755  * automatically set to position relative if it isn't already.
36756  * @param {Object} dataSource The data model to bind to
36757  * @param {Object} colModel The column model with info about this grid's columns
36758  */
36759 Roo.grid.EditorGrid = function(container, config){
36760     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36761     this.getGridEl().addClass("xedit-grid");
36762
36763     if(!this.selModel){
36764         this.selModel = new Roo.grid.CellSelectionModel();
36765     }
36766
36767     this.activeEditor = null;
36768
36769         this.addEvents({
36770             /**
36771              * @event beforeedit
36772              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36773              * <ul style="padding:5px;padding-left:16px;">
36774              * <li>grid - This grid</li>
36775              * <li>record - The record being edited</li>
36776              * <li>field - The field name being edited</li>
36777              * <li>value - The value for the field being edited.</li>
36778              * <li>row - The grid row index</li>
36779              * <li>column - The grid column index</li>
36780              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36781              * </ul>
36782              * @param {Object} e An edit event (see above for description)
36783              */
36784             "beforeedit" : true,
36785             /**
36786              * @event afteredit
36787              * Fires after a cell is edited. <br />
36788              * <ul style="padding:5px;padding-left:16px;">
36789              * <li>grid - This grid</li>
36790              * <li>record - The record being edited</li>
36791              * <li>field - The field name being edited</li>
36792              * <li>value - The value being set</li>
36793              * <li>originalValue - The original value for the field, before the edit.</li>
36794              * <li>row - The grid row index</li>
36795              * <li>column - The grid column index</li>
36796              * </ul>
36797              * @param {Object} e An edit event (see above for description)
36798              */
36799             "afteredit" : true,
36800             /**
36801              * @event validateedit
36802              * Fires after a cell is edited, but before the value is set in the record. 
36803          * You can use this to modify the value being set in the field, Return false
36804              * to cancel the change. The edit event object has the following properties <br />
36805              * <ul style="padding:5px;padding-left:16px;">
36806          * <li>editor - This editor</li>
36807              * <li>grid - This grid</li>
36808              * <li>record - The record being edited</li>
36809              * <li>field - The field name being edited</li>
36810              * <li>value - The value being set</li>
36811              * <li>originalValue - The original value for the field, before the edit.</li>
36812              * <li>row - The grid row index</li>
36813              * <li>column - The grid column index</li>
36814              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36815              * </ul>
36816              * @param {Object} e An edit event (see above for description)
36817              */
36818             "validateedit" : true
36819         });
36820     this.on("bodyscroll", this.stopEditing,  this);
36821     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36822 };
36823
36824 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36825     /**
36826      * @cfg {Number} clicksToEdit
36827      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36828      */
36829     clicksToEdit: 2,
36830
36831     // private
36832     isEditor : true,
36833     // private
36834     trackMouseOver: false, // causes very odd FF errors
36835
36836     onCellDblClick : function(g, row, col){
36837         this.startEditing(row, col);
36838     },
36839
36840     onEditComplete : function(ed, value, startValue){
36841         this.editing = false;
36842         this.activeEditor = null;
36843         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36844         var r = ed.record;
36845         var field = this.colModel.getDataIndex(ed.col);
36846         var e = {
36847             grid: this,
36848             record: r,
36849             field: field,
36850             originalValue: startValue,
36851             value: value,
36852             row: ed.row,
36853             column: ed.col,
36854             cancel:false,
36855             editor: ed
36856         };
36857         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36858         cell.show();
36859           
36860         if(String(value) !== String(startValue)){
36861             
36862             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36863                 r.set(field, e.value);
36864                 // if we are dealing with a combo box..
36865                 // then we also set the 'name' colum to be the displayField
36866                 if (ed.field.displayField && ed.field.name) {
36867                     r.set(ed.field.name, ed.field.el.dom.value);
36868                 }
36869                 
36870                 delete e.cancel; //?? why!!!
36871                 this.fireEvent("afteredit", e);
36872             }
36873         } else {
36874             this.fireEvent("afteredit", e); // always fire it!
36875         }
36876         this.view.focusCell(ed.row, ed.col);
36877     },
36878
36879     /**
36880      * Starts editing the specified for the specified row/column
36881      * @param {Number} rowIndex
36882      * @param {Number} colIndex
36883      */
36884     startEditing : function(row, col){
36885         this.stopEditing();
36886         if(this.colModel.isCellEditable(col, row)){
36887             this.view.ensureVisible(row, col, true);
36888           
36889             var r = this.dataSource.getAt(row);
36890             var field = this.colModel.getDataIndex(col);
36891             var cell = Roo.get(this.view.getCell(row,col));
36892             var e = {
36893                 grid: this,
36894                 record: r,
36895                 field: field,
36896                 value: r.data[field],
36897                 row: row,
36898                 column: col,
36899                 cancel:false 
36900             };
36901             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36902                 this.editing = true;
36903                 var ed = this.colModel.getCellEditor(col, row);
36904                 
36905                 if (!ed) {
36906                     return;
36907                 }
36908                 if(!ed.rendered){
36909                     ed.render(ed.parentEl || document.body);
36910                 }
36911                 ed.field.reset();
36912                
36913                 cell.hide();
36914                 
36915                 (function(){ // complex but required for focus issues in safari, ie and opera
36916                     ed.row = row;
36917                     ed.col = col;
36918                     ed.record = r;
36919                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36920                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36921                     this.activeEditor = ed;
36922                     var v = r.data[field];
36923                     ed.startEdit(this.view.getCell(row, col), v);
36924                     // combo's with 'displayField and name set
36925                     if (ed.field.displayField && ed.field.name) {
36926                         ed.field.el.dom.value = r.data[ed.field.name];
36927                     }
36928                     
36929                     
36930                 }).defer(50, this);
36931             }
36932         }
36933     },
36934         
36935     /**
36936      * Stops any active editing
36937      */
36938     stopEditing : function(){
36939         if(this.activeEditor){
36940             this.activeEditor.completeEdit();
36941         }
36942         this.activeEditor = null;
36943     },
36944         
36945          /**
36946      * Called to get grid's drag proxy text, by default returns this.ddText.
36947      * @return {String}
36948      */
36949     getDragDropText : function(){
36950         var count = this.selModel.getSelectedCell() ? 1 : 0;
36951         return String.format(this.ddText, count, count == 1 ? '' : 's');
36952     }
36953         
36954 });/*
36955  * Based on:
36956  * Ext JS Library 1.1.1
36957  * Copyright(c) 2006-2007, Ext JS, LLC.
36958  *
36959  * Originally Released Under LGPL - original licence link has changed is not relivant.
36960  *
36961  * Fork - LGPL
36962  * <script type="text/javascript">
36963  */
36964
36965 // private - not really -- you end up using it !
36966 // This is a support class used internally by the Grid components
36967
36968 /**
36969  * @class Roo.grid.GridEditor
36970  * @extends Roo.Editor
36971  * Class for creating and editable grid elements.
36972  * @param {Object} config any settings (must include field)
36973  */
36974 Roo.grid.GridEditor = function(field, config){
36975     if (!config && field.field) {
36976         config = field;
36977         field = Roo.factory(config.field, Roo.form);
36978     }
36979     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36980     field.monitorTab = false;
36981 };
36982
36983 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36984     
36985     /**
36986      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36987      */
36988     
36989     alignment: "tl-tl",
36990     autoSize: "width",
36991     hideEl : false,
36992     cls: "x-small-editor x-grid-editor",
36993     shim:false,
36994     shadow:"frame"
36995 });/*
36996  * Based on:
36997  * Ext JS Library 1.1.1
36998  * Copyright(c) 2006-2007, Ext JS, LLC.
36999  *
37000  * Originally Released Under LGPL - original licence link has changed is not relivant.
37001  *
37002  * Fork - LGPL
37003  * <script type="text/javascript">
37004  */
37005   
37006
37007   
37008 Roo.grid.PropertyRecord = Roo.data.Record.create([
37009     {name:'name',type:'string'},  'value'
37010 ]);
37011
37012
37013 Roo.grid.PropertyStore = function(grid, source){
37014     this.grid = grid;
37015     this.store = new Roo.data.Store({
37016         recordType : Roo.grid.PropertyRecord
37017     });
37018     this.store.on('update', this.onUpdate,  this);
37019     if(source){
37020         this.setSource(source);
37021     }
37022     Roo.grid.PropertyStore.superclass.constructor.call(this);
37023 };
37024
37025
37026
37027 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37028     setSource : function(o){
37029         this.source = o;
37030         this.store.removeAll();
37031         var data = [];
37032         for(var k in o){
37033             if(this.isEditableValue(o[k])){
37034                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37035             }
37036         }
37037         this.store.loadRecords({records: data}, {}, true);
37038     },
37039
37040     onUpdate : function(ds, record, type){
37041         if(type == Roo.data.Record.EDIT){
37042             var v = record.data['value'];
37043             var oldValue = record.modified['value'];
37044             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37045                 this.source[record.id] = v;
37046                 record.commit();
37047                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37048             }else{
37049                 record.reject();
37050             }
37051         }
37052     },
37053
37054     getProperty : function(row){
37055        return this.store.getAt(row);
37056     },
37057
37058     isEditableValue: function(val){
37059         if(val && val instanceof Date){
37060             return true;
37061         }else if(typeof val == 'object' || typeof val == 'function'){
37062             return false;
37063         }
37064         return true;
37065     },
37066
37067     setValue : function(prop, value){
37068         this.source[prop] = value;
37069         this.store.getById(prop).set('value', value);
37070     },
37071
37072     getSource : function(){
37073         return this.source;
37074     }
37075 });
37076
37077 Roo.grid.PropertyColumnModel = function(grid, store){
37078     this.grid = grid;
37079     var g = Roo.grid;
37080     g.PropertyColumnModel.superclass.constructor.call(this, [
37081         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37082         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37083     ]);
37084     this.store = store;
37085     this.bselect = Roo.DomHelper.append(document.body, {
37086         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37087             {tag: 'option', value: 'true', html: 'true'},
37088             {tag: 'option', value: 'false', html: 'false'}
37089         ]
37090     });
37091     Roo.id(this.bselect);
37092     var f = Roo.form;
37093     this.editors = {
37094         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37095         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37096         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37097         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37098         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37099     };
37100     this.renderCellDelegate = this.renderCell.createDelegate(this);
37101     this.renderPropDelegate = this.renderProp.createDelegate(this);
37102 };
37103
37104 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37105     
37106     
37107     nameText : 'Name',
37108     valueText : 'Value',
37109     
37110     dateFormat : 'm/j/Y',
37111     
37112     
37113     renderDate : function(dateVal){
37114         return dateVal.dateFormat(this.dateFormat);
37115     },
37116
37117     renderBool : function(bVal){
37118         return bVal ? 'true' : 'false';
37119     },
37120
37121     isCellEditable : function(colIndex, rowIndex){
37122         return colIndex == 1;
37123     },
37124
37125     getRenderer : function(col){
37126         return col == 1 ?
37127             this.renderCellDelegate : this.renderPropDelegate;
37128     },
37129
37130     renderProp : function(v){
37131         return this.getPropertyName(v);
37132     },
37133
37134     renderCell : function(val){
37135         var rv = val;
37136         if(val instanceof Date){
37137             rv = this.renderDate(val);
37138         }else if(typeof val == 'boolean'){
37139             rv = this.renderBool(val);
37140         }
37141         return Roo.util.Format.htmlEncode(rv);
37142     },
37143
37144     getPropertyName : function(name){
37145         var pn = this.grid.propertyNames;
37146         return pn && pn[name] ? pn[name] : name;
37147     },
37148
37149     getCellEditor : function(colIndex, rowIndex){
37150         var p = this.store.getProperty(rowIndex);
37151         var n = p.data['name'], val = p.data['value'];
37152         
37153         if(typeof(this.grid.customEditors[n]) == 'string'){
37154             return this.editors[this.grid.customEditors[n]];
37155         }
37156         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37157             return this.grid.customEditors[n];
37158         }
37159         if(val instanceof Date){
37160             return this.editors['date'];
37161         }else if(typeof val == 'number'){
37162             return this.editors['number'];
37163         }else if(typeof val == 'boolean'){
37164             return this.editors['boolean'];
37165         }else{
37166             return this.editors['string'];
37167         }
37168     }
37169 });
37170
37171 /**
37172  * @class Roo.grid.PropertyGrid
37173  * @extends Roo.grid.EditorGrid
37174  * This class represents the  interface of a component based property grid control.
37175  * <br><br>Usage:<pre><code>
37176  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37177       
37178  });
37179  // set any options
37180  grid.render();
37181  * </code></pre>
37182   
37183  * @constructor
37184  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37185  * The container MUST have some type of size defined for the grid to fill. The container will be
37186  * automatically set to position relative if it isn't already.
37187  * @param {Object} config A config object that sets properties on this grid.
37188  */
37189 Roo.grid.PropertyGrid = function(container, config){
37190     config = config || {};
37191     var store = new Roo.grid.PropertyStore(this);
37192     this.store = store;
37193     var cm = new Roo.grid.PropertyColumnModel(this, store);
37194     store.store.sort('name', 'ASC');
37195     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37196         ds: store.store,
37197         cm: cm,
37198         enableColLock:false,
37199         enableColumnMove:false,
37200         stripeRows:false,
37201         trackMouseOver: false,
37202         clicksToEdit:1
37203     }, config));
37204     this.getGridEl().addClass('x-props-grid');
37205     this.lastEditRow = null;
37206     this.on('columnresize', this.onColumnResize, this);
37207     this.addEvents({
37208          /**
37209              * @event beforepropertychange
37210              * Fires before a property changes (return false to stop?)
37211              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37212              * @param {String} id Record Id
37213              * @param {String} newval New Value
37214          * @param {String} oldval Old Value
37215              */
37216         "beforepropertychange": true,
37217         /**
37218              * @event propertychange
37219              * Fires after a property changes
37220              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37221              * @param {String} id Record Id
37222              * @param {String} newval New Value
37223          * @param {String} oldval Old Value
37224              */
37225         "propertychange": true
37226     });
37227     this.customEditors = this.customEditors || {};
37228 };
37229 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37230     
37231      /**
37232      * @cfg {Object} customEditors map of colnames=> custom editors.
37233      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37234      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37235      * false disables editing of the field.
37236          */
37237     
37238       /**
37239      * @cfg {Object} propertyNames map of property Names to their displayed value
37240          */
37241     
37242     render : function(){
37243         Roo.grid.PropertyGrid.superclass.render.call(this);
37244         this.autoSize.defer(100, this);
37245     },
37246
37247     autoSize : function(){
37248         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37249         if(this.view){
37250             this.view.fitColumns();
37251         }
37252     },
37253
37254     onColumnResize : function(){
37255         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37256         this.autoSize();
37257     },
37258     /**
37259      * Sets the data for the Grid
37260      * accepts a Key => Value object of all the elements avaiable.
37261      * @param {Object} data  to appear in grid.
37262      */
37263     setSource : function(source){
37264         this.store.setSource(source);
37265         //this.autoSize();
37266     },
37267     /**
37268      * Gets all the data from the grid.
37269      * @return {Object} data  data stored in grid
37270      */
37271     getSource : function(){
37272         return this.store.getSource();
37273     }
37274 });/*
37275   
37276  * Licence LGPL
37277  
37278  */
37279  
37280 /**
37281  * @class Roo.grid.Calendar
37282  * @extends Roo.util.Grid
37283  * This class extends the Grid to provide a calendar widget
37284  * <br><br>Usage:<pre><code>
37285  var grid = new Roo.grid.Calendar("my-container-id", {
37286      ds: myDataStore,
37287      cm: myColModel,
37288      selModel: mySelectionModel,
37289      autoSizeColumns: true,
37290      monitorWindowResize: false,
37291      trackMouseOver: true
37292      eventstore : real data store..
37293  });
37294  // set any options
37295  grid.render();
37296   
37297   * @constructor
37298  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37299  * The container MUST have some type of size defined for the grid to fill. The container will be
37300  * automatically set to position relative if it isn't already.
37301  * @param {Object} config A config object that sets properties on this grid.
37302  */
37303 Roo.grid.Calendar = function(container, config){
37304         // initialize the container
37305         this.container = Roo.get(container);
37306         this.container.update("");
37307         this.container.setStyle("overflow", "hidden");
37308     this.container.addClass('x-grid-container');
37309
37310     this.id = this.container.id;
37311
37312     Roo.apply(this, config);
37313     // check and correct shorthanded configs
37314     
37315     var rows = [];
37316     var d =1;
37317     for (var r = 0;r < 6;r++) {
37318         
37319         rows[r]=[];
37320         for (var c =0;c < 7;c++) {
37321             rows[r][c]= '';
37322         }
37323     }
37324     if (this.eventStore) {
37325         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37326         this.eventStore.on('load',this.onLoad, this);
37327         this.eventStore.on('beforeload',this.clearEvents, this);
37328          
37329     }
37330     
37331     this.dataSource = new Roo.data.Store({
37332             proxy: new Roo.data.MemoryProxy(rows),
37333             reader: new Roo.data.ArrayReader({}, [
37334                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37335     });
37336
37337     this.dataSource.load();
37338     this.ds = this.dataSource;
37339     this.ds.xmodule = this.xmodule || false;
37340     
37341     
37342     var cellRender = function(v,x,r)
37343     {
37344         return String.format(
37345             '<div class="fc-day  fc-widget-content"><div>' +
37346                 '<div class="fc-event-container"></div>' +
37347                 '<div class="fc-day-number">{0}</div>'+
37348                 
37349                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37350             '</div></div>', v);
37351     
37352     }
37353     
37354     
37355     this.colModel = new Roo.grid.ColumnModel( [
37356         {
37357             xtype: 'ColumnModel',
37358             xns: Roo.grid,
37359             dataIndex : 'weekday0',
37360             header : 'Sunday',
37361             renderer : cellRender
37362         },
37363         {
37364             xtype: 'ColumnModel',
37365             xns: Roo.grid,
37366             dataIndex : 'weekday1',
37367             header : 'Monday',
37368             renderer : cellRender
37369         },
37370         {
37371             xtype: 'ColumnModel',
37372             xns: Roo.grid,
37373             dataIndex : 'weekday2',
37374             header : 'Tuesday',
37375             renderer : cellRender
37376         },
37377         {
37378             xtype: 'ColumnModel',
37379             xns: Roo.grid,
37380             dataIndex : 'weekday3',
37381             header : 'Wednesday',
37382             renderer : cellRender
37383         },
37384         {
37385             xtype: 'ColumnModel',
37386             xns: Roo.grid,
37387             dataIndex : 'weekday4',
37388             header : 'Thursday',
37389             renderer : cellRender
37390         },
37391         {
37392             xtype: 'ColumnModel',
37393             xns: Roo.grid,
37394             dataIndex : 'weekday5',
37395             header : 'Friday',
37396             renderer : cellRender
37397         },
37398         {
37399             xtype: 'ColumnModel',
37400             xns: Roo.grid,
37401             dataIndex : 'weekday6',
37402             header : 'Saturday',
37403             renderer : cellRender
37404         }
37405     ]);
37406     this.cm = this.colModel;
37407     this.cm.xmodule = this.xmodule || false;
37408  
37409         
37410           
37411     //this.selModel = new Roo.grid.CellSelectionModel();
37412     //this.sm = this.selModel;
37413     //this.selModel.init(this);
37414     
37415     
37416     if(this.width){
37417         this.container.setWidth(this.width);
37418     }
37419
37420     if(this.height){
37421         this.container.setHeight(this.height);
37422     }
37423     /** @private */
37424         this.addEvents({
37425         // raw events
37426         /**
37427          * @event click
37428          * The raw click event for the entire grid.
37429          * @param {Roo.EventObject} e
37430          */
37431         "click" : true,
37432         /**
37433          * @event dblclick
37434          * The raw dblclick event for the entire grid.
37435          * @param {Roo.EventObject} e
37436          */
37437         "dblclick" : true,
37438         /**
37439          * @event contextmenu
37440          * The raw contextmenu event for the entire grid.
37441          * @param {Roo.EventObject} e
37442          */
37443         "contextmenu" : true,
37444         /**
37445          * @event mousedown
37446          * The raw mousedown event for the entire grid.
37447          * @param {Roo.EventObject} e
37448          */
37449         "mousedown" : true,
37450         /**
37451          * @event mouseup
37452          * The raw mouseup event for the entire grid.
37453          * @param {Roo.EventObject} e
37454          */
37455         "mouseup" : true,
37456         /**
37457          * @event mouseover
37458          * The raw mouseover event for the entire grid.
37459          * @param {Roo.EventObject} e
37460          */
37461         "mouseover" : true,
37462         /**
37463          * @event mouseout
37464          * The raw mouseout event for the entire grid.
37465          * @param {Roo.EventObject} e
37466          */
37467         "mouseout" : true,
37468         /**
37469          * @event keypress
37470          * The raw keypress event for the entire grid.
37471          * @param {Roo.EventObject} e
37472          */
37473         "keypress" : true,
37474         /**
37475          * @event keydown
37476          * The raw keydown event for the entire grid.
37477          * @param {Roo.EventObject} e
37478          */
37479         "keydown" : true,
37480
37481         // custom events
37482
37483         /**
37484          * @event cellclick
37485          * Fires when a cell is clicked
37486          * @param {Grid} this
37487          * @param {Number} rowIndex
37488          * @param {Number} columnIndex
37489          * @param {Roo.EventObject} e
37490          */
37491         "cellclick" : true,
37492         /**
37493          * @event celldblclick
37494          * Fires when a cell is double clicked
37495          * @param {Grid} this
37496          * @param {Number} rowIndex
37497          * @param {Number} columnIndex
37498          * @param {Roo.EventObject} e
37499          */
37500         "celldblclick" : true,
37501         /**
37502          * @event rowclick
37503          * Fires when a row is clicked
37504          * @param {Grid} this
37505          * @param {Number} rowIndex
37506          * @param {Roo.EventObject} e
37507          */
37508         "rowclick" : true,
37509         /**
37510          * @event rowdblclick
37511          * Fires when a row is double clicked
37512          * @param {Grid} this
37513          * @param {Number} rowIndex
37514          * @param {Roo.EventObject} e
37515          */
37516         "rowdblclick" : true,
37517         /**
37518          * @event headerclick
37519          * Fires when a header is clicked
37520          * @param {Grid} this
37521          * @param {Number} columnIndex
37522          * @param {Roo.EventObject} e
37523          */
37524         "headerclick" : true,
37525         /**
37526          * @event headerdblclick
37527          * Fires when a header cell is double clicked
37528          * @param {Grid} this
37529          * @param {Number} columnIndex
37530          * @param {Roo.EventObject} e
37531          */
37532         "headerdblclick" : true,
37533         /**
37534          * @event rowcontextmenu
37535          * Fires when a row is right clicked
37536          * @param {Grid} this
37537          * @param {Number} rowIndex
37538          * @param {Roo.EventObject} e
37539          */
37540         "rowcontextmenu" : true,
37541         /**
37542          * @event cellcontextmenu
37543          * Fires when a cell is right clicked
37544          * @param {Grid} this
37545          * @param {Number} rowIndex
37546          * @param {Number} cellIndex
37547          * @param {Roo.EventObject} e
37548          */
37549          "cellcontextmenu" : true,
37550         /**
37551          * @event headercontextmenu
37552          * Fires when a header is right clicked
37553          * @param {Grid} this
37554          * @param {Number} columnIndex
37555          * @param {Roo.EventObject} e
37556          */
37557         "headercontextmenu" : true,
37558         /**
37559          * @event bodyscroll
37560          * Fires when the body element is scrolled
37561          * @param {Number} scrollLeft
37562          * @param {Number} scrollTop
37563          */
37564         "bodyscroll" : true,
37565         /**
37566          * @event columnresize
37567          * Fires when the user resizes a column
37568          * @param {Number} columnIndex
37569          * @param {Number} newSize
37570          */
37571         "columnresize" : true,
37572         /**
37573          * @event columnmove
37574          * Fires when the user moves a column
37575          * @param {Number} oldIndex
37576          * @param {Number} newIndex
37577          */
37578         "columnmove" : true,
37579         /**
37580          * @event startdrag
37581          * Fires when row(s) start being dragged
37582          * @param {Grid} this
37583          * @param {Roo.GridDD} dd The drag drop object
37584          * @param {event} e The raw browser event
37585          */
37586         "startdrag" : true,
37587         /**
37588          * @event enddrag
37589          * Fires when a drag operation is complete
37590          * @param {Grid} this
37591          * @param {Roo.GridDD} dd The drag drop object
37592          * @param {event} e The raw browser event
37593          */
37594         "enddrag" : true,
37595         /**
37596          * @event dragdrop
37597          * Fires when dragged row(s) are dropped on a valid DD target
37598          * @param {Grid} this
37599          * @param {Roo.GridDD} dd The drag drop object
37600          * @param {String} targetId The target drag drop object
37601          * @param {event} e The raw browser event
37602          */
37603         "dragdrop" : true,
37604         /**
37605          * @event dragover
37606          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37607          * @param {Grid} this
37608          * @param {Roo.GridDD} dd The drag drop object
37609          * @param {String} targetId The target drag drop object
37610          * @param {event} e The raw browser event
37611          */
37612         "dragover" : true,
37613         /**
37614          * @event dragenter
37615          *  Fires when the dragged row(s) first cross another DD target while being dragged
37616          * @param {Grid} this
37617          * @param {Roo.GridDD} dd The drag drop object
37618          * @param {String} targetId The target drag drop object
37619          * @param {event} e The raw browser event
37620          */
37621         "dragenter" : true,
37622         /**
37623          * @event dragout
37624          * Fires when the dragged row(s) leave another DD target while being dragged
37625          * @param {Grid} this
37626          * @param {Roo.GridDD} dd The drag drop object
37627          * @param {String} targetId The target drag drop object
37628          * @param {event} e The raw browser event
37629          */
37630         "dragout" : true,
37631         /**
37632          * @event rowclass
37633          * Fires when a row is rendered, so you can change add a style to it.
37634          * @param {GridView} gridview   The grid view
37635          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37636          */
37637         'rowclass' : true,
37638
37639         /**
37640          * @event render
37641          * Fires when the grid is rendered
37642          * @param {Grid} grid
37643          */
37644         'render' : true,
37645             /**
37646              * @event select
37647              * Fires when a date is selected
37648              * @param {DatePicker} this
37649              * @param {Date} date The selected date
37650              */
37651         'select': true,
37652         /**
37653              * @event monthchange
37654              * Fires when the displayed month changes 
37655              * @param {DatePicker} this
37656              * @param {Date} date The selected month
37657              */
37658         'monthchange': true,
37659         /**
37660              * @event evententer
37661              * Fires when mouse over an event
37662              * @param {Calendar} this
37663              * @param {event} Event
37664              */
37665         'evententer': true,
37666         /**
37667              * @event eventleave
37668              * Fires when the mouse leaves an
37669              * @param {Calendar} this
37670              * @param {event}
37671              */
37672         'eventleave': true,
37673         /**
37674              * @event eventclick
37675              * Fires when the mouse click an
37676              * @param {Calendar} this
37677              * @param {event}
37678              */
37679         'eventclick': true,
37680         /**
37681              * @event eventrender
37682              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37683              * @param {Calendar} this
37684              * @param {data} data to be modified
37685              */
37686         'eventrender': true
37687         
37688     });
37689
37690     Roo.grid.Grid.superclass.constructor.call(this);
37691     this.on('render', function() {
37692         this.view.el.addClass('x-grid-cal'); 
37693         
37694         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37695
37696     },this);
37697     
37698     if (!Roo.grid.Calendar.style) {
37699         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37700             
37701             
37702             '.x-grid-cal .x-grid-col' :  {
37703                 height: 'auto !important',
37704                 'vertical-align': 'top'
37705             },
37706             '.x-grid-cal  .fc-event-hori' : {
37707                 height: '14px'
37708             }
37709              
37710             
37711         }, Roo.id());
37712     }
37713
37714     
37715     
37716 };
37717 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37718     /**
37719      * @cfg {Store} eventStore The store that loads events.
37720      */
37721     eventStore : 25,
37722
37723      
37724     activeDate : false,
37725     startDay : 0,
37726     autoWidth : true,
37727     monitorWindowResize : false,
37728
37729     
37730     resizeColumns : function() {
37731         var col = (this.view.el.getWidth() / 7) - 3;
37732         // loop through cols, and setWidth
37733         for(var i =0 ; i < 7 ; i++){
37734             this.cm.setColumnWidth(i, col);
37735         }
37736     },
37737      setDate :function(date) {
37738         
37739         Roo.log('setDate?');
37740         
37741         this.resizeColumns();
37742         var vd = this.activeDate;
37743         this.activeDate = date;
37744 //        if(vd && this.el){
37745 //            var t = date.getTime();
37746 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37747 //                Roo.log('using add remove');
37748 //                
37749 //                this.fireEvent('monthchange', this, date);
37750 //                
37751 //                this.cells.removeClass("fc-state-highlight");
37752 //                this.cells.each(function(c){
37753 //                   if(c.dateValue == t){
37754 //                       c.addClass("fc-state-highlight");
37755 //                       setTimeout(function(){
37756 //                            try{c.dom.firstChild.focus();}catch(e){}
37757 //                       }, 50);
37758 //                       return false;
37759 //                   }
37760 //                   return true;
37761 //                });
37762 //                return;
37763 //            }
37764 //        }
37765         
37766         var days = date.getDaysInMonth();
37767         
37768         var firstOfMonth = date.getFirstDateOfMonth();
37769         var startingPos = firstOfMonth.getDay()-this.startDay;
37770         
37771         if(startingPos < this.startDay){
37772             startingPos += 7;
37773         }
37774         
37775         var pm = date.add(Date.MONTH, -1);
37776         var prevStart = pm.getDaysInMonth()-startingPos;
37777 //        
37778         
37779         
37780         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37781         
37782         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37783         //this.cells.addClassOnOver('fc-state-hover');
37784         
37785         var cells = this.cells.elements;
37786         var textEls = this.textNodes;
37787         
37788         //Roo.each(cells, function(cell){
37789         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37790         //});
37791         
37792         days += startingPos;
37793
37794         // convert everything to numbers so it's fast
37795         var day = 86400000;
37796         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37797         //Roo.log(d);
37798         //Roo.log(pm);
37799         //Roo.log(prevStart);
37800         
37801         var today = new Date().clearTime().getTime();
37802         var sel = date.clearTime().getTime();
37803         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37804         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37805         var ddMatch = this.disabledDatesRE;
37806         var ddText = this.disabledDatesText;
37807         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37808         var ddaysText = this.disabledDaysText;
37809         var format = this.format;
37810         
37811         var setCellClass = function(cal, cell){
37812             
37813             //Roo.log('set Cell Class');
37814             cell.title = "";
37815             var t = d.getTime();
37816             
37817             //Roo.log(d);
37818             
37819             
37820             cell.dateValue = t;
37821             if(t == today){
37822                 cell.className += " fc-today";
37823                 cell.className += " fc-state-highlight";
37824                 cell.title = cal.todayText;
37825             }
37826             if(t == sel){
37827                 // disable highlight in other month..
37828                 cell.className += " fc-state-highlight";
37829                 
37830             }
37831             // disabling
37832             if(t < min) {
37833                 //cell.className = " fc-state-disabled";
37834                 cell.title = cal.minText;
37835                 return;
37836             }
37837             if(t > max) {
37838                 //cell.className = " fc-state-disabled";
37839                 cell.title = cal.maxText;
37840                 return;
37841             }
37842             if(ddays){
37843                 if(ddays.indexOf(d.getDay()) != -1){
37844                     // cell.title = ddaysText;
37845                    // cell.className = " fc-state-disabled";
37846                 }
37847             }
37848             if(ddMatch && format){
37849                 var fvalue = d.dateFormat(format);
37850                 if(ddMatch.test(fvalue)){
37851                     cell.title = ddText.replace("%0", fvalue);
37852                    cell.className = " fc-state-disabled";
37853                 }
37854             }
37855             
37856             if (!cell.initialClassName) {
37857                 cell.initialClassName = cell.dom.className;
37858             }
37859             
37860             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37861         };
37862
37863         var i = 0;
37864         
37865         for(; i < startingPos; i++) {
37866             cells[i].dayName =  (++prevStart);
37867             Roo.log(textEls[i]);
37868             d.setDate(d.getDate()+1);
37869             
37870             //cells[i].className = "fc-past fc-other-month";
37871             setCellClass(this, cells[i]);
37872         }
37873         
37874         var intDay = 0;
37875         
37876         for(; i < days; i++){
37877             intDay = i - startingPos + 1;
37878             cells[i].dayName =  (intDay);
37879             d.setDate(d.getDate()+1);
37880             
37881             cells[i].className = ''; // "x-date-active";
37882             setCellClass(this, cells[i]);
37883         }
37884         var extraDays = 0;
37885         
37886         for(; i < 42; i++) {
37887             //textEls[i].innerHTML = (++extraDays);
37888             
37889             d.setDate(d.getDate()+1);
37890             cells[i].dayName = (++extraDays);
37891             cells[i].className = "fc-future fc-other-month";
37892             setCellClass(this, cells[i]);
37893         }
37894         
37895         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37896         
37897         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37898         
37899         // this will cause all the cells to mis
37900         var rows= [];
37901         var i =0;
37902         for (var r = 0;r < 6;r++) {
37903             for (var c =0;c < 7;c++) {
37904                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37905             }    
37906         }
37907         
37908         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37909         for(i=0;i<cells.length;i++) {
37910             
37911             this.cells.elements[i].dayName = cells[i].dayName ;
37912             this.cells.elements[i].className = cells[i].className;
37913             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37914             this.cells.elements[i].title = cells[i].title ;
37915             this.cells.elements[i].dateValue = cells[i].dateValue ;
37916         }
37917         
37918         
37919         
37920         
37921         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37922         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37923         
37924         ////if(totalRows != 6){
37925             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37926            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37927        // }
37928         
37929         this.fireEvent('monthchange', this, date);
37930         
37931         
37932     },
37933  /**
37934      * Returns the grid's SelectionModel.
37935      * @return {SelectionModel}
37936      */
37937     getSelectionModel : function(){
37938         if(!this.selModel){
37939             this.selModel = new Roo.grid.CellSelectionModel();
37940         }
37941         return this.selModel;
37942     },
37943
37944     load: function() {
37945         this.eventStore.load()
37946         
37947         
37948         
37949     },
37950     
37951     findCell : function(dt) {
37952         dt = dt.clearTime().getTime();
37953         var ret = false;
37954         this.cells.each(function(c){
37955             //Roo.log("check " +c.dateValue + '?=' + dt);
37956             if(c.dateValue == dt){
37957                 ret = c;
37958                 return false;
37959             }
37960             return true;
37961         });
37962         
37963         return ret;
37964     },
37965     
37966     findCells : function(rec) {
37967         var s = rec.data.start_dt.clone().clearTime().getTime();
37968        // Roo.log(s);
37969         var e= rec.data.end_dt.clone().clearTime().getTime();
37970        // Roo.log(e);
37971         var ret = [];
37972         this.cells.each(function(c){
37973              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37974             
37975             if(c.dateValue > e){
37976                 return ;
37977             }
37978             if(c.dateValue < s){
37979                 return ;
37980             }
37981             ret.push(c);
37982         });
37983         
37984         return ret;    
37985     },
37986     
37987     findBestRow: function(cells)
37988     {
37989         var ret = 0;
37990         
37991         for (var i =0 ; i < cells.length;i++) {
37992             ret  = Math.max(cells[i].rows || 0,ret);
37993         }
37994         return ret;
37995         
37996     },
37997     
37998     
37999     addItem : function(rec)
38000     {
38001         // look for vertical location slot in
38002         var cells = this.findCells(rec);
38003         
38004         rec.row = this.findBestRow(cells);
38005         
38006         // work out the location.
38007         
38008         var crow = false;
38009         var rows = [];
38010         for(var i =0; i < cells.length; i++) {
38011             if (!crow) {
38012                 crow = {
38013                     start : cells[i],
38014                     end :  cells[i]
38015                 };
38016                 continue;
38017             }
38018             if (crow.start.getY() == cells[i].getY()) {
38019                 // on same row.
38020                 crow.end = cells[i];
38021                 continue;
38022             }
38023             // different row.
38024             rows.push(crow);
38025             crow = {
38026                 start: cells[i],
38027                 end : cells[i]
38028             };
38029             
38030         }
38031         
38032         rows.push(crow);
38033         rec.els = [];
38034         rec.rows = rows;
38035         rec.cells = cells;
38036         for (var i = 0; i < cells.length;i++) {
38037             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38038             
38039         }
38040         
38041         
38042     },
38043     
38044     clearEvents: function() {
38045         
38046         if (!this.eventStore.getCount()) {
38047             return;
38048         }
38049         // reset number of rows in cells.
38050         Roo.each(this.cells.elements, function(c){
38051             c.rows = 0;
38052         });
38053         
38054         this.eventStore.each(function(e) {
38055             this.clearEvent(e);
38056         },this);
38057         
38058     },
38059     
38060     clearEvent : function(ev)
38061     {
38062         if (ev.els) {
38063             Roo.each(ev.els, function(el) {
38064                 el.un('mouseenter' ,this.onEventEnter, this);
38065                 el.un('mouseleave' ,this.onEventLeave, this);
38066                 el.remove();
38067             },this);
38068             ev.els = [];
38069         }
38070     },
38071     
38072     
38073     renderEvent : function(ev,ctr) {
38074         if (!ctr) {
38075              ctr = this.view.el.select('.fc-event-container',true).first();
38076         }
38077         
38078          
38079         this.clearEvent(ev);
38080             //code
38081        
38082         
38083         
38084         ev.els = [];
38085         var cells = ev.cells;
38086         var rows = ev.rows;
38087         this.fireEvent('eventrender', this, ev);
38088         
38089         for(var i =0; i < rows.length; i++) {
38090             
38091             cls = '';
38092             if (i == 0) {
38093                 cls += ' fc-event-start';
38094             }
38095             if ((i+1) == rows.length) {
38096                 cls += ' fc-event-end';
38097             }
38098             
38099             //Roo.log(ev.data);
38100             // how many rows should it span..
38101             var cg = this.eventTmpl.append(ctr,Roo.apply({
38102                 fccls : cls
38103                 
38104             }, ev.data) , true);
38105             
38106             
38107             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38108             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38109             cg.on('click', this.onEventClick, this, ev);
38110             
38111             ev.els.push(cg);
38112             
38113             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38114             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38115             //Roo.log(cg);
38116              
38117             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38118             cg.setWidth(ebox.right - sbox.x -2);
38119         }
38120     },
38121     
38122     renderEvents: function()
38123     {   
38124         // first make sure there is enough space..
38125         
38126         if (!this.eventTmpl) {
38127             this.eventTmpl = new Roo.Template(
38128                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38129                     '<div class="fc-event-inner">' +
38130                         '<span class="fc-event-time">{time}</span>' +
38131                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38132                     '</div>' +
38133                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38134                 '</div>'
38135             );
38136                 
38137         }
38138                
38139         
38140         
38141         this.cells.each(function(c) {
38142             //Roo.log(c.select('.fc-day-content div',true).first());
38143             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38144         });
38145         
38146         var ctr = this.view.el.select('.fc-event-container',true).first();
38147         
38148         var cls;
38149         this.eventStore.each(function(ev){
38150             
38151             this.renderEvent(ev);
38152              
38153              
38154         }, this);
38155         this.view.layout();
38156         
38157     },
38158     
38159     onEventEnter: function (e, el,event,d) {
38160         this.fireEvent('evententer', this, el, event);
38161     },
38162     
38163     onEventLeave: function (e, el,event,d) {
38164         this.fireEvent('eventleave', this, el, event);
38165     },
38166     
38167     onEventClick: function (e, el,event,d) {
38168         this.fireEvent('eventclick', this, el, event);
38169     },
38170     
38171     onMonthChange: function () {
38172         this.store.load();
38173     },
38174     
38175     onLoad: function () {
38176         
38177         //Roo.log('calendar onload');
38178 //         
38179         if(this.eventStore.getCount() > 0){
38180             
38181            
38182             
38183             this.eventStore.each(function(d){
38184                 
38185                 
38186                 // FIXME..
38187                 var add =   d.data;
38188                 if (typeof(add.end_dt) == 'undefined')  {
38189                     Roo.log("Missing End time in calendar data: ");
38190                     Roo.log(d);
38191                     return;
38192                 }
38193                 if (typeof(add.start_dt) == 'undefined')  {
38194                     Roo.log("Missing Start time in calendar data: ");
38195                     Roo.log(d);
38196                     return;
38197                 }
38198                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38199                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38200                 add.id = add.id || d.id;
38201                 add.title = add.title || '??';
38202                 
38203                 this.addItem(d);
38204                 
38205              
38206             },this);
38207         }
38208         
38209         this.renderEvents();
38210     }
38211     
38212
38213 });
38214 /*
38215  grid : {
38216                 xtype: 'Grid',
38217                 xns: Roo.grid,
38218                 listeners : {
38219                     render : function ()
38220                     {
38221                         _this.grid = this;
38222                         
38223                         if (!this.view.el.hasClass('course-timesheet')) {
38224                             this.view.el.addClass('course-timesheet');
38225                         }
38226                         if (this.tsStyle) {
38227                             this.ds.load({});
38228                             return; 
38229                         }
38230                         Roo.log('width');
38231                         Roo.log(_this.grid.view.el.getWidth());
38232                         
38233                         
38234                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38235                             '.course-timesheet .x-grid-row' : {
38236                                 height: '80px'
38237                             },
38238                             '.x-grid-row td' : {
38239                                 'vertical-align' : 0
38240                             },
38241                             '.course-edit-link' : {
38242                                 'color' : 'blue',
38243                                 'text-overflow' : 'ellipsis',
38244                                 'overflow' : 'hidden',
38245                                 'white-space' : 'nowrap',
38246                                 'cursor' : 'pointer'
38247                             },
38248                             '.sub-link' : {
38249                                 'color' : 'green'
38250                             },
38251                             '.de-act-sup-link' : {
38252                                 'color' : 'purple',
38253                                 'text-decoration' : 'line-through'
38254                             },
38255                             '.de-act-link' : {
38256                                 'color' : 'red',
38257                                 'text-decoration' : 'line-through'
38258                             },
38259                             '.course-timesheet .course-highlight' : {
38260                                 'border-top-style': 'dashed !important',
38261                                 'border-bottom-bottom': 'dashed !important'
38262                             },
38263                             '.course-timesheet .course-item' : {
38264                                 'font-family'   : 'tahoma, arial, helvetica',
38265                                 'font-size'     : '11px',
38266                                 'overflow'      : 'hidden',
38267                                 'padding-left'  : '10px',
38268                                 'padding-right' : '10px',
38269                                 'padding-top' : '10px' 
38270                             }
38271                             
38272                         }, Roo.id());
38273                                 this.ds.load({});
38274                     }
38275                 },
38276                 autoWidth : true,
38277                 monitorWindowResize : false,
38278                 cellrenderer : function(v,x,r)
38279                 {
38280                     return v;
38281                 },
38282                 sm : {
38283                     xtype: 'CellSelectionModel',
38284                     xns: Roo.grid
38285                 },
38286                 dataSource : {
38287                     xtype: 'Store',
38288                     xns: Roo.data,
38289                     listeners : {
38290                         beforeload : function (_self, options)
38291                         {
38292                             options.params = options.params || {};
38293                             options.params._month = _this.monthField.getValue();
38294                             options.params.limit = 9999;
38295                             options.params['sort'] = 'when_dt';    
38296                             options.params['dir'] = 'ASC';    
38297                             this.proxy.loadResponse = this.loadResponse;
38298                             Roo.log("load?");
38299                             //this.addColumns();
38300                         },
38301                         load : function (_self, records, options)
38302                         {
38303                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38304                                 // if you click on the translation.. you can edit it...
38305                                 var el = Roo.get(this);
38306                                 var id = el.dom.getAttribute('data-id');
38307                                 var d = el.dom.getAttribute('data-date');
38308                                 var t = el.dom.getAttribute('data-time');
38309                                 //var id = this.child('span').dom.textContent;
38310                                 
38311                                 //Roo.log(this);
38312                                 Pman.Dialog.CourseCalendar.show({
38313                                     id : id,
38314                                     when_d : d,
38315                                     when_t : t,
38316                                     productitem_active : id ? 1 : 0
38317                                 }, function() {
38318                                     _this.grid.ds.load({});
38319                                 });
38320                            
38321                            });
38322                            
38323                            _this.panel.fireEvent('resize', [ '', '' ]);
38324                         }
38325                     },
38326                     loadResponse : function(o, success, response){
38327                             // this is overridden on before load..
38328                             
38329                             Roo.log("our code?");       
38330                             //Roo.log(success);
38331                             //Roo.log(response)
38332                             delete this.activeRequest;
38333                             if(!success){
38334                                 this.fireEvent("loadexception", this, o, response);
38335                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38336                                 return;
38337                             }
38338                             var result;
38339                             try {
38340                                 result = o.reader.read(response);
38341                             }catch(e){
38342                                 Roo.log("load exception?");
38343                                 this.fireEvent("loadexception", this, o, response, e);
38344                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38345                                 return;
38346                             }
38347                             Roo.log("ready...");        
38348                             // loop through result.records;
38349                             // and set this.tdate[date] = [] << array of records..
38350                             _this.tdata  = {};
38351                             Roo.each(result.records, function(r){
38352                                 //Roo.log(r.data);
38353                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38354                                     _this.tdata[r.data.when_dt.format('j')] = [];
38355                                 }
38356                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38357                             });
38358                             
38359                             //Roo.log(_this.tdata);
38360                             
38361                             result.records = [];
38362                             result.totalRecords = 6;
38363                     
38364                             // let's generate some duumy records for the rows.
38365                             //var st = _this.dateField.getValue();
38366                             
38367                             // work out monday..
38368                             //st = st.add(Date.DAY, -1 * st.format('w'));
38369                             
38370                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38371                             
38372                             var firstOfMonth = date.getFirstDayOfMonth();
38373                             var days = date.getDaysInMonth();
38374                             var d = 1;
38375                             var firstAdded = false;
38376                             for (var i = 0; i < result.totalRecords ; i++) {
38377                                 //var d= st.add(Date.DAY, i);
38378                                 var row = {};
38379                                 var added = 0;
38380                                 for(var w = 0 ; w < 7 ; w++){
38381                                     if(!firstAdded && firstOfMonth != w){
38382                                         continue;
38383                                     }
38384                                     if(d > days){
38385                                         continue;
38386                                     }
38387                                     firstAdded = true;
38388                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38389                                     row['weekday'+w] = String.format(
38390                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38391                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38392                                                     d,
38393                                                     date.format('Y-m-')+dd
38394                                                 );
38395                                     added++;
38396                                     if(typeof(_this.tdata[d]) != 'undefined'){
38397                                         Roo.each(_this.tdata[d], function(r){
38398                                             var is_sub = '';
38399                                             var deactive = '';
38400                                             var id = r.id;
38401                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38402                                             if(r.parent_id*1>0){
38403                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38404                                                 id = r.parent_id;
38405                                             }
38406                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38407                                                 deactive = 'de-act-link';
38408                                             }
38409                                             
38410                                             row['weekday'+w] += String.format(
38411                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38412                                                     id, //0
38413                                                     r.product_id_name, //1
38414                                                     r.when_dt.format('h:ia'), //2
38415                                                     is_sub, //3
38416                                                     deactive, //4
38417                                                     desc // 5
38418                                             );
38419                                         });
38420                                     }
38421                                     d++;
38422                                 }
38423                                 
38424                                 // only do this if something added..
38425                                 if(added > 0){ 
38426                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38427                                 }
38428                                 
38429                                 
38430                                 // push it twice. (second one with an hour..
38431                                 
38432                             }
38433                             //Roo.log(result);
38434                             this.fireEvent("load", this, o, o.request.arg);
38435                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38436                         },
38437                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38438                     proxy : {
38439                         xtype: 'HttpProxy',
38440                         xns: Roo.data,
38441                         method : 'GET',
38442                         url : baseURL + '/Roo/Shop_course.php'
38443                     },
38444                     reader : {
38445                         xtype: 'JsonReader',
38446                         xns: Roo.data,
38447                         id : 'id',
38448                         fields : [
38449                             {
38450                                 'name': 'id',
38451                                 'type': 'int'
38452                             },
38453                             {
38454                                 'name': 'when_dt',
38455                                 'type': 'string'
38456                             },
38457                             {
38458                                 'name': 'end_dt',
38459                                 'type': 'string'
38460                             },
38461                             {
38462                                 'name': 'parent_id',
38463                                 'type': 'int'
38464                             },
38465                             {
38466                                 'name': 'product_id',
38467                                 'type': 'int'
38468                             },
38469                             {
38470                                 'name': 'productitem_id',
38471                                 'type': 'int'
38472                             },
38473                             {
38474                                 'name': 'guid',
38475                                 'type': 'int'
38476                             }
38477                         ]
38478                     }
38479                 },
38480                 toolbar : {
38481                     xtype: 'Toolbar',
38482                     xns: Roo,
38483                     items : [
38484                         {
38485                             xtype: 'Button',
38486                             xns: Roo.Toolbar,
38487                             listeners : {
38488                                 click : function (_self, e)
38489                                 {
38490                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38491                                     sd.setMonth(sd.getMonth()-1);
38492                                     _this.monthField.setValue(sd.format('Y-m-d'));
38493                                     _this.grid.ds.load({});
38494                                 }
38495                             },
38496                             text : "Back"
38497                         },
38498                         {
38499                             xtype: 'Separator',
38500                             xns: Roo.Toolbar
38501                         },
38502                         {
38503                             xtype: 'MonthField',
38504                             xns: Roo.form,
38505                             listeners : {
38506                                 render : function (_self)
38507                                 {
38508                                     _this.monthField = _self;
38509                                    // _this.monthField.set  today
38510                                 },
38511                                 select : function (combo, date)
38512                                 {
38513                                     _this.grid.ds.load({});
38514                                 }
38515                             },
38516                             value : (function() { return new Date(); })()
38517                         },
38518                         {
38519                             xtype: 'Separator',
38520                             xns: Roo.Toolbar
38521                         },
38522                         {
38523                             xtype: 'TextItem',
38524                             xns: Roo.Toolbar,
38525                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38526                         },
38527                         {
38528                             xtype: 'Fill',
38529                             xns: Roo.Toolbar
38530                         },
38531                         {
38532                             xtype: 'Button',
38533                             xns: Roo.Toolbar,
38534                             listeners : {
38535                                 click : function (_self, e)
38536                                 {
38537                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38538                                     sd.setMonth(sd.getMonth()+1);
38539                                     _this.monthField.setValue(sd.format('Y-m-d'));
38540                                     _this.grid.ds.load({});
38541                                 }
38542                             },
38543                             text : "Next"
38544                         }
38545                     ]
38546                 },
38547                  
38548             }
38549         };
38550         
38551         *//*
38552  * Based on:
38553  * Ext JS Library 1.1.1
38554  * Copyright(c) 2006-2007, Ext JS, LLC.
38555  *
38556  * Originally Released Under LGPL - original licence link has changed is not relivant.
38557  *
38558  * Fork - LGPL
38559  * <script type="text/javascript">
38560  */
38561  
38562 /**
38563  * @class Roo.LoadMask
38564  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38565  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38566  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38567  * element's UpdateManager load indicator and will be destroyed after the initial load.
38568  * @constructor
38569  * Create a new LoadMask
38570  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38571  * @param {Object} config The config object
38572  */
38573 Roo.LoadMask = function(el, config){
38574     this.el = Roo.get(el);
38575     Roo.apply(this, config);
38576     if(this.store){
38577         this.store.on('beforeload', this.onBeforeLoad, this);
38578         this.store.on('load', this.onLoad, this);
38579         this.store.on('loadexception', this.onLoadException, this);
38580         this.removeMask = false;
38581     }else{
38582         var um = this.el.getUpdateManager();
38583         um.showLoadIndicator = false; // disable the default indicator
38584         um.on('beforeupdate', this.onBeforeLoad, this);
38585         um.on('update', this.onLoad, this);
38586         um.on('failure', this.onLoad, this);
38587         this.removeMask = true;
38588     }
38589 };
38590
38591 Roo.LoadMask.prototype = {
38592     /**
38593      * @cfg {Boolean} removeMask
38594      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38595      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38596      */
38597     /**
38598      * @cfg {String} msg
38599      * The text to display in a centered loading message box (defaults to 'Loading...')
38600      */
38601     msg : 'Loading...',
38602     /**
38603      * @cfg {String} msgCls
38604      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38605      */
38606     msgCls : 'x-mask-loading',
38607
38608     /**
38609      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38610      * @type Boolean
38611      */
38612     disabled: false,
38613
38614     /**
38615      * Disables the mask to prevent it from being displayed
38616      */
38617     disable : function(){
38618        this.disabled = true;
38619     },
38620
38621     /**
38622      * Enables the mask so that it can be displayed
38623      */
38624     enable : function(){
38625         this.disabled = false;
38626     },
38627     
38628     onLoadException : function()
38629     {
38630         Roo.log(arguments);
38631         
38632         if (typeof(arguments[3]) != 'undefined') {
38633             Roo.MessageBox.alert("Error loading",arguments[3]);
38634         } 
38635         /*
38636         try {
38637             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38638                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38639             }   
38640         } catch(e) {
38641             
38642         }
38643         */
38644     
38645         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38646     },
38647     // private
38648     onLoad : function()
38649     {
38650         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38651     },
38652
38653     // private
38654     onBeforeLoad : function(){
38655         if(!this.disabled){
38656             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38657         }
38658     },
38659
38660     // private
38661     destroy : function(){
38662         if(this.store){
38663             this.store.un('beforeload', this.onBeforeLoad, this);
38664             this.store.un('load', this.onLoad, this);
38665             this.store.un('loadexception', this.onLoadException, this);
38666         }else{
38667             var um = this.el.getUpdateManager();
38668             um.un('beforeupdate', this.onBeforeLoad, this);
38669             um.un('update', this.onLoad, this);
38670             um.un('failure', this.onLoad, this);
38671         }
38672     }
38673 };/*
38674  * Based on:
38675  * Ext JS Library 1.1.1
38676  * Copyright(c) 2006-2007, Ext JS, LLC.
38677  *
38678  * Originally Released Under LGPL - original licence link has changed is not relivant.
38679  *
38680  * Fork - LGPL
38681  * <script type="text/javascript">
38682  */
38683
38684
38685 /**
38686  * @class Roo.XTemplate
38687  * @extends Roo.Template
38688  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38689 <pre><code>
38690 var t = new Roo.XTemplate(
38691         '&lt;select name="{name}"&gt;',
38692                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38693         '&lt;/select&gt;'
38694 );
38695  
38696 // then append, applying the master template values
38697  </code></pre>
38698  *
38699  * Supported features:
38700  *
38701  *  Tags:
38702
38703 <pre><code>
38704       {a_variable} - output encoded.
38705       {a_variable.format:("Y-m-d")} - call a method on the variable
38706       {a_variable:raw} - unencoded output
38707       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38708       {a_variable:this.method_on_template(...)} - call a method on the template object.
38709  
38710 </code></pre>
38711  *  The tpl tag:
38712 <pre><code>
38713         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38714         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38715         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38716         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38717   
38718         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38719         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38720 </code></pre>
38721  *      
38722  */
38723 Roo.XTemplate = function()
38724 {
38725     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38726     if (this.html) {
38727         this.compile();
38728     }
38729 };
38730
38731
38732 Roo.extend(Roo.XTemplate, Roo.Template, {
38733
38734     /**
38735      * The various sub templates
38736      */
38737     tpls : false,
38738     /**
38739      *
38740      * basic tag replacing syntax
38741      * WORD:WORD()
38742      *
38743      * // you can fake an object call by doing this
38744      *  x.t:(test,tesT) 
38745      * 
38746      */
38747     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38748
38749     /**
38750      * compile the template
38751      *
38752      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38753      *
38754      */
38755     compile: function()
38756     {
38757         var s = this.html;
38758      
38759         s = ['<tpl>', s, '</tpl>'].join('');
38760     
38761         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38762             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38763             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38764             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38765             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38766             m,
38767             id     = 0,
38768             tpls   = [];
38769     
38770         while(true == !!(m = s.match(re))){
38771             var forMatch   = m[0].match(nameRe),
38772                 ifMatch   = m[0].match(ifRe),
38773                 execMatch   = m[0].match(execRe),
38774                 namedMatch   = m[0].match(namedRe),
38775                 
38776                 exp  = null, 
38777                 fn   = null,
38778                 exec = null,
38779                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38780                 
38781             if (ifMatch) {
38782                 // if - puts fn into test..
38783                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38784                 if(exp){
38785                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38786                 }
38787             }
38788             
38789             if (execMatch) {
38790                 // exec - calls a function... returns empty if true is  returned.
38791                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38792                 if(exp){
38793                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38794                 }
38795             }
38796             
38797             
38798             if (name) {
38799                 // for = 
38800                 switch(name){
38801                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38802                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38803                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38804                 }
38805             }
38806             var uid = namedMatch ? namedMatch[1] : id;
38807             
38808             
38809             tpls.push({
38810                 id:     namedMatch ? namedMatch[1] : id,
38811                 target: name,
38812                 exec:   exec,
38813                 test:   fn,
38814                 body:   m[1] || ''
38815             });
38816             if (namedMatch) {
38817                 s = s.replace(m[0], '');
38818             } else { 
38819                 s = s.replace(m[0], '{xtpl'+ id + '}');
38820             }
38821             ++id;
38822         }
38823         this.tpls = [];
38824         for(var i = tpls.length-1; i >= 0; --i){
38825             this.compileTpl(tpls[i]);
38826             this.tpls[tpls[i].id] = tpls[i];
38827         }
38828         this.master = tpls[tpls.length-1];
38829         return this;
38830     },
38831     /**
38832      * same as applyTemplate, except it's done to one of the subTemplates
38833      * when using named templates, you can do:
38834      *
38835      * var str = pl.applySubTemplate('your-name', values);
38836      *
38837      * 
38838      * @param {Number} id of the template
38839      * @param {Object} values to apply to template
38840      * @param {Object} parent (normaly the instance of this object)
38841      */
38842     applySubTemplate : function(id, values, parent)
38843     {
38844         
38845         
38846         var t = this.tpls[id];
38847         
38848         
38849         try { 
38850             if(t.test && !t.test.call(this, values, parent)){
38851                 return '';
38852             }
38853         } catch(e) {
38854             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38855             Roo.log(e.toString());
38856             Roo.log(t.test);
38857             return ''
38858         }
38859         try { 
38860             
38861             if(t.exec && t.exec.call(this, values, parent)){
38862                 return '';
38863             }
38864         } catch(e) {
38865             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38866             Roo.log(e.toString());
38867             Roo.log(t.exec);
38868             return ''
38869         }
38870         try {
38871             var vs = t.target ? t.target.call(this, values, parent) : values;
38872             parent = t.target ? values : parent;
38873             if(t.target && vs instanceof Array){
38874                 var buf = [];
38875                 for(var i = 0, len = vs.length; i < len; i++){
38876                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38877                 }
38878                 return buf.join('');
38879             }
38880             return t.compiled.call(this, vs, parent);
38881         } catch (e) {
38882             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38883             Roo.log(e.toString());
38884             Roo.log(t.compiled);
38885             return '';
38886         }
38887     },
38888
38889     compileTpl : function(tpl)
38890     {
38891         var fm = Roo.util.Format;
38892         var useF = this.disableFormats !== true;
38893         var sep = Roo.isGecko ? "+" : ",";
38894         var undef = function(str) {
38895             Roo.log("Property not found :"  + str);
38896             return '';
38897         };
38898         
38899         var fn = function(m, name, format, args)
38900         {
38901             //Roo.log(arguments);
38902             args = args ? args.replace(/\\'/g,"'") : args;
38903             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38904             if (typeof(format) == 'undefined') {
38905                 format= 'htmlEncode';
38906             }
38907             if (format == 'raw' ) {
38908                 format = false;
38909             }
38910             
38911             if(name.substr(0, 4) == 'xtpl'){
38912                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38913             }
38914             
38915             // build an array of options to determine if value is undefined..
38916             
38917             // basically get 'xxxx.yyyy' then do
38918             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38919             //    (function () { Roo.log("Property not found"); return ''; })() :
38920             //    ......
38921             
38922             var udef_ar = [];
38923             var lookfor = '';
38924             Roo.each(name.split('.'), function(st) {
38925                 lookfor += (lookfor.length ? '.': '') + st;
38926                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38927             });
38928             
38929             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38930             
38931             
38932             if(format && useF){
38933                 
38934                 args = args ? ',' + args : "";
38935                  
38936                 if(format.substr(0, 5) != "this."){
38937                     format = "fm." + format + '(';
38938                 }else{
38939                     format = 'this.call("'+ format.substr(5) + '", ';
38940                     args = ", values";
38941                 }
38942                 
38943                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38944             }
38945              
38946             if (args.length) {
38947                 // called with xxyx.yuu:(test,test)
38948                 // change to ()
38949                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38950             }
38951             // raw.. - :raw modifier..
38952             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38953             
38954         };
38955         var body;
38956         // branched to use + in gecko and [].join() in others
38957         if(Roo.isGecko){
38958             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38959                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38960                     "';};};";
38961         }else{
38962             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38963             body.push(tpl.body.replace(/(\r\n|\n)/g,
38964                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38965             body.push("'].join('');};};");
38966             body = body.join('');
38967         }
38968         
38969         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38970        
38971         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38972         eval(body);
38973         
38974         return this;
38975     },
38976
38977     applyTemplate : function(values){
38978         return this.master.compiled.call(this, values, {});
38979         //var s = this.subs;
38980     },
38981
38982     apply : function(){
38983         return this.applyTemplate.apply(this, arguments);
38984     }
38985
38986  });
38987
38988 Roo.XTemplate.from = function(el){
38989     el = Roo.getDom(el);
38990     return new Roo.XTemplate(el.value || el.innerHTML);
38991 };