Roo/menu/Menu.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      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @constructor
1104  * @param {Object} config
1105  */
1106 Roo.data.SimpleStore = function(config)
1107 {
1108     Roo.data.SimpleStore.superclass.constructor.call(this, {
1109         isLocal : true,
1110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1111                 id: config.id
1112             },
1113             Roo.data.Record.create(config.fields)
1114         ),
1115         proxy : new Roo.data.MemoryProxy(config.data)
1116     });
1117     this.load();
1118 };
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1120  * Based on:
1121  * Ext JS Library 1.1.1
1122  * Copyright(c) 2006-2007, Ext JS, LLC.
1123  *
1124  * Originally Released Under LGPL - original licence link has changed is not relivant.
1125  *
1126  * Fork - LGPL
1127  * <script type="text/javascript">
1128  */
1129
1130 /**
1131 /**
1132  * @extends Roo.data.Store
1133  * @class Roo.data.JsonStore
1134  * Small helper class to make creating Stores for JSON data easier. <br/>
1135 <pre><code>
1136 var store = new Roo.data.JsonStore({
1137     url: 'get-images.php',
1138     root: 'images',
1139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1140 });
1141 </code></pre>
1142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143  * JsonReader and HttpProxy (unless inline data is provided).</b>
1144  * @cfg {Array} fields An array of field definition objects, or field name strings.
1145  * @constructor
1146  * @param {Object} config
1147  */
1148 Roo.data.JsonStore = function(c){
1149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151         reader: new Roo.data.JsonReader(c, c.fields)
1152     }));
1153 };
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1155  * Based on:
1156  * Ext JS Library 1.1.1
1157  * Copyright(c) 2006-2007, Ext JS, LLC.
1158  *
1159  * Originally Released Under LGPL - original licence link has changed is not relivant.
1160  *
1161  * Fork - LGPL
1162  * <script type="text/javascript">
1163  */
1164
1165  
1166 Roo.data.Field = function(config){
1167     if(typeof config == "string"){
1168         config = {name: config};
1169     }
1170     Roo.apply(this, config);
1171     
1172     if(!this.type){
1173         this.type = "auto";
1174     }
1175     
1176     var st = Roo.data.SortTypes;
1177     // named sortTypes are supported, here we look them up
1178     if(typeof this.sortType == "string"){
1179         this.sortType = st[this.sortType];
1180     }
1181     
1182     // set default sortType for strings and dates
1183     if(!this.sortType){
1184         switch(this.type){
1185             case "string":
1186                 this.sortType = st.asUCString;
1187                 break;
1188             case "date":
1189                 this.sortType = st.asDate;
1190                 break;
1191             default:
1192                 this.sortType = st.none;
1193         }
1194     }
1195
1196     // define once
1197     var stripRe = /[\$,%]/g;
1198
1199     // prebuilt conversion function for this field, instead of
1200     // switching every time we're reading a value
1201     if(!this.convert){
1202         var cv, dateFormat = this.dateFormat;
1203         switch(this.type){
1204             case "":
1205             case "auto":
1206             case undefined:
1207                 cv = function(v){ return v; };
1208                 break;
1209             case "string":
1210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1211                 break;
1212             case "int":
1213                 cv = function(v){
1214                     return v !== undefined && v !== null && v !== '' ?
1215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1216                     };
1217                 break;
1218             case "float":
1219                 cv = function(v){
1220                     return v !== undefined && v !== null && v !== '' ?
1221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1222                     };
1223                 break;
1224             case "bool":
1225             case "boolean":
1226                 cv = function(v){ return v === true || v === "true" || v == 1; };
1227                 break;
1228             case "date":
1229                 cv = function(v){
1230                     if(!v){
1231                         return '';
1232                     }
1233                     if(v instanceof Date){
1234                         return v;
1235                     }
1236                     if(dateFormat){
1237                         if(dateFormat == "timestamp"){
1238                             return new Date(v*1000);
1239                         }
1240                         return Date.parseDate(v, dateFormat);
1241                     }
1242                     var parsed = Date.parse(v);
1243                     return parsed ? new Date(parsed) : null;
1244                 };
1245              break;
1246             
1247         }
1248         this.convert = cv;
1249     }
1250 };
1251
1252 Roo.data.Field.prototype = {
1253     dateFormat: null,
1254     defaultValue: "",
1255     mapping: null,
1256     sortType : null,
1257     sortDir : "ASC"
1258 };/*
1259  * Based on:
1260  * Ext JS Library 1.1.1
1261  * Copyright(c) 2006-2007, Ext JS, LLC.
1262  *
1263  * Originally Released Under LGPL - original licence link has changed is not relivant.
1264  *
1265  * Fork - LGPL
1266  * <script type="text/javascript">
1267  */
1268  
1269 // Base class for reading structured data from a data source.  This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1271
1272 /**
1273  * @class Roo.data.DataReader
1274  * Base class for reading structured data from a data source.  This class is intended to be
1275  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1276  */
1277
1278 Roo.data.DataReader = function(meta, recordType){
1279     
1280     this.meta = meta;
1281     
1282     this.recordType = recordType instanceof Array ? 
1283         Roo.data.Record.create(recordType) : recordType;
1284 };
1285
1286 Roo.data.DataReader.prototype = {
1287     
1288     
1289     readerType : 'Data',
1290      /**
1291      * Create an empty record
1292      * @param {Object} data (optional) - overlay some values
1293      * @return {Roo.data.Record} record created.
1294      */
1295     newRow :  function(d) {
1296         var da =  {};
1297         this.recordType.prototype.fields.each(function(c) {
1298             switch( c.type) {
1299                 case 'int' : da[c.name] = 0; break;
1300                 case 'date' : da[c.name] = new Date(); break;
1301                 case 'float' : da[c.name] = 0.0; break;
1302                 case 'boolean' : da[c.name] = false; break;
1303                 default : da[c.name] = ""; break;
1304             }
1305             
1306         });
1307         return new this.recordType(Roo.apply(da, d));
1308     }
1309     
1310     
1311 };/*
1312  * Based on:
1313  * Ext JS Library 1.1.1
1314  * Copyright(c) 2006-2007, Ext JS, LLC.
1315  *
1316  * Originally Released Under LGPL - original licence link has changed is not relivant.
1317  *
1318  * Fork - LGPL
1319  * <script type="text/javascript">
1320  */
1321
1322 /**
1323  * @class Roo.data.DataProxy
1324  * @extends Roo.data.Observable
1325  * This class is an abstract base class for implementations which provide retrieval of
1326  * unformatted data objects.<br>
1327  * <p>
1328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329  * (of the appropriate type which knows how to parse the data object) to provide a block of
1330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1331  * <p>
1332  * Custom implementations must implement the load method as described in
1333  * {@link Roo.data.HttpProxy#load}.
1334  */
1335 Roo.data.DataProxy = function(){
1336     this.addEvents({
1337         /**
1338          * @event beforeload
1339          * Fires before a network request is made to retrieve a data object.
1340          * @param {Object} This DataProxy object.
1341          * @param {Object} params The params parameter to the load function.
1342          */
1343         beforeload : true,
1344         /**
1345          * @event load
1346          * Fires before the load method's callback is called.
1347          * @param {Object} This DataProxy object.
1348          * @param {Object} o The data object.
1349          * @param {Object} arg The callback argument object passed to the load function.
1350          */
1351         load : true,
1352         /**
1353          * @event loadexception
1354          * Fires if an Exception occurs during data retrieval.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} o The data object.
1357          * @param {Object} arg The callback argument object passed to the load function.
1358          * @param {Object} e The Exception.
1359          */
1360         loadexception : true
1361     });
1362     Roo.data.DataProxy.superclass.constructor.call(this);
1363 };
1364
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1366
1367     /**
1368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1369      */
1370 /*
1371  * Based on:
1372  * Ext JS Library 1.1.1
1373  * Copyright(c) 2006-2007, Ext JS, LLC.
1374  *
1375  * Originally Released Under LGPL - original licence link has changed is not relivant.
1376  *
1377  * Fork - LGPL
1378  * <script type="text/javascript">
1379  */
1380 /**
1381  * @class Roo.data.MemoryProxy
1382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383  * to the Reader when its load method is called.
1384  * @constructor
1385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1386  */
1387 Roo.data.MemoryProxy = function(data){
1388     if (data.data) {
1389         data = data.data;
1390     }
1391     Roo.data.MemoryProxy.superclass.constructor.call(this);
1392     this.data = data;
1393 };
1394
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1396     
1397     /**
1398      * Load data from the requested source (in this case an in-memory
1399      * data object passed to the constructor), read the data object into
1400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401      * process that block using the passed callback.
1402      * @param {Object} params This parameter is not used by the MemoryProxy class.
1403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404      * object into a block of Roo.data.Records.
1405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406      * The function must be passed <ul>
1407      * <li>The Record block object</li>
1408      * <li>The "arg" argument from the load function</li>
1409      * <li>A boolean success indicator</li>
1410      * </ul>
1411      * @param {Object} scope The scope in which to call the callback
1412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1413      */
1414     load : function(params, reader, callback, scope, arg){
1415         params = params || {};
1416         var result;
1417         try {
1418             result = reader.readRecords(params.data ? params.data :this.data);
1419         }catch(e){
1420             this.fireEvent("loadexception", this, arg, null, e);
1421             callback.call(scope, null, arg, false);
1422             return;
1423         }
1424         callback.call(scope, result, arg, true);
1425     },
1426     
1427     // private
1428     update : function(params, records){
1429         
1430     }
1431 });/*
1432  * Based on:
1433  * Ext JS Library 1.1.1
1434  * Copyright(c) 2006-2007, Ext JS, LLC.
1435  *
1436  * Originally Released Under LGPL - original licence link has changed is not relivant.
1437  *
1438  * Fork - LGPL
1439  * <script type="text/javascript">
1440  */
1441 /**
1442  * @class Roo.data.HttpProxy
1443  * @extends Roo.data.DataProxy
1444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445  * configured to reference a certain URL.<br><br>
1446  * <p>
1447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448  * from which the running page was served.<br><br>
1449  * <p>
1450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1451  * <p>
1452  * Be aware that to enable the browser to parse an XML document, the server must set
1453  * the Content-Type header in the HTTP response to "text/xml".
1454  * @constructor
1455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457  * will be used to make the request.
1458  */
1459 Roo.data.HttpProxy = function(conn){
1460     Roo.data.HttpProxy.superclass.constructor.call(this);
1461     // is conn a conn config or a real conn?
1462     this.conn = conn;
1463     this.useAjax = !conn || !conn.events;
1464   
1465 };
1466
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468     // thse are take from connection...
1469     
1470     /**
1471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1472      */
1473     /**
1474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475      * extra parameters to each request made by this object. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479      *  to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @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)
1483      */
1484     /**
1485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1486      */
1487      /**
1488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1489      * @type Boolean
1490      */
1491   
1492
1493     /**
1494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1495      * @type Boolean
1496      */
1497     /**
1498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500      * a finer-grained basis than the DataProxy events.
1501      */
1502     getConnection : function(){
1503         return this.useAjax ? Roo.Ajax : this.conn;
1504     },
1505
1506     /**
1507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509      * process that block using the passed callback.
1510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511      * for the request to the remote server.
1512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513      * object into a block of Roo.data.Records.
1514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515      * The function must be passed <ul>
1516      * <li>The Record block object</li>
1517      * <li>The "arg" argument from the load function</li>
1518      * <li>A boolean success indicator</li>
1519      * </ul>
1520      * @param {Object} scope The scope in which to call the callback
1521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1522      */
1523     load : function(params, reader, callback, scope, arg){
1524         if(this.fireEvent("beforeload", this, params) !== false){
1525             var  o = {
1526                 params : params || {},
1527                 request: {
1528                     callback : callback,
1529                     scope : scope,
1530                     arg : arg
1531                 },
1532                 reader: reader,
1533                 callback : this.loadResponse,
1534                 scope: this
1535             };
1536             if(this.useAjax){
1537                 Roo.applyIf(o, this.conn);
1538                 if(this.activeRequest){
1539                     Roo.Ajax.abort(this.activeRequest);
1540                 }
1541                 this.activeRequest = Roo.Ajax.request(o);
1542             }else{
1543                 this.conn.request(o);
1544             }
1545         }else{
1546             callback.call(scope||this, null, arg, false);
1547         }
1548     },
1549
1550     // private
1551     loadResponse : function(o, success, response){
1552         delete this.activeRequest;
1553         if(!success){
1554             this.fireEvent("loadexception", this, o, response);
1555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1556             return;
1557         }
1558         var result;
1559         try {
1560             result = o.reader.read(response);
1561         }catch(e){
1562             this.fireEvent("loadexception", this, o, response, e);
1563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564             return;
1565         }
1566         
1567         this.fireEvent("load", this, o, o.request.arg);
1568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1569     },
1570
1571     // private
1572     update : function(dataSet){
1573
1574     },
1575
1576     // private
1577     updateResponse : function(dataSet){
1578
1579     }
1580 });/*
1581  * Based on:
1582  * Ext JS Library 1.1.1
1583  * Copyright(c) 2006-2007, Ext JS, LLC.
1584  *
1585  * Originally Released Under LGPL - original licence link has changed is not relivant.
1586  *
1587  * Fork - LGPL
1588  * <script type="text/javascript">
1589  */
1590
1591 /**
1592  * @class Roo.data.ScriptTagProxy
1593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594  * other than the originating domain of the running page.<br><br>
1595  * <p>
1596  * <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
1597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1598  * <p>
1599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600  * source code that is used as the source inside a &lt;script> tag.<br><br>
1601  * <p>
1602  * In order for the browser to process the returned data, the server must wrap the data object
1603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605  * depending on whether the callback name was passed:
1606  * <p>
1607  * <pre><code>
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1610 if (cb != null) {
1611     scriptTag = true;
1612     response.setContentType("text/javascript");
1613 } else {
1614     response.setContentType("application/x-json");
1615 }
1616 Writer out = response.getWriter();
1617 if (scriptTag) {
1618     out.write(cb + "(");
1619 }
1620 out.print(dataBlock.toJsonString());
1621 if (scriptTag) {
1622     out.write(");");
1623 }
1624 </pre></code>
1625  *
1626  * @constructor
1627  * @param {Object} config A configuration object.
1628  */
1629 Roo.data.ScriptTagProxy = function(config){
1630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631     Roo.apply(this, config);
1632     this.head = document.getElementsByTagName("head")[0];
1633 };
1634
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1636
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1638     /**
1639      * @cfg {String} url The URL from which to request the data object.
1640      */
1641     /**
1642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1643      */
1644     timeout : 30000,
1645     /**
1646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647      * the server the name of the callback function set up by the load call to process the returned data object.
1648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649      * javascript output which calls this named function passing the data object as its only parameter.
1650      */
1651     callbackParam : "callback",
1652     /**
1653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654      * name to the request.
1655      */
1656     nocache : true,
1657
1658     /**
1659      * Load data from the configured URL, read the data object into
1660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661      * process that block using the passed callback.
1662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663      * for the request to the remote server.
1664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665      * object into a block of Roo.data.Records.
1666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667      * The function must be passed <ul>
1668      * <li>The Record block object</li>
1669      * <li>The "arg" argument from the load function</li>
1670      * <li>A boolean success indicator</li>
1671      * </ul>
1672      * @param {Object} scope The scope in which to call the callback
1673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1674      */
1675     load : function(params, reader, callback, scope, arg){
1676         if(this.fireEvent("beforeload", this, params) !== false){
1677
1678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1679
1680             var url = this.url;
1681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1682             if(this.nocache){
1683                 url += "&_dc=" + (new Date().getTime());
1684             }
1685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1686             var trans = {
1687                 id : transId,
1688                 cb : "stcCallback"+transId,
1689                 scriptId : "stcScript"+transId,
1690                 params : params,
1691                 arg : arg,
1692                 url : url,
1693                 callback : callback,
1694                 scope : scope,
1695                 reader : reader
1696             };
1697             var conn = this;
1698
1699             window[trans.cb] = function(o){
1700                 conn.handleResponse(o, trans);
1701             };
1702
1703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1704
1705             if(this.autoAbort !== false){
1706                 this.abort();
1707             }
1708
1709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1710
1711             var script = document.createElement("script");
1712             script.setAttribute("src", url);
1713             script.setAttribute("type", "text/javascript");
1714             script.setAttribute("id", trans.scriptId);
1715             this.head.appendChild(script);
1716
1717             this.trans = trans;
1718         }else{
1719             callback.call(scope||this, null, arg, false);
1720         }
1721     },
1722
1723     // private
1724     isLoading : function(){
1725         return this.trans ? true : false;
1726     },
1727
1728     /**
1729      * Abort the current server request.
1730      */
1731     abort : function(){
1732         if(this.isLoading()){
1733             this.destroyTrans(this.trans);
1734         }
1735     },
1736
1737     // private
1738     destroyTrans : function(trans, isLoaded){
1739         this.head.removeChild(document.getElementById(trans.scriptId));
1740         clearTimeout(trans.timeoutId);
1741         if(isLoaded){
1742             window[trans.cb] = undefined;
1743             try{
1744                 delete window[trans.cb];
1745             }catch(e){}
1746         }else{
1747             // if hasn't been loaded, wait for load to remove it to prevent script error
1748             window[trans.cb] = function(){
1749                 window[trans.cb] = undefined;
1750                 try{
1751                     delete window[trans.cb];
1752                 }catch(e){}
1753             };
1754         }
1755     },
1756
1757     // private
1758     handleResponse : function(o, trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, true);
1761         var result;
1762         try {
1763             result = trans.reader.readRecords(o);
1764         }catch(e){
1765             this.fireEvent("loadexception", this, o, trans.arg, e);
1766             trans.callback.call(trans.scope||window, null, trans.arg, false);
1767             return;
1768         }
1769         this.fireEvent("load", this, o, trans.arg);
1770         trans.callback.call(trans.scope||window, result, trans.arg, true);
1771     },
1772
1773     // private
1774     handleFailure : function(trans){
1775         this.trans = false;
1776         this.destroyTrans(trans, false);
1777         this.fireEvent("loadexception", this, null, trans.arg);
1778         trans.callback.call(trans.scope||window, null, trans.arg, false);
1779     }
1780 });/*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.data.JsonReader
1793  * @extends Roo.data.DataReader
1794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795  * based on mappings in a provided Roo.data.Record constructor.
1796  * 
1797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798  * in the reply previously. 
1799  * 
1800  * <p>
1801  * Example code:
1802  * <pre><code>
1803 var RecordDef = Roo.data.Record.create([
1804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1806 ]);
1807 var myReader = new Roo.data.JsonReader({
1808     totalProperty: "results",    // The property which contains the total dataset size (optional)
1809     root: "rows",                // The property which contains an Array of row objects
1810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1811 }, RecordDef);
1812 </code></pre>
1813  * <p>
1814  * This would consume a JSON file like this:
1815  * <pre><code>
1816 { 'results': 2, 'rows': [
1817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1819 }
1820 </code></pre>
1821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823  * paged from the remote server.
1824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825  * @cfg {String} root name of the property which contains the Array of row objects.
1826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827  * @cfg {Array} fields Array of field definition objects
1828  * @constructor
1829  * Create a new JsonReader
1830  * @param {Object} meta Metadata configuration options
1831  * @param {Object} recordType Either an Array of field definition objects,
1832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1833  */
1834 Roo.data.JsonReader = function(meta, recordType){
1835     
1836     meta = meta || {};
1837     // set some defaults:
1838     Roo.applyIf(meta, {
1839         totalProperty: 'total',
1840         successProperty : 'success',
1841         root : 'data',
1842         id : 'id'
1843     });
1844     
1845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1846 };
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1848     
1849     readerType : 'Json',
1850     
1851     /**
1852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1853      * Used by Store query builder to append _requestMeta to params.
1854      * 
1855      */
1856     metaFromRemote : false,
1857     /**
1858      * This method is only used by a DataProxy which has retrieved data from a remote server.
1859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860      * @return {Object} data A data block which is used by an Roo.data.Store object as
1861      * a cache of Roo.data.Records.
1862      */
1863     read : function(response){
1864         var json = response.responseText;
1865        
1866         var o = /* eval:var:o */ eval("("+json+")");
1867         if(!o) {
1868             throw {message: "JsonReader.read: Json object not found"};
1869         }
1870         
1871         if(o.metaData){
1872             
1873             delete this.ef;
1874             this.metaFromRemote = true;
1875             this.meta = o.metaData;
1876             this.recordType = Roo.data.Record.create(o.metaData.fields);
1877             this.onMetaChange(this.meta, this.recordType, o);
1878         }
1879         return this.readRecords(o);
1880     },
1881
1882     // private function a store will implement
1883     onMetaChange : function(meta, recordType, o){
1884
1885     },
1886
1887     /**
1888          * @ignore
1889          */
1890     simpleAccess: function(obj, subsc) {
1891         return obj[subsc];
1892     },
1893
1894         /**
1895          * @ignore
1896          */
1897     getJsonAccessor: function(){
1898         var re = /[\[\.]/;
1899         return function(expr) {
1900             try {
1901                 return(re.test(expr))
1902                     ? new Function("obj", "return obj." + expr)
1903                     : function(obj){
1904                         return obj[expr];
1905                     };
1906             } catch(e){}
1907             return Roo.emptyFn;
1908         };
1909     }(),
1910
1911     /**
1912      * Create a data block containing Roo.data.Records from an XML document.
1913      * @param {Object} o An object which contains an Array of row objects in the property specified
1914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915      * which contains the total size of the dataset.
1916      * @return {Object} data A data block which is used by an Roo.data.Store object as
1917      * a cache of Roo.data.Records.
1918      */
1919     readRecords : function(o){
1920         /**
1921          * After any data loads, the raw JSON data is available for further custom processing.
1922          * @type Object
1923          */
1924         this.o = o;
1925         var s = this.meta, Record = this.recordType,
1926             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1927
1928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1929         if (!this.ef) {
1930             if(s.totalProperty) {
1931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1932                 }
1933                 if(s.successProperty) {
1934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1935                 }
1936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1937                 if (s.id) {
1938                         var g = this.getJsonAccessor(s.id);
1939                         this.getId = function(rec) {
1940                                 var r = g(rec);  
1941                                 return (r === undefined || r === "") ? null : r;
1942                         };
1943                 } else {
1944                         this.getId = function(){return null;};
1945                 }
1946             this.ef = [];
1947             for(var jj = 0; jj < fl; jj++){
1948                 f = fi[jj];
1949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950                 this.ef[jj] = this.getJsonAccessor(map);
1951             }
1952         }
1953
1954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955         if(s.totalProperty){
1956             var vt = parseInt(this.getTotal(o), 10);
1957             if(!isNaN(vt)){
1958                 totalRecords = vt;
1959             }
1960         }
1961         if(s.successProperty){
1962             var vs = this.getSuccess(o);
1963             if(vs === false || vs === 'false'){
1964                 success = false;
1965             }
1966         }
1967         var records = [];
1968         for(var i = 0; i < c; i++){
1969                 var n = root[i];
1970             var values = {};
1971             var id = this.getId(n);
1972             for(var j = 0; j < fl; j++){
1973                 f = fi[j];
1974             var v = this.ef[j](n);
1975             if (!f.convert) {
1976                 Roo.log('missing convert for ' + f.name);
1977                 Roo.log(f);
1978                 continue;
1979             }
1980             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1981             }
1982             var record = new Record(values, id);
1983             record.json = n;
1984             records[i] = record;
1985         }
1986         return {
1987             raw : o,
1988             success : success,
1989             records : records,
1990             totalRecords : totalRecords
1991         };
1992     },
1993     // used when loading children.. @see loadDataFromChildren
1994     toLoadData: function(rec)
1995     {
1996         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1997         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1998         return { data : data, total : data.length };
1999         
2000     }
2001 });/*
2002  * Based on:
2003  * Ext JS Library 1.1.1
2004  * Copyright(c) 2006-2007, Ext JS, LLC.
2005  *
2006  * Originally Released Under LGPL - original licence link has changed is not relivant.
2007  *
2008  * Fork - LGPL
2009  * <script type="text/javascript">
2010  */
2011
2012 /**
2013  * @class Roo.data.XmlReader
2014  * @extends Roo.data.DataReader
2015  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2016  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2017  * <p>
2018  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2019  * header in the HTTP response must be set to "text/xml".</em>
2020  * <p>
2021  * Example code:
2022  * <pre><code>
2023 var RecordDef = Roo.data.Record.create([
2024    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2025    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2026 ]);
2027 var myReader = new Roo.data.XmlReader({
2028    totalRecords: "results", // The element which contains the total dataset size (optional)
2029    record: "row",           // The repeated element which contains row information
2030    id: "id"                 // The element within the row that provides an ID for the record (optional)
2031 }, RecordDef);
2032 </code></pre>
2033  * <p>
2034  * This would consume an XML file like this:
2035  * <pre><code>
2036 &lt;?xml?>
2037 &lt;dataset>
2038  &lt;results>2&lt;/results>
2039  &lt;row>
2040    &lt;id>1&lt;/id>
2041    &lt;name>Bill&lt;/name>
2042    &lt;occupation>Gardener&lt;/occupation>
2043  &lt;/row>
2044  &lt;row>
2045    &lt;id>2&lt;/id>
2046    &lt;name>Ben&lt;/name>
2047    &lt;occupation>Horticulturalist&lt;/occupation>
2048  &lt;/row>
2049 &lt;/dataset>
2050 </code></pre>
2051  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2052  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2053  * paged from the remote server.
2054  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2055  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2056  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2057  * a record identifier value.
2058  * @constructor
2059  * Create a new XmlReader
2060  * @param {Object} meta Metadata configuration options
2061  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2062  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2063  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2064  */
2065 Roo.data.XmlReader = function(meta, recordType){
2066     meta = meta || {};
2067     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2068 };
2069 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2070     
2071     readerType : 'Xml',
2072     
2073     /**
2074      * This method is only used by a DataProxy which has retrieved data from a remote server.
2075          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2076          * to contain a method called 'responseXML' that returns an XML document object.
2077      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2078      * a cache of Roo.data.Records.
2079      */
2080     read : function(response){
2081         var doc = response.responseXML;
2082         if(!doc) {
2083             throw {message: "XmlReader.read: XML Document not available"};
2084         }
2085         return this.readRecords(doc);
2086     },
2087
2088     /**
2089      * Create a data block containing Roo.data.Records from an XML document.
2090          * @param {Object} doc A parsed XML document.
2091      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2092      * a cache of Roo.data.Records.
2093      */
2094     readRecords : function(doc){
2095         /**
2096          * After any data loads/reads, the raw XML Document is available for further custom processing.
2097          * @type XMLDocument
2098          */
2099         this.xmlData = doc;
2100         var root = doc.documentElement || doc;
2101         var q = Roo.DomQuery;
2102         var recordType = this.recordType, fields = recordType.prototype.fields;
2103         var sid = this.meta.id;
2104         var totalRecords = 0, success = true;
2105         if(this.meta.totalRecords){
2106             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2107         }
2108         
2109         if(this.meta.success){
2110             var sv = q.selectValue(this.meta.success, root, true);
2111             success = sv !== false && sv !== 'false';
2112         }
2113         var records = [];
2114         var ns = q.select(this.meta.record, root);
2115         for(var i = 0, len = ns.length; i < len; i++) {
2116                 var n = ns[i];
2117                 var values = {};
2118                 var id = sid ? q.selectValue(sid, n) : undefined;
2119                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2120                     var f = fields.items[j];
2121                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2122                     v = f.convert(v);
2123                     values[f.name] = v;
2124                 }
2125                 var record = new recordType(values, id);
2126                 record.node = n;
2127                 records[records.length] = record;
2128             }
2129
2130             return {
2131                 success : success,
2132                 records : records,
2133                 totalRecords : totalRecords || records.length
2134             };
2135     }
2136 });/*
2137  * Based on:
2138  * Ext JS Library 1.1.1
2139  * Copyright(c) 2006-2007, Ext JS, LLC.
2140  *
2141  * Originally Released Under LGPL - original licence link has changed is not relivant.
2142  *
2143  * Fork - LGPL
2144  * <script type="text/javascript">
2145  */
2146
2147 /**
2148  * @class Roo.data.ArrayReader
2149  * @extends Roo.data.DataReader
2150  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2151  * Each element of that Array represents a row of data fields. The
2152  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2153  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2154  * <p>
2155  * Example code:.
2156  * <pre><code>
2157 var RecordDef = Roo.data.Record.create([
2158     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2159     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2160 ]);
2161 var myReader = new Roo.data.ArrayReader({
2162     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2163 }, RecordDef);
2164 </code></pre>
2165  * <p>
2166  * This would consume an Array like this:
2167  * <pre><code>
2168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2169   </code></pre>
2170  
2171  * @constructor
2172  * Create a new JsonReader
2173  * @param {Object} meta Metadata configuration options.
2174  * @param {Object|Array} recordType Either an Array of field definition objects
2175  * 
2176  * @cfg {Array} fields Array of field definition objects
2177  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2178  * as specified to {@link Roo.data.Record#create},
2179  * or an {@link Roo.data.Record} object
2180  *
2181  * 
2182  * created using {@link Roo.data.Record#create}.
2183  */
2184 Roo.data.ArrayReader = function(meta, recordType)
2185 {    
2186     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2187 };
2188
2189 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2190     
2191       /**
2192      * Create a data block containing Roo.data.Records from an XML document.
2193      * @param {Object} o An Array of row objects which represents the dataset.
2194      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2195      * a cache of Roo.data.Records.
2196      */
2197     readRecords : function(o)
2198     {
2199         var sid = this.meta ? this.meta.id : null;
2200         var recordType = this.recordType, fields = recordType.prototype.fields;
2201         var records = [];
2202         var root = o;
2203         for(var i = 0; i < root.length; i++){
2204             var n = root[i];
2205             var values = {};
2206             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2207             for(var j = 0, jlen = fields.length; j < jlen; j++){
2208                 var f = fields.items[j];
2209                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2210                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2211                 v = f.convert(v);
2212                 values[f.name] = v;
2213             }
2214             var record = new recordType(values, id);
2215             record.json = n;
2216             records[records.length] = record;
2217         }
2218         return {
2219             records : records,
2220             totalRecords : records.length
2221         };
2222     },
2223     // used when loading children.. @see loadDataFromChildren
2224     toLoadData: function(rec)
2225     {
2226         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2227         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2228         
2229     }
2230     
2231     
2232 });/*
2233  * Based on:
2234  * Ext JS Library 1.1.1
2235  * Copyright(c) 2006-2007, Ext JS, LLC.
2236  *
2237  * Originally Released Under LGPL - original licence link has changed is not relivant.
2238  *
2239  * Fork - LGPL
2240  * <script type="text/javascript">
2241  */
2242
2243
2244 /**
2245  * @class Roo.data.Tree
2246  * @extends Roo.util.Observable
2247  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2248  * in the tree have most standard DOM functionality.
2249  * @constructor
2250  * @param {Node} root (optional) The root node
2251  */
2252 Roo.data.Tree = function(root){
2253    this.nodeHash = {};
2254    /**
2255     * The root node for this tree
2256     * @type Node
2257     */
2258    this.root = null;
2259    if(root){
2260        this.setRootNode(root);
2261    }
2262    this.addEvents({
2263        /**
2264         * @event append
2265         * Fires when a new child node is appended to a node in this tree.
2266         * @param {Tree} tree The owner tree
2267         * @param {Node} parent The parent node
2268         * @param {Node} node The newly appended node
2269         * @param {Number} index The index of the newly appended node
2270         */
2271        "append" : true,
2272        /**
2273         * @event remove
2274         * Fires when a child node is removed from a node in this tree.
2275         * @param {Tree} tree The owner tree
2276         * @param {Node} parent The parent node
2277         * @param {Node} node The child node removed
2278         */
2279        "remove" : true,
2280        /**
2281         * @event move
2282         * Fires when a node is moved to a new location in the tree
2283         * @param {Tree} tree The owner tree
2284         * @param {Node} node The node moved
2285         * @param {Node} oldParent The old parent of this node
2286         * @param {Node} newParent The new parent of this node
2287         * @param {Number} index The index it was moved to
2288         */
2289        "move" : true,
2290        /**
2291         * @event insert
2292         * Fires when a new child node is inserted in a node in this tree.
2293         * @param {Tree} tree The owner tree
2294         * @param {Node} parent The parent node
2295         * @param {Node} node The child node inserted
2296         * @param {Node} refNode The child node the node was inserted before
2297         */
2298        "insert" : true,
2299        /**
2300         * @event beforeappend
2301         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2302         * @param {Tree} tree The owner tree
2303         * @param {Node} parent The parent node
2304         * @param {Node} node The child node to be appended
2305         */
2306        "beforeappend" : true,
2307        /**
2308         * @event beforeremove
2309         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2310         * @param {Tree} tree The owner tree
2311         * @param {Node} parent The parent node
2312         * @param {Node} node The child node to be removed
2313         */
2314        "beforeremove" : true,
2315        /**
2316         * @event beforemove
2317         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} node The node being moved
2320         * @param {Node} oldParent The parent of the node
2321         * @param {Node} newParent The new parent the node is moving to
2322         * @param {Number} index The index it is being moved to
2323         */
2324        "beforemove" : true,
2325        /**
2326         * @event beforeinsert
2327         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2328         * @param {Tree} tree The owner tree
2329         * @param {Node} parent The parent node
2330         * @param {Node} node The child node to be inserted
2331         * @param {Node} refNode The child node the node is being inserted before
2332         */
2333        "beforeinsert" : true
2334    });
2335
2336     Roo.data.Tree.superclass.constructor.call(this);
2337 };
2338
2339 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2340     pathSeparator: "/",
2341
2342     proxyNodeEvent : function(){
2343         return this.fireEvent.apply(this, arguments);
2344     },
2345
2346     /**
2347      * Returns the root node for this tree.
2348      * @return {Node}
2349      */
2350     getRootNode : function(){
2351         return this.root;
2352     },
2353
2354     /**
2355      * Sets the root node for this tree.
2356      * @param {Node} node
2357      * @return {Node}
2358      */
2359     setRootNode : function(node){
2360         this.root = node;
2361         node.ownerTree = this;
2362         node.isRoot = true;
2363         this.registerNode(node);
2364         return node;
2365     },
2366
2367     /**
2368      * Gets a node in this tree by its id.
2369      * @param {String} id
2370      * @return {Node}
2371      */
2372     getNodeById : function(id){
2373         return this.nodeHash[id];
2374     },
2375
2376     registerNode : function(node){
2377         this.nodeHash[node.id] = node;
2378     },
2379
2380     unregisterNode : function(node){
2381         delete this.nodeHash[node.id];
2382     },
2383
2384     toString : function(){
2385         return "[Tree"+(this.id?" "+this.id:"")+"]";
2386     }
2387 });
2388
2389 /**
2390  * @class Roo.data.Node
2391  * @extends Roo.util.Observable
2392  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2393  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2394  * @constructor
2395  * @param {Object} attributes The attributes/config for the node
2396  */
2397 Roo.data.Node = function(attributes){
2398     /**
2399      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2400      * @type {Object}
2401      */
2402     this.attributes = attributes || {};
2403     this.leaf = this.attributes.leaf;
2404     /**
2405      * The node id. @type String
2406      */
2407     this.id = this.attributes.id;
2408     if(!this.id){
2409         this.id = Roo.id(null, "ynode-");
2410         this.attributes.id = this.id;
2411     }
2412      
2413     
2414     /**
2415      * All child nodes of this node. @type Array
2416      */
2417     this.childNodes = [];
2418     if(!this.childNodes.indexOf){ // indexOf is a must
2419         this.childNodes.indexOf = function(o){
2420             for(var i = 0, len = this.length; i < len; i++){
2421                 if(this[i] == o) {
2422                     return i;
2423                 }
2424             }
2425             return -1;
2426         };
2427     }
2428     /**
2429      * The parent node for this node. @type Node
2430      */
2431     this.parentNode = null;
2432     /**
2433      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2434      */
2435     this.firstChild = null;
2436     /**
2437      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.lastChild = null;
2440     /**
2441      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2442      */
2443     this.previousSibling = null;
2444     /**
2445      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.nextSibling = null;
2448
2449     this.addEvents({
2450        /**
2451         * @event append
2452         * Fires when a new child node is appended
2453         * @param {Tree} tree The owner tree
2454         * @param {Node} this This node
2455         * @param {Node} node The newly appended node
2456         * @param {Number} index The index of the newly appended node
2457         */
2458        "append" : true,
2459        /**
2460         * @event remove
2461         * Fires when a child node is removed
2462         * @param {Tree} tree The owner tree
2463         * @param {Node} this This node
2464         * @param {Node} node The removed node
2465         */
2466        "remove" : true,
2467        /**
2468         * @event move
2469         * Fires when this node is moved to a new location in the tree
2470         * @param {Tree} tree The owner tree
2471         * @param {Node} this This node
2472         * @param {Node} oldParent The old parent of this node
2473         * @param {Node} newParent The new parent of this node
2474         * @param {Number} index The index it was moved to
2475         */
2476        "move" : true,
2477        /**
2478         * @event insert
2479         * Fires when a new child node is inserted.
2480         * @param {Tree} tree The owner tree
2481         * @param {Node} this This node
2482         * @param {Node} node The child node inserted
2483         * @param {Node} refNode The child node the node was inserted before
2484         */
2485        "insert" : true,
2486        /**
2487         * @event beforeappend
2488         * Fires before a new child is appended, return false to cancel the append.
2489         * @param {Tree} tree The owner tree
2490         * @param {Node} this This node
2491         * @param {Node} node The child node to be appended
2492         */
2493        "beforeappend" : true,
2494        /**
2495         * @event beforeremove
2496         * Fires before a child is removed, return false to cancel the remove.
2497         * @param {Tree} tree The owner tree
2498         * @param {Node} this This node
2499         * @param {Node} node The child node to be removed
2500         */
2501        "beforeremove" : true,
2502        /**
2503         * @event beforemove
2504         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} oldParent The parent of this node
2508         * @param {Node} newParent The new parent this node is moving to
2509         * @param {Number} index The index it is being moved to
2510         */
2511        "beforemove" : true,
2512        /**
2513         * @event beforeinsert
2514         * Fires before a new child is inserted, return false to cancel the insert.
2515         * @param {Tree} tree The owner tree
2516         * @param {Node} this This node
2517         * @param {Node} node The child node to be inserted
2518         * @param {Node} refNode The child node the node is being inserted before
2519         */
2520        "beforeinsert" : true
2521    });
2522     this.listeners = this.attributes.listeners;
2523     Roo.data.Node.superclass.constructor.call(this);
2524 };
2525
2526 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2527     fireEvent : function(evtName){
2528         // first do standard event for this node
2529         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2530             return false;
2531         }
2532         // then bubble it up to the tree if the event wasn't cancelled
2533         var ot = this.getOwnerTree();
2534         if(ot){
2535             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2536                 return false;
2537             }
2538         }
2539         return true;
2540     },
2541
2542     /**
2543      * Returns true if this node is a leaf
2544      * @return {Boolean}
2545      */
2546     isLeaf : function(){
2547         return this.leaf === true;
2548     },
2549
2550     // private
2551     setFirstChild : function(node){
2552         this.firstChild = node;
2553     },
2554
2555     //private
2556     setLastChild : function(node){
2557         this.lastChild = node;
2558     },
2559
2560
2561     /**
2562      * Returns true if this node is the last child of its parent
2563      * @return {Boolean}
2564      */
2565     isLast : function(){
2566        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2567     },
2568
2569     /**
2570      * Returns true if this node is the first child of its parent
2571      * @return {Boolean}
2572      */
2573     isFirst : function(){
2574        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2575     },
2576
2577     hasChildNodes : function(){
2578         return !this.isLeaf() && this.childNodes.length > 0;
2579     },
2580
2581     /**
2582      * Insert node(s) as the last child node of this node.
2583      * @param {Node/Array} node The node or Array of nodes to append
2584      * @return {Node} The appended node if single append, or null if an array was passed
2585      */
2586     appendChild : function(node){
2587         var multi = false;
2588         if(node instanceof Array){
2589             multi = node;
2590         }else if(arguments.length > 1){
2591             multi = arguments;
2592         }
2593         
2594         // if passed an array or multiple args do them one by one
2595         if(multi){
2596             for(var i = 0, len = multi.length; i < len; i++) {
2597                 this.appendChild(multi[i]);
2598             }
2599         }else{
2600             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2601                 return false;
2602             }
2603             var index = this.childNodes.length;
2604             var oldParent = node.parentNode;
2605             // it's a move, make sure we move it cleanly
2606             if(oldParent){
2607                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2608                     return false;
2609                 }
2610                 oldParent.removeChild(node);
2611             }
2612             
2613             index = this.childNodes.length;
2614             if(index == 0){
2615                 this.setFirstChild(node);
2616             }
2617             this.childNodes.push(node);
2618             node.parentNode = this;
2619             var ps = this.childNodes[index-1];
2620             if(ps){
2621                 node.previousSibling = ps;
2622                 ps.nextSibling = node;
2623             }else{
2624                 node.previousSibling = null;
2625             }
2626             node.nextSibling = null;
2627             this.setLastChild(node);
2628             node.setOwnerTree(this.getOwnerTree());
2629             this.fireEvent("append", this.ownerTree, this, node, index);
2630             if(this.ownerTree) {
2631                 this.ownerTree.fireEvent("appendnode", this, node, index);
2632             }
2633             if(oldParent){
2634                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2635             }
2636             return node;
2637         }
2638     },
2639
2640     /**
2641      * Removes a child node from this node.
2642      * @param {Node} node The node to remove
2643      * @return {Node} The removed node
2644      */
2645     removeChild : function(node){
2646         var index = this.childNodes.indexOf(node);
2647         if(index == -1){
2648             return false;
2649         }
2650         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2651             return false;
2652         }
2653
2654         // remove it from childNodes collection
2655         this.childNodes.splice(index, 1);
2656
2657         // update siblings
2658         if(node.previousSibling){
2659             node.previousSibling.nextSibling = node.nextSibling;
2660         }
2661         if(node.nextSibling){
2662             node.nextSibling.previousSibling = node.previousSibling;
2663         }
2664
2665         // update child refs
2666         if(this.firstChild == node){
2667             this.setFirstChild(node.nextSibling);
2668         }
2669         if(this.lastChild == node){
2670             this.setLastChild(node.previousSibling);
2671         }
2672
2673         node.setOwnerTree(null);
2674         // clear any references from the node
2675         node.parentNode = null;
2676         node.previousSibling = null;
2677         node.nextSibling = null;
2678         this.fireEvent("remove", this.ownerTree, this, node);
2679         return node;
2680     },
2681
2682     /**
2683      * Inserts the first node before the second node in this nodes childNodes collection.
2684      * @param {Node} node The node to insert
2685      * @param {Node} refNode The node to insert before (if null the node is appended)
2686      * @return {Node} The inserted node
2687      */
2688     insertBefore : function(node, refNode){
2689         if(!refNode){ // like standard Dom, refNode can be null for append
2690             return this.appendChild(node);
2691         }
2692         // nothing to do
2693         if(node == refNode){
2694             return false;
2695         }
2696
2697         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2698             return false;
2699         }
2700         var index = this.childNodes.indexOf(refNode);
2701         var oldParent = node.parentNode;
2702         var refIndex = index;
2703
2704         // when moving internally, indexes will change after remove
2705         if(oldParent == this && this.childNodes.indexOf(node) < index){
2706             refIndex--;
2707         }
2708
2709         // it's a move, make sure we move it cleanly
2710         if(oldParent){
2711             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2712                 return false;
2713             }
2714             oldParent.removeChild(node);
2715         }
2716         if(refIndex == 0){
2717             this.setFirstChild(node);
2718         }
2719         this.childNodes.splice(refIndex, 0, node);
2720         node.parentNode = this;
2721         var ps = this.childNodes[refIndex-1];
2722         if(ps){
2723             node.previousSibling = ps;
2724             ps.nextSibling = node;
2725         }else{
2726             node.previousSibling = null;
2727         }
2728         node.nextSibling = refNode;
2729         refNode.previousSibling = node;
2730         node.setOwnerTree(this.getOwnerTree());
2731         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2732         if(oldParent){
2733             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2734         }
2735         return node;
2736     },
2737
2738     /**
2739      * Returns the child node at the specified index.
2740      * @param {Number} index
2741      * @return {Node}
2742      */
2743     item : function(index){
2744         return this.childNodes[index];
2745     },
2746
2747     /**
2748      * Replaces one child node in this node with another.
2749      * @param {Node} newChild The replacement node
2750      * @param {Node} oldChild The node to replace
2751      * @return {Node} The replaced node
2752      */
2753     replaceChild : function(newChild, oldChild){
2754         this.insertBefore(newChild, oldChild);
2755         this.removeChild(oldChild);
2756         return oldChild;
2757     },
2758
2759     /**
2760      * Returns the index of a child node
2761      * @param {Node} node
2762      * @return {Number} The index of the node or -1 if it was not found
2763      */
2764     indexOf : function(child){
2765         return this.childNodes.indexOf(child);
2766     },
2767
2768     /**
2769      * Returns the tree this node is in.
2770      * @return {Tree}
2771      */
2772     getOwnerTree : function(){
2773         // if it doesn't have one, look for one
2774         if(!this.ownerTree){
2775             var p = this;
2776             while(p){
2777                 if(p.ownerTree){
2778                     this.ownerTree = p.ownerTree;
2779                     break;
2780                 }
2781                 p = p.parentNode;
2782             }
2783         }
2784         return this.ownerTree;
2785     },
2786
2787     /**
2788      * Returns depth of this node (the root node has a depth of 0)
2789      * @return {Number}
2790      */
2791     getDepth : function(){
2792         var depth = 0;
2793         var p = this;
2794         while(p.parentNode){
2795             ++depth;
2796             p = p.parentNode;
2797         }
2798         return depth;
2799     },
2800
2801     // private
2802     setOwnerTree : function(tree){
2803         // if it's move, we need to update everyone
2804         if(tree != this.ownerTree){
2805             if(this.ownerTree){
2806                 this.ownerTree.unregisterNode(this);
2807             }
2808             this.ownerTree = tree;
2809             var cs = this.childNodes;
2810             for(var i = 0, len = cs.length; i < len; i++) {
2811                 cs[i].setOwnerTree(tree);
2812             }
2813             if(tree){
2814                 tree.registerNode(this);
2815             }
2816         }
2817     },
2818
2819     /**
2820      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2821      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2822      * @return {String} The path
2823      */
2824     getPath : function(attr){
2825         attr = attr || "id";
2826         var p = this.parentNode;
2827         var b = [this.attributes[attr]];
2828         while(p){
2829             b.unshift(p.attributes[attr]);
2830             p = p.parentNode;
2831         }
2832         var sep = this.getOwnerTree().pathSeparator;
2833         return sep + b.join(sep);
2834     },
2835
2836     /**
2837      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2838      * function call will be the scope provided or the current node. The arguments to the function
2839      * will be the args provided or the current node. If the function returns false at any point,
2840      * the bubble is stopped.
2841      * @param {Function} fn The function to call
2842      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2843      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2844      */
2845     bubble : function(fn, scope, args){
2846         var p = this;
2847         while(p){
2848             if(fn.call(scope || p, args || p) === false){
2849                 break;
2850             }
2851             p = p.parentNode;
2852         }
2853     },
2854
2855     /**
2856      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2857      * function call will be the scope provided or the current node. The arguments to the function
2858      * will be the args provided or the current node. If the function returns false at any point,
2859      * the cascade is stopped on that branch.
2860      * @param {Function} fn The function to call
2861      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2862      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2863      */
2864     cascade : function(fn, scope, args){
2865         if(fn.call(scope || this, args || this) !== false){
2866             var cs = this.childNodes;
2867             for(var i = 0, len = cs.length; i < len; i++) {
2868                 cs[i].cascade(fn, scope, args);
2869             }
2870         }
2871     },
2872
2873     /**
2874      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2875      * function call will be the scope provided or the current node. The arguments to the function
2876      * will be the args provided or the current node. If the function returns false at any point,
2877      * the iteration stops.
2878      * @param {Function} fn The function to call
2879      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2880      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2881      */
2882     eachChild : function(fn, scope, args){
2883         var cs = this.childNodes;
2884         for(var i = 0, len = cs.length; i < len; i++) {
2885                 if(fn.call(scope || this, args || cs[i]) === false){
2886                     break;
2887                 }
2888         }
2889     },
2890
2891     /**
2892      * Finds the first child that has the attribute with the specified value.
2893      * @param {String} attribute The attribute name
2894      * @param {Mixed} value The value to search for
2895      * @return {Node} The found child or null if none was found
2896      */
2897     findChild : function(attribute, value){
2898         var cs = this.childNodes;
2899         for(var i = 0, len = cs.length; i < len; i++) {
2900                 if(cs[i].attributes[attribute] == value){
2901                     return cs[i];
2902                 }
2903         }
2904         return null;
2905     },
2906
2907     /**
2908      * Finds the first child by a custom function. The child matches if the function passed
2909      * returns true.
2910      * @param {Function} fn
2911      * @param {Object} scope (optional)
2912      * @return {Node} The found child or null if none was found
2913      */
2914     findChildBy : function(fn, scope){
2915         var cs = this.childNodes;
2916         for(var i = 0, len = cs.length; i < len; i++) {
2917                 if(fn.call(scope||cs[i], cs[i]) === true){
2918                     return cs[i];
2919                 }
2920         }
2921         return null;
2922     },
2923
2924     /**
2925      * Sorts this nodes children using the supplied sort function
2926      * @param {Function} fn
2927      * @param {Object} scope (optional)
2928      */
2929     sort : function(fn, scope){
2930         var cs = this.childNodes;
2931         var len = cs.length;
2932         if(len > 0){
2933             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2934             cs.sort(sortFn);
2935             for(var i = 0; i < len; i++){
2936                 var n = cs[i];
2937                 n.previousSibling = cs[i-1];
2938                 n.nextSibling = cs[i+1];
2939                 if(i == 0){
2940                     this.setFirstChild(n);
2941                 }
2942                 if(i == len-1){
2943                     this.setLastChild(n);
2944                 }
2945             }
2946         }
2947     },
2948
2949     /**
2950      * Returns true if this node is an ancestor (at any point) of the passed node.
2951      * @param {Node} node
2952      * @return {Boolean}
2953      */
2954     contains : function(node){
2955         return node.isAncestor(this);
2956     },
2957
2958     /**
2959      * Returns true if the passed node is an ancestor (at any point) of this node.
2960      * @param {Node} node
2961      * @return {Boolean}
2962      */
2963     isAncestor : function(node){
2964         var p = this.parentNode;
2965         while(p){
2966             if(p == node){
2967                 return true;
2968             }
2969             p = p.parentNode;
2970         }
2971         return false;
2972     },
2973
2974     toString : function(){
2975         return "[Node"+(this.id?" "+this.id:"")+"]";
2976     }
2977 });/*
2978  * Based on:
2979  * Ext JS Library 1.1.1
2980  * Copyright(c) 2006-2007, Ext JS, LLC.
2981  *
2982  * Originally Released Under LGPL - original licence link has changed is not relivant.
2983  *
2984  * Fork - LGPL
2985  * <script type="text/javascript">
2986  */
2987
2988
2989 /**
2990  * @class Roo.Shadow
2991  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2992  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2993  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2994  * @constructor
2995  * Create a new Shadow
2996  * @param {Object} config The config object
2997  */
2998 Roo.Shadow = function(config){
2999     Roo.apply(this, config);
3000     if(typeof this.mode != "string"){
3001         this.mode = this.defaultMode;
3002     }
3003     var o = this.offset, a = {h: 0};
3004     var rad = Math.floor(this.offset/2);
3005     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3006         case "drop":
3007             a.w = 0;
3008             a.l = a.t = o;
3009             a.t -= 1;
3010             if(Roo.isIE){
3011                 a.l -= this.offset + rad;
3012                 a.t -= this.offset + rad;
3013                 a.w -= rad;
3014                 a.h -= rad;
3015                 a.t += 1;
3016             }
3017         break;
3018         case "sides":
3019             a.w = (o*2);
3020             a.l = -o;
3021             a.t = o-1;
3022             if(Roo.isIE){
3023                 a.l -= (this.offset - rad);
3024                 a.t -= this.offset + rad;
3025                 a.l += 1;
3026                 a.w -= (this.offset - rad)*2;
3027                 a.w -= rad + 1;
3028                 a.h -= 1;
3029             }
3030         break;
3031         case "frame":
3032             a.w = a.h = (o*2);
3033             a.l = a.t = -o;
3034             a.t += 1;
3035             a.h -= 2;
3036             if(Roo.isIE){
3037                 a.l -= (this.offset - rad);
3038                 a.t -= (this.offset - rad);
3039                 a.l += 1;
3040                 a.w -= (this.offset + rad + 1);
3041                 a.h -= (this.offset + rad);
3042                 a.h += 1;
3043             }
3044         break;
3045     };
3046
3047     this.adjusts = a;
3048 };
3049
3050 Roo.Shadow.prototype = {
3051     /**
3052      * @cfg {String} mode
3053      * The shadow display mode.  Supports the following options:<br />
3054      * sides: Shadow displays on both sides and bottom only<br />
3055      * frame: Shadow displays equally on all four sides<br />
3056      * drop: Traditional bottom-right drop shadow (default)
3057      */
3058     mode: false,
3059     /**
3060      * @cfg {String} offset
3061      * The number of pixels to offset the shadow from the element (defaults to 4)
3062      */
3063     offset: 4,
3064
3065     // private
3066     defaultMode: "drop",
3067
3068     /**
3069      * Displays the shadow under the target element
3070      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3071      */
3072     show : function(target){
3073         target = Roo.get(target);
3074         if(!this.el){
3075             this.el = Roo.Shadow.Pool.pull();
3076             if(this.el.dom.nextSibling != target.dom){
3077                 this.el.insertBefore(target);
3078             }
3079         }
3080         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3081         if(Roo.isIE){
3082             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3083         }
3084         this.realign(
3085             target.getLeft(true),
3086             target.getTop(true),
3087             target.getWidth(),
3088             target.getHeight()
3089         );
3090         this.el.dom.style.display = "block";
3091     },
3092
3093     /**
3094      * Returns true if the shadow is visible, else false
3095      */
3096     isVisible : function(){
3097         return this.el ? true : false;  
3098     },
3099
3100     /**
3101      * Direct alignment when values are already available. Show must be called at least once before
3102      * calling this method to ensure it is initialized.
3103      * @param {Number} left The target element left position
3104      * @param {Number} top The target element top position
3105      * @param {Number} width The target element width
3106      * @param {Number} height The target element height
3107      */
3108     realign : function(l, t, w, h){
3109         if(!this.el){
3110             return;
3111         }
3112         var a = this.adjusts, d = this.el.dom, s = d.style;
3113         var iea = 0;
3114         s.left = (l+a.l)+"px";
3115         s.top = (t+a.t)+"px";
3116         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3117  
3118         if(s.width != sws || s.height != shs){
3119             s.width = sws;
3120             s.height = shs;
3121             if(!Roo.isIE){
3122                 var cn = d.childNodes;
3123                 var sww = Math.max(0, (sw-12))+"px";
3124                 cn[0].childNodes[1].style.width = sww;
3125                 cn[1].childNodes[1].style.width = sww;
3126                 cn[2].childNodes[1].style.width = sww;
3127                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3128             }
3129         }
3130     },
3131
3132     /**
3133      * Hides this shadow
3134      */
3135     hide : function(){
3136         if(this.el){
3137             this.el.dom.style.display = "none";
3138             Roo.Shadow.Pool.push(this.el);
3139             delete this.el;
3140         }
3141     },
3142
3143     /**
3144      * Adjust the z-index of this shadow
3145      * @param {Number} zindex The new z-index
3146      */
3147     setZIndex : function(z){
3148         this.zIndex = z;
3149         if(this.el){
3150             this.el.setStyle("z-index", z);
3151         }
3152     }
3153 };
3154
3155 // Private utility class that manages the internal Shadow cache
3156 Roo.Shadow.Pool = function(){
3157     var p = [];
3158     var markup = Roo.isIE ?
3159                  '<div class="x-ie-shadow"></div>' :
3160                  '<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>';
3161     return {
3162         pull : function(){
3163             var sh = p.shift();
3164             if(!sh){
3165                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3166                 sh.autoBoxAdjust = false;
3167             }
3168             return sh;
3169         },
3170
3171         push : function(sh){
3172             p.push(sh);
3173         }
3174     };
3175 }();/*
3176  * Based on:
3177  * Ext JS Library 1.1.1
3178  * Copyright(c) 2006-2007, Ext JS, LLC.
3179  *
3180  * Originally Released Under LGPL - original licence link has changed is not relivant.
3181  *
3182  * Fork - LGPL
3183  * <script type="text/javascript">
3184  */
3185
3186
3187 /**
3188  * @class Roo.SplitBar
3189  * @extends Roo.util.Observable
3190  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3191  * <br><br>
3192  * Usage:
3193  * <pre><code>
3194 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3195                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3196 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3197 split.minSize = 100;
3198 split.maxSize = 600;
3199 split.animate = true;
3200 split.on('moved', splitterMoved);
3201 </code></pre>
3202  * @constructor
3203  * Create a new SplitBar
3204  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3205  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3206  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3207  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3208                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3209                         position of the SplitBar).
3210  */
3211 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3212     
3213     /** @private */
3214     this.el = Roo.get(dragElement, true);
3215     this.el.dom.unselectable = "on";
3216     /** @private */
3217     this.resizingEl = Roo.get(resizingElement, true);
3218
3219     /**
3220      * @private
3221      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3222      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3223      * @type Number
3224      */
3225     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3226     
3227     /**
3228      * The minimum size of the resizing element. (Defaults to 0)
3229      * @type Number
3230      */
3231     this.minSize = 0;
3232     
3233     /**
3234      * The maximum size of the resizing element. (Defaults to 2000)
3235      * @type Number
3236      */
3237     this.maxSize = 2000;
3238     
3239     /**
3240      * Whether to animate the transition to the new size
3241      * @type Boolean
3242      */
3243     this.animate = false;
3244     
3245     /**
3246      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3247      * @type Boolean
3248      */
3249     this.useShim = false;
3250     
3251     /** @private */
3252     this.shim = null;
3253     
3254     if(!existingProxy){
3255         /** @private */
3256         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3257     }else{
3258         this.proxy = Roo.get(existingProxy).dom;
3259     }
3260     /** @private */
3261     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3262     
3263     /** @private */
3264     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3265     
3266     /** @private */
3267     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3268     
3269     /** @private */
3270     this.dragSpecs = {};
3271     
3272     /**
3273      * @private The adapter to use to positon and resize elements
3274      */
3275     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3276     this.adapter.init(this);
3277     
3278     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3279         /** @private */
3280         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3281         this.el.addClass("x-splitbar-h");
3282     }else{
3283         /** @private */
3284         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3285         this.el.addClass("x-splitbar-v");
3286     }
3287     
3288     this.addEvents({
3289         /**
3290          * @event resize
3291          * Fires when the splitter is moved (alias for {@link #event-moved})
3292          * @param {Roo.SplitBar} this
3293          * @param {Number} newSize the new width or height
3294          */
3295         "resize" : true,
3296         /**
3297          * @event moved
3298          * Fires when the splitter is moved
3299          * @param {Roo.SplitBar} this
3300          * @param {Number} newSize the new width or height
3301          */
3302         "moved" : true,
3303         /**
3304          * @event beforeresize
3305          * Fires before the splitter is dragged
3306          * @param {Roo.SplitBar} this
3307          */
3308         "beforeresize" : true,
3309
3310         "beforeapply" : true
3311     });
3312
3313     Roo.util.Observable.call(this);
3314 };
3315
3316 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3317     onStartProxyDrag : function(x, y){
3318         this.fireEvent("beforeresize", this);
3319         if(!this.overlay){
3320             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3321             o.unselectable();
3322             o.enableDisplayMode("block");
3323             // all splitbars share the same overlay
3324             Roo.SplitBar.prototype.overlay = o;
3325         }
3326         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3327         this.overlay.show();
3328         Roo.get(this.proxy).setDisplayed("block");
3329         var size = this.adapter.getElementSize(this);
3330         this.activeMinSize = this.getMinimumSize();;
3331         this.activeMaxSize = this.getMaximumSize();;
3332         var c1 = size - this.activeMinSize;
3333         var c2 = Math.max(this.activeMaxSize - size, 0);
3334         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3335             this.dd.resetConstraints();
3336             this.dd.setXConstraint(
3337                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3338                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3339             );
3340             this.dd.setYConstraint(0, 0);
3341         }else{
3342             this.dd.resetConstraints();
3343             this.dd.setXConstraint(0, 0);
3344             this.dd.setYConstraint(
3345                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3346                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3347             );
3348          }
3349         this.dragSpecs.startSize = size;
3350         this.dragSpecs.startPoint = [x, y];
3351         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3352     },
3353     
3354     /** 
3355      * @private Called after the drag operation by the DDProxy
3356      */
3357     onEndProxyDrag : function(e){
3358         Roo.get(this.proxy).setDisplayed(false);
3359         var endPoint = Roo.lib.Event.getXY(e);
3360         if(this.overlay){
3361             this.overlay.hide();
3362         }
3363         var newSize;
3364         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3365             newSize = this.dragSpecs.startSize + 
3366                 (this.placement == Roo.SplitBar.LEFT ?
3367                     endPoint[0] - this.dragSpecs.startPoint[0] :
3368                     this.dragSpecs.startPoint[0] - endPoint[0]
3369                 );
3370         }else{
3371             newSize = this.dragSpecs.startSize + 
3372                 (this.placement == Roo.SplitBar.TOP ?
3373                     endPoint[1] - this.dragSpecs.startPoint[1] :
3374                     this.dragSpecs.startPoint[1] - endPoint[1]
3375                 );
3376         }
3377         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3378         if(newSize != this.dragSpecs.startSize){
3379             if(this.fireEvent('beforeapply', this, newSize) !== false){
3380                 this.adapter.setElementSize(this, newSize);
3381                 this.fireEvent("moved", this, newSize);
3382                 this.fireEvent("resize", this, newSize);
3383             }
3384         }
3385     },
3386     
3387     /**
3388      * Get the adapter this SplitBar uses
3389      * @return The adapter object
3390      */
3391     getAdapter : function(){
3392         return this.adapter;
3393     },
3394     
3395     /**
3396      * Set the adapter this SplitBar uses
3397      * @param {Object} adapter A SplitBar adapter object
3398      */
3399     setAdapter : function(adapter){
3400         this.adapter = adapter;
3401         this.adapter.init(this);
3402     },
3403     
3404     /**
3405      * Gets the minimum size for the resizing element
3406      * @return {Number} The minimum size
3407      */
3408     getMinimumSize : function(){
3409         return this.minSize;
3410     },
3411     
3412     /**
3413      * Sets the minimum size for the resizing element
3414      * @param {Number} minSize The minimum size
3415      */
3416     setMinimumSize : function(minSize){
3417         this.minSize = minSize;
3418     },
3419     
3420     /**
3421      * Gets the maximum size for the resizing element
3422      * @return {Number} The maximum size
3423      */
3424     getMaximumSize : function(){
3425         return this.maxSize;
3426     },
3427     
3428     /**
3429      * Sets the maximum size for the resizing element
3430      * @param {Number} maxSize The maximum size
3431      */
3432     setMaximumSize : function(maxSize){
3433         this.maxSize = maxSize;
3434     },
3435     
3436     /**
3437      * Sets the initialize size for the resizing element
3438      * @param {Number} size The initial size
3439      */
3440     setCurrentSize : function(size){
3441         var oldAnimate = this.animate;
3442         this.animate = false;
3443         this.adapter.setElementSize(this, size);
3444         this.animate = oldAnimate;
3445     },
3446     
3447     /**
3448      * Destroy this splitbar. 
3449      * @param {Boolean} removeEl True to remove the element
3450      */
3451     destroy : function(removeEl){
3452         if(this.shim){
3453             this.shim.remove();
3454         }
3455         this.dd.unreg();
3456         this.proxy.parentNode.removeChild(this.proxy);
3457         if(removeEl){
3458             this.el.remove();
3459         }
3460     }
3461 });
3462
3463 /**
3464  * @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.
3465  */
3466 Roo.SplitBar.createProxy = function(dir){
3467     var proxy = new Roo.Element(document.createElement("div"));
3468     proxy.unselectable();
3469     var cls = 'x-splitbar-proxy';
3470     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3471     document.body.appendChild(proxy.dom);
3472     return proxy.dom;
3473 };
3474
3475 /** 
3476  * @class Roo.SplitBar.BasicLayoutAdapter
3477  * Default Adapter. It assumes the splitter and resizing element are not positioned
3478  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3479  */
3480 Roo.SplitBar.BasicLayoutAdapter = function(){
3481 };
3482
3483 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3484     // do nothing for now
3485     init : function(s){
3486     
3487     },
3488     /**
3489      * Called before drag operations to get the current size of the resizing element. 
3490      * @param {Roo.SplitBar} s The SplitBar using this adapter
3491      */
3492      getElementSize : function(s){
3493         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3494             return s.resizingEl.getWidth();
3495         }else{
3496             return s.resizingEl.getHeight();
3497         }
3498     },
3499     
3500     /**
3501      * Called after drag operations to set the size of the resizing element.
3502      * @param {Roo.SplitBar} s The SplitBar using this adapter
3503      * @param {Number} newSize The new size to set
3504      * @param {Function} onComplete A function to be invoked when resizing is complete
3505      */
3506     setElementSize : function(s, newSize, onComplete){
3507         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3508             if(!s.animate){
3509                 s.resizingEl.setWidth(newSize);
3510                 if(onComplete){
3511                     onComplete(s, newSize);
3512                 }
3513             }else{
3514                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3515             }
3516         }else{
3517             
3518             if(!s.animate){
3519                 s.resizingEl.setHeight(newSize);
3520                 if(onComplete){
3521                     onComplete(s, newSize);
3522                 }
3523             }else{
3524                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3525             }
3526         }
3527     }
3528 };
3529
3530 /** 
3531  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3532  * @extends Roo.SplitBar.BasicLayoutAdapter
3533  * Adapter that  moves the splitter element to align with the resized sizing element. 
3534  * Used with an absolute positioned SplitBar.
3535  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3536  * document.body, make sure you assign an id to the body element.
3537  */
3538 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3539     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3540     this.container = Roo.get(container);
3541 };
3542
3543 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3544     init : function(s){
3545         this.basic.init(s);
3546     },
3547     
3548     getElementSize : function(s){
3549         return this.basic.getElementSize(s);
3550     },
3551     
3552     setElementSize : function(s, newSize, onComplete){
3553         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3554     },
3555     
3556     moveSplitter : function(s){
3557         var yes = Roo.SplitBar;
3558         switch(s.placement){
3559             case yes.LEFT:
3560                 s.el.setX(s.resizingEl.getRight());
3561                 break;
3562             case yes.RIGHT:
3563                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3564                 break;
3565             case yes.TOP:
3566                 s.el.setY(s.resizingEl.getBottom());
3567                 break;
3568             case yes.BOTTOM:
3569                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3570                 break;
3571         }
3572     }
3573 };
3574
3575 /**
3576  * Orientation constant - Create a vertical SplitBar
3577  * @static
3578  * @type Number
3579  */
3580 Roo.SplitBar.VERTICAL = 1;
3581
3582 /**
3583  * Orientation constant - Create a horizontal SplitBar
3584  * @static
3585  * @type Number
3586  */
3587 Roo.SplitBar.HORIZONTAL = 2;
3588
3589 /**
3590  * Placement constant - The resizing element is to the left of the splitter element
3591  * @static
3592  * @type Number
3593  */
3594 Roo.SplitBar.LEFT = 1;
3595
3596 /**
3597  * Placement constant - The resizing element is to the right of the splitter element
3598  * @static
3599  * @type Number
3600  */
3601 Roo.SplitBar.RIGHT = 2;
3602
3603 /**
3604  * Placement constant - The resizing element is positioned above the splitter element
3605  * @static
3606  * @type Number
3607  */
3608 Roo.SplitBar.TOP = 3;
3609
3610 /**
3611  * Placement constant - The resizing element is positioned under splitter element
3612  * @static
3613  * @type Number
3614  */
3615 Roo.SplitBar.BOTTOM = 4;
3616 /*
3617  * Based on:
3618  * Ext JS Library 1.1.1
3619  * Copyright(c) 2006-2007, Ext JS, LLC.
3620  *
3621  * Originally Released Under LGPL - original licence link has changed is not relivant.
3622  *
3623  * Fork - LGPL
3624  * <script type="text/javascript">
3625  */
3626
3627 /**
3628  * @class Roo.View
3629  * @extends Roo.util.Observable
3630  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3631  * This class also supports single and multi selection modes. <br>
3632  * Create a data model bound view:
3633  <pre><code>
3634  var store = new Roo.data.Store(...);
3635
3636  var view = new Roo.View({
3637     el : "my-element",
3638     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3639  
3640     singleSelect: true,
3641     selectedClass: "ydataview-selected",
3642     store: store
3643  });
3644
3645  // listen for node click?
3646  view.on("click", function(vw, index, node, e){
3647  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3648  });
3649
3650  // load XML data
3651  dataModel.load("foobar.xml");
3652  </code></pre>
3653  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3654  * <br><br>
3655  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3656  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3657  * 
3658  * Note: old style constructor is still suported (container, template, config)
3659  * 
3660  * @constructor
3661  * Create a new View
3662  * @param {Object} config The config object
3663  * 
3664  */
3665 Roo.View = function(config, depreciated_tpl, depreciated_config){
3666     
3667     this.parent = false;
3668     
3669     if (typeof(depreciated_tpl) == 'undefined') {
3670         // new way.. - universal constructor.
3671         Roo.apply(this, config);
3672         this.el  = Roo.get(this.el);
3673     } else {
3674         // old format..
3675         this.el  = Roo.get(config);
3676         this.tpl = depreciated_tpl;
3677         Roo.apply(this, depreciated_config);
3678     }
3679     this.wrapEl  = this.el.wrap().wrap();
3680     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3681     
3682     
3683     if(typeof(this.tpl) == "string"){
3684         this.tpl = new Roo.Template(this.tpl);
3685     } else {
3686         // support xtype ctors..
3687         this.tpl = new Roo.factory(this.tpl, Roo);
3688     }
3689     
3690     
3691     this.tpl.compile();
3692     
3693     /** @private */
3694     this.addEvents({
3695         /**
3696          * @event beforeclick
3697          * Fires before a click is processed. Returns false to cancel the default action.
3698          * @param {Roo.View} this
3699          * @param {Number} index The index of the target node
3700          * @param {HTMLElement} node The target node
3701          * @param {Roo.EventObject} e The raw event object
3702          */
3703             "beforeclick" : true,
3704         /**
3705          * @event click
3706          * Fires when a template node is clicked.
3707          * @param {Roo.View} this
3708          * @param {Number} index The index of the target node
3709          * @param {HTMLElement} node The target node
3710          * @param {Roo.EventObject} e The raw event object
3711          */
3712             "click" : true,
3713         /**
3714          * @event dblclick
3715          * Fires when a template node is double clicked.
3716          * @param {Roo.View} this
3717          * @param {Number} index The index of the target node
3718          * @param {HTMLElement} node The target node
3719          * @param {Roo.EventObject} e The raw event object
3720          */
3721             "dblclick" : true,
3722         /**
3723          * @event contextmenu
3724          * Fires when a template node is right clicked.
3725          * @param {Roo.View} this
3726          * @param {Number} index The index of the target node
3727          * @param {HTMLElement} node The target node
3728          * @param {Roo.EventObject} e The raw event object
3729          */
3730             "contextmenu" : true,
3731         /**
3732          * @event selectionchange
3733          * Fires when the selected nodes change.
3734          * @param {Roo.View} this
3735          * @param {Array} selections Array of the selected nodes
3736          */
3737             "selectionchange" : true,
3738     
3739         /**
3740          * @event beforeselect
3741          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3742          * @param {Roo.View} this
3743          * @param {HTMLElement} node The node to be selected
3744          * @param {Array} selections Array of currently selected nodes
3745          */
3746             "beforeselect" : true,
3747         /**
3748          * @event preparedata
3749          * Fires on every row to render, to allow you to change the data.
3750          * @param {Roo.View} this
3751          * @param {Object} data to be rendered (change this)
3752          */
3753           "preparedata" : true
3754           
3755           
3756         });
3757
3758
3759
3760     this.el.on({
3761         "click": this.onClick,
3762         "dblclick": this.onDblClick,
3763         "contextmenu": this.onContextMenu,
3764         scope:this
3765     });
3766
3767     this.selections = [];
3768     this.nodes = [];
3769     this.cmp = new Roo.CompositeElementLite([]);
3770     if(this.store){
3771         this.store = Roo.factory(this.store, Roo.data);
3772         this.setStore(this.store, true);
3773     }
3774     
3775     if ( this.footer && this.footer.xtype) {
3776            
3777          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3778         
3779         this.footer.dataSource = this.store;
3780         this.footer.container = fctr;
3781         this.footer = Roo.factory(this.footer, Roo);
3782         fctr.insertFirst(this.el);
3783         
3784         // this is a bit insane - as the paging toolbar seems to detach the el..
3785 //        dom.parentNode.parentNode.parentNode
3786          // they get detached?
3787     }
3788     
3789     
3790     Roo.View.superclass.constructor.call(this);
3791     
3792     
3793 };
3794
3795 Roo.extend(Roo.View, Roo.util.Observable, {
3796     
3797      /**
3798      * @cfg {Roo.data.Store} store Data store to load data from.
3799      */
3800     store : false,
3801     
3802     /**
3803      * @cfg {String|Roo.Element} el The container element.
3804      */
3805     el : '',
3806     
3807     /**
3808      * @cfg {String|Roo.Template} tpl The template used by this View 
3809      */
3810     tpl : false,
3811     /**
3812      * @cfg {String} dataName the named area of the template to use as the data area
3813      *                          Works with domtemplates roo-name="name"
3814      */
3815     dataName: false,
3816     /**
3817      * @cfg {String} selectedClass The css class to add to selected nodes
3818      */
3819     selectedClass : "x-view-selected",
3820      /**
3821      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3822      */
3823     emptyText : "",
3824     
3825     /**
3826      * @cfg {String} text to display on mask (default Loading)
3827      */
3828     mask : false,
3829     /**
3830      * @cfg {Boolean} multiSelect Allow multiple selection
3831      */
3832     multiSelect : false,
3833     /**
3834      * @cfg {Boolean} singleSelect Allow single selection
3835      */
3836     singleSelect:  false,
3837     
3838     /**
3839      * @cfg {Boolean} toggleSelect - selecting 
3840      */
3841     toggleSelect : false,
3842     
3843     /**
3844      * @cfg {Boolean} tickable - selecting 
3845      */
3846     tickable : false,
3847     
3848     /**
3849      * Returns the element this view is bound to.
3850      * @return {Roo.Element}
3851      */
3852     getEl : function(){
3853         return this.wrapEl;
3854     },
3855     
3856     
3857
3858     /**
3859      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3860      */
3861     refresh : function(){
3862         //Roo.log('refresh');
3863         var t = this.tpl;
3864         
3865         // if we are using something like 'domtemplate', then
3866         // the what gets used is:
3867         // t.applySubtemplate(NAME, data, wrapping data..)
3868         // the outer template then get' applied with
3869         //     the store 'extra data'
3870         // and the body get's added to the
3871         //      roo-name="data" node?
3872         //      <span class='roo-tpl-{name}'></span> ?????
3873         
3874         
3875         
3876         this.clearSelections();
3877         this.el.update("");
3878         var html = [];
3879         var records = this.store.getRange();
3880         if(records.length < 1) {
3881             
3882             // is this valid??  = should it render a template??
3883             
3884             this.el.update(this.emptyText);
3885             return;
3886         }
3887         var el = this.el;
3888         if (this.dataName) {
3889             this.el.update(t.apply(this.store.meta)); //????
3890             el = this.el.child('.roo-tpl-' + this.dataName);
3891         }
3892         
3893         for(var i = 0, len = records.length; i < len; i++){
3894             var data = this.prepareData(records[i].data, i, records[i]);
3895             this.fireEvent("preparedata", this, data, i, records[i]);
3896             
3897             var d = Roo.apply({}, data);
3898             
3899             if(this.tickable){
3900                 Roo.apply(d, {'roo-id' : Roo.id()});
3901                 
3902                 var _this = this;
3903             
3904                 Roo.each(this.parent.item, function(item){
3905                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3906                         return;
3907                     }
3908                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3909                 });
3910             }
3911             
3912             html[html.length] = Roo.util.Format.trim(
3913                 this.dataName ?
3914                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3915                     t.apply(d)
3916             );
3917         }
3918         
3919         
3920         
3921         el.update(html.join(""));
3922         this.nodes = el.dom.childNodes;
3923         this.updateIndexes(0);
3924     },
3925     
3926
3927     /**
3928      * Function to override to reformat the data that is sent to
3929      * the template for each node.
3930      * DEPRICATED - use the preparedata event handler.
3931      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3932      * a JSON object for an UpdateManager bound view).
3933      */
3934     prepareData : function(data, index, record)
3935     {
3936         this.fireEvent("preparedata", this, data, index, record);
3937         return data;
3938     },
3939
3940     onUpdate : function(ds, record){
3941         // Roo.log('on update');   
3942         this.clearSelections();
3943         var index = this.store.indexOf(record);
3944         var n = this.nodes[index];
3945         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3946         n.parentNode.removeChild(n);
3947         this.updateIndexes(index, index);
3948     },
3949
3950     
3951     
3952 // --------- FIXME     
3953     onAdd : function(ds, records, index)
3954     {
3955         //Roo.log(['on Add', ds, records, index] );        
3956         this.clearSelections();
3957         if(this.nodes.length == 0){
3958             this.refresh();
3959             return;
3960         }
3961         var n = this.nodes[index];
3962         for(var i = 0, len = records.length; i < len; i++){
3963             var d = this.prepareData(records[i].data, i, records[i]);
3964             if(n){
3965                 this.tpl.insertBefore(n, d);
3966             }else{
3967                 
3968                 this.tpl.append(this.el, d);
3969             }
3970         }
3971         this.updateIndexes(index);
3972     },
3973
3974     onRemove : function(ds, record, index){
3975        // Roo.log('onRemove');
3976         this.clearSelections();
3977         var el = this.dataName  ?
3978             this.el.child('.roo-tpl-' + this.dataName) :
3979             this.el; 
3980         
3981         el.dom.removeChild(this.nodes[index]);
3982         this.updateIndexes(index);
3983     },
3984
3985     /**
3986      * Refresh an individual node.
3987      * @param {Number} index
3988      */
3989     refreshNode : function(index){
3990         this.onUpdate(this.store, this.store.getAt(index));
3991     },
3992
3993     updateIndexes : function(startIndex, endIndex){
3994         var ns = this.nodes;
3995         startIndex = startIndex || 0;
3996         endIndex = endIndex || ns.length - 1;
3997         for(var i = startIndex; i <= endIndex; i++){
3998             ns[i].nodeIndex = i;
3999         }
4000     },
4001
4002     /**
4003      * Changes the data store this view uses and refresh the view.
4004      * @param {Store} store
4005      */
4006     setStore : function(store, initial){
4007         if(!initial && this.store){
4008             this.store.un("datachanged", this.refresh);
4009             this.store.un("add", this.onAdd);
4010             this.store.un("remove", this.onRemove);
4011             this.store.un("update", this.onUpdate);
4012             this.store.un("clear", this.refresh);
4013             this.store.un("beforeload", this.onBeforeLoad);
4014             this.store.un("load", this.onLoad);
4015             this.store.un("loadexception", this.onLoad);
4016         }
4017         if(store){
4018           
4019             store.on("datachanged", this.refresh, this);
4020             store.on("add", this.onAdd, this);
4021             store.on("remove", this.onRemove, this);
4022             store.on("update", this.onUpdate, this);
4023             store.on("clear", this.refresh, this);
4024             store.on("beforeload", this.onBeforeLoad, this);
4025             store.on("load", this.onLoad, this);
4026             store.on("loadexception", this.onLoad, this);
4027         }
4028         
4029         if(store){
4030             this.refresh();
4031         }
4032     },
4033     /**
4034      * onbeforeLoad - masks the loading area.
4035      *
4036      */
4037     onBeforeLoad : function(store,opts)
4038     {
4039          //Roo.log('onBeforeLoad');   
4040         if (!opts.add) {
4041             this.el.update("");
4042         }
4043         this.el.mask(this.mask ? this.mask : "Loading" ); 
4044     },
4045     onLoad : function ()
4046     {
4047         this.el.unmask();
4048     },
4049     
4050
4051     /**
4052      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4053      * @param {HTMLElement} node
4054      * @return {HTMLElement} The template node
4055      */
4056     findItemFromChild : function(node){
4057         var el = this.dataName  ?
4058             this.el.child('.roo-tpl-' + this.dataName,true) :
4059             this.el.dom; 
4060         
4061         if(!node || node.parentNode == el){
4062                     return node;
4063             }
4064             var p = node.parentNode;
4065             while(p && p != el){
4066             if(p.parentNode == el){
4067                 return p;
4068             }
4069             p = p.parentNode;
4070         }
4071             return null;
4072     },
4073
4074     /** @ignore */
4075     onClick : function(e){
4076         var item = this.findItemFromChild(e.getTarget());
4077         if(item){
4078             var index = this.indexOf(item);
4079             if(this.onItemClick(item, index, e) !== false){
4080                 this.fireEvent("click", this, index, item, e);
4081             }
4082         }else{
4083             this.clearSelections();
4084         }
4085     },
4086
4087     /** @ignore */
4088     onContextMenu : function(e){
4089         var item = this.findItemFromChild(e.getTarget());
4090         if(item){
4091             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4092         }
4093     },
4094
4095     /** @ignore */
4096     onDblClick : function(e){
4097         var item = this.findItemFromChild(e.getTarget());
4098         if(item){
4099             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4100         }
4101     },
4102
4103     onItemClick : function(item, index, e)
4104     {
4105         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4106             return false;
4107         }
4108         if (this.toggleSelect) {
4109             var m = this.isSelected(item) ? 'unselect' : 'select';
4110             //Roo.log(m);
4111             var _t = this;
4112             _t[m](item, true, false);
4113             return true;
4114         }
4115         if(this.multiSelect || this.singleSelect){
4116             if(this.multiSelect && e.shiftKey && this.lastSelection){
4117                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4118             }else{
4119                 this.select(item, this.multiSelect && e.ctrlKey);
4120                 this.lastSelection = item;
4121             }
4122             
4123             if(!this.tickable){
4124                 e.preventDefault();
4125             }
4126             
4127         }
4128         return true;
4129     },
4130
4131     /**
4132      * Get the number of selected nodes.
4133      * @return {Number}
4134      */
4135     getSelectionCount : function(){
4136         return this.selections.length;
4137     },
4138
4139     /**
4140      * Get the currently selected nodes.
4141      * @return {Array} An array of HTMLElements
4142      */
4143     getSelectedNodes : function(){
4144         return this.selections;
4145     },
4146
4147     /**
4148      * Get the indexes of the selected nodes.
4149      * @return {Array}
4150      */
4151     getSelectedIndexes : function(){
4152         var indexes = [], s = this.selections;
4153         for(var i = 0, len = s.length; i < len; i++){
4154             indexes.push(s[i].nodeIndex);
4155         }
4156         return indexes;
4157     },
4158
4159     /**
4160      * Clear all selections
4161      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4162      */
4163     clearSelections : function(suppressEvent){
4164         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4165             this.cmp.elements = this.selections;
4166             this.cmp.removeClass(this.selectedClass);
4167             this.selections = [];
4168             if(!suppressEvent){
4169                 this.fireEvent("selectionchange", this, this.selections);
4170             }
4171         }
4172     },
4173
4174     /**
4175      * Returns true if the passed node is selected
4176      * @param {HTMLElement/Number} node The node or node index
4177      * @return {Boolean}
4178      */
4179     isSelected : function(node){
4180         var s = this.selections;
4181         if(s.length < 1){
4182             return false;
4183         }
4184         node = this.getNode(node);
4185         return s.indexOf(node) !== -1;
4186     },
4187
4188     /**
4189      * Selects nodes.
4190      * @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
4191      * @param {Boolean} keepExisting (optional) true to keep existing selections
4192      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4193      */
4194     select : function(nodeInfo, keepExisting, suppressEvent){
4195         if(nodeInfo instanceof Array){
4196             if(!keepExisting){
4197                 this.clearSelections(true);
4198             }
4199             for(var i = 0, len = nodeInfo.length; i < len; i++){
4200                 this.select(nodeInfo[i], true, true);
4201             }
4202             return;
4203         } 
4204         var node = this.getNode(nodeInfo);
4205         if(!node || this.isSelected(node)){
4206             return; // already selected.
4207         }
4208         if(!keepExisting){
4209             this.clearSelections(true);
4210         }
4211         
4212         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4213             Roo.fly(node).addClass(this.selectedClass);
4214             this.selections.push(node);
4215             if(!suppressEvent){
4216                 this.fireEvent("selectionchange", this, this.selections);
4217             }
4218         }
4219         
4220         
4221     },
4222       /**
4223      * Unselects nodes.
4224      * @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
4225      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4227      */
4228     unselect : function(nodeInfo, keepExisting, suppressEvent)
4229     {
4230         if(nodeInfo instanceof Array){
4231             Roo.each(this.selections, function(s) {
4232                 this.unselect(s, nodeInfo);
4233             }, this);
4234             return;
4235         }
4236         var node = this.getNode(nodeInfo);
4237         if(!node || !this.isSelected(node)){
4238             //Roo.log("not selected");
4239             return; // not selected.
4240         }
4241         // fireevent???
4242         var ns = [];
4243         Roo.each(this.selections, function(s) {
4244             if (s == node ) {
4245                 Roo.fly(node).removeClass(this.selectedClass);
4246
4247                 return;
4248             }
4249             ns.push(s);
4250         },this);
4251         
4252         this.selections= ns;
4253         this.fireEvent("selectionchange", this, this.selections);
4254     },
4255
4256     /**
4257      * Gets a template node.
4258      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4259      * @return {HTMLElement} The node or null if it wasn't found
4260      */
4261     getNode : function(nodeInfo){
4262         if(typeof nodeInfo == "string"){
4263             return document.getElementById(nodeInfo);
4264         }else if(typeof nodeInfo == "number"){
4265             return this.nodes[nodeInfo];
4266         }
4267         return nodeInfo;
4268     },
4269
4270     /**
4271      * Gets a range template nodes.
4272      * @param {Number} startIndex
4273      * @param {Number} endIndex
4274      * @return {Array} An array of nodes
4275      */
4276     getNodes : function(start, end){
4277         var ns = this.nodes;
4278         start = start || 0;
4279         end = typeof end == "undefined" ? ns.length - 1 : end;
4280         var nodes = [];
4281         if(start <= end){
4282             for(var i = start; i <= end; i++){
4283                 nodes.push(ns[i]);
4284             }
4285         } else{
4286             for(var i = start; i >= end; i--){
4287                 nodes.push(ns[i]);
4288             }
4289         }
4290         return nodes;
4291     },
4292
4293     /**
4294      * Finds the index of the passed node
4295      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4296      * @return {Number} The index of the node or -1
4297      */
4298     indexOf : function(node){
4299         node = this.getNode(node);
4300         if(typeof node.nodeIndex == "number"){
4301             return node.nodeIndex;
4302         }
4303         var ns = this.nodes;
4304         for(var i = 0, len = ns.length; i < len; i++){
4305             if(ns[i] == node){
4306                 return i;
4307             }
4308         }
4309         return -1;
4310     }
4311 });
4312 /*
4313  * Based on:
4314  * Ext JS Library 1.1.1
4315  * Copyright(c) 2006-2007, Ext JS, LLC.
4316  *
4317  * Originally Released Under LGPL - original licence link has changed is not relivant.
4318  *
4319  * Fork - LGPL
4320  * <script type="text/javascript">
4321  */
4322
4323 /**
4324  * @class Roo.JsonView
4325  * @extends Roo.View
4326  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4327 <pre><code>
4328 var view = new Roo.JsonView({
4329     container: "my-element",
4330     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4331     multiSelect: true, 
4332     jsonRoot: "data" 
4333 });
4334
4335 // listen for node click?
4336 view.on("click", function(vw, index, node, e){
4337     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4338 });
4339
4340 // direct load of JSON data
4341 view.load("foobar.php");
4342
4343 // Example from my blog list
4344 var tpl = new Roo.Template(
4345     '&lt;div class="entry"&gt;' +
4346     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4347     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4348     "&lt;/div&gt;&lt;hr /&gt;"
4349 );
4350
4351 var moreView = new Roo.JsonView({
4352     container :  "entry-list", 
4353     template : tpl,
4354     jsonRoot: "posts"
4355 });
4356 moreView.on("beforerender", this.sortEntries, this);
4357 moreView.load({
4358     url: "/blog/get-posts.php",
4359     params: "allposts=true",
4360     text: "Loading Blog Entries..."
4361 });
4362 </code></pre>
4363
4364 * Note: old code is supported with arguments : (container, template, config)
4365
4366
4367  * @constructor
4368  * Create a new JsonView
4369  * 
4370  * @param {Object} config The config object
4371  * 
4372  */
4373 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4374     
4375     
4376     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4377
4378     var um = this.el.getUpdateManager();
4379     um.setRenderer(this);
4380     um.on("update", this.onLoad, this);
4381     um.on("failure", this.onLoadException, this);
4382
4383     /**
4384      * @event beforerender
4385      * Fires before rendering of the downloaded JSON data.
4386      * @param {Roo.JsonView} this
4387      * @param {Object} data The JSON data loaded
4388      */
4389     /**
4390      * @event load
4391      * Fires when data is loaded.
4392      * @param {Roo.JsonView} this
4393      * @param {Object} data The JSON data loaded
4394      * @param {Object} response The raw Connect response object
4395      */
4396     /**
4397      * @event loadexception
4398      * Fires when loading fails.
4399      * @param {Roo.JsonView} this
4400      * @param {Object} response The raw Connect response object
4401      */
4402     this.addEvents({
4403         'beforerender' : true,
4404         'load' : true,
4405         'loadexception' : true
4406     });
4407 };
4408 Roo.extend(Roo.JsonView, Roo.View, {
4409     /**
4410      * @type {String} The root property in the loaded JSON object that contains the data
4411      */
4412     jsonRoot : "",
4413
4414     /**
4415      * Refreshes the view.
4416      */
4417     refresh : function(){
4418         this.clearSelections();
4419         this.el.update("");
4420         var html = [];
4421         var o = this.jsonData;
4422         if(o && o.length > 0){
4423             for(var i = 0, len = o.length; i < len; i++){
4424                 var data = this.prepareData(o[i], i, o);
4425                 html[html.length] = this.tpl.apply(data);
4426             }
4427         }else{
4428             html.push(this.emptyText);
4429         }
4430         this.el.update(html.join(""));
4431         this.nodes = this.el.dom.childNodes;
4432         this.updateIndexes(0);
4433     },
4434
4435     /**
4436      * 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.
4437      * @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:
4438      <pre><code>
4439      view.load({
4440          url: "your-url.php",
4441          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4442          callback: yourFunction,
4443          scope: yourObject, //(optional scope)
4444          discardUrl: false,
4445          nocache: false,
4446          text: "Loading...",
4447          timeout: 30,
4448          scripts: false
4449      });
4450      </code></pre>
4451      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4452      * 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.
4453      * @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}
4454      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4455      * @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.
4456      */
4457     load : function(){
4458         var um = this.el.getUpdateManager();
4459         um.update.apply(um, arguments);
4460     },
4461
4462     // note - render is a standard framework call...
4463     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4464     render : function(el, response){
4465         
4466         this.clearSelections();
4467         this.el.update("");
4468         var o;
4469         try{
4470             if (response != '') {
4471                 o = Roo.util.JSON.decode(response.responseText);
4472                 if(this.jsonRoot){
4473                     
4474                     o = o[this.jsonRoot];
4475                 }
4476             }
4477         } catch(e){
4478         }
4479         /**
4480          * The current JSON data or null
4481          */
4482         this.jsonData = o;
4483         this.beforeRender();
4484         this.refresh();
4485     },
4486
4487 /**
4488  * Get the number of records in the current JSON dataset
4489  * @return {Number}
4490  */
4491     getCount : function(){
4492         return this.jsonData ? this.jsonData.length : 0;
4493     },
4494
4495 /**
4496  * Returns the JSON object for the specified node(s)
4497  * @param {HTMLElement/Array} node The node or an array of nodes
4498  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4499  * you get the JSON object for the node
4500  */
4501     getNodeData : function(node){
4502         if(node instanceof Array){
4503             var data = [];
4504             for(var i = 0, len = node.length; i < len; i++){
4505                 data.push(this.getNodeData(node[i]));
4506             }
4507             return data;
4508         }
4509         return this.jsonData[this.indexOf(node)] || null;
4510     },
4511
4512     beforeRender : function(){
4513         this.snapshot = this.jsonData;
4514         if(this.sortInfo){
4515             this.sort.apply(this, this.sortInfo);
4516         }
4517         this.fireEvent("beforerender", this, this.jsonData);
4518     },
4519
4520     onLoad : function(el, o){
4521         this.fireEvent("load", this, this.jsonData, o);
4522     },
4523
4524     onLoadException : function(el, o){
4525         this.fireEvent("loadexception", this, o);
4526     },
4527
4528 /**
4529  * Filter the data by a specific property.
4530  * @param {String} property A property on your JSON objects
4531  * @param {String/RegExp} value Either string that the property values
4532  * should start with, or a RegExp to test against the property
4533  */
4534     filter : function(property, value){
4535         if(this.jsonData){
4536             var data = [];
4537             var ss = this.snapshot;
4538             if(typeof value == "string"){
4539                 var vlen = value.length;
4540                 if(vlen == 0){
4541                     this.clearFilter();
4542                     return;
4543                 }
4544                 value = value.toLowerCase();
4545                 for(var i = 0, len = ss.length; i < len; i++){
4546                     var o = ss[i];
4547                     if(o[property].substr(0, vlen).toLowerCase() == value){
4548                         data.push(o);
4549                     }
4550                 }
4551             } else if(value.exec){ // regex?
4552                 for(var i = 0, len = ss.length; i < len; i++){
4553                     var o = ss[i];
4554                     if(value.test(o[property])){
4555                         data.push(o);
4556                     }
4557                 }
4558             } else{
4559                 return;
4560             }
4561             this.jsonData = data;
4562             this.refresh();
4563         }
4564     },
4565
4566 /**
4567  * Filter by a function. The passed function will be called with each
4568  * object in the current dataset. If the function returns true the value is kept,
4569  * otherwise it is filtered.
4570  * @param {Function} fn
4571  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4572  */
4573     filterBy : function(fn, scope){
4574         if(this.jsonData){
4575             var data = [];
4576             var ss = this.snapshot;
4577             for(var i = 0, len = ss.length; i < len; i++){
4578                 var o = ss[i];
4579                 if(fn.call(scope || this, o)){
4580                     data.push(o);
4581                 }
4582             }
4583             this.jsonData = data;
4584             this.refresh();
4585         }
4586     },
4587
4588 /**
4589  * Clears the current filter.
4590  */
4591     clearFilter : function(){
4592         if(this.snapshot && this.jsonData != this.snapshot){
4593             this.jsonData = this.snapshot;
4594             this.refresh();
4595         }
4596     },
4597
4598
4599 /**
4600  * Sorts the data for this view and refreshes it.
4601  * @param {String} property A property on your JSON objects to sort on
4602  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4603  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4604  */
4605     sort : function(property, dir, sortType){
4606         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4607         if(this.jsonData){
4608             var p = property;
4609             var dsc = dir && dir.toLowerCase() == "desc";
4610             var f = function(o1, o2){
4611                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4612                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4613                 ;
4614                 if(v1 < v2){
4615                     return dsc ? +1 : -1;
4616                 } else if(v1 > v2){
4617                     return dsc ? -1 : +1;
4618                 } else{
4619                     return 0;
4620                 }
4621             };
4622             this.jsonData.sort(f);
4623             this.refresh();
4624             if(this.jsonData != this.snapshot){
4625                 this.snapshot.sort(f);
4626             }
4627         }
4628     }
4629 });/*
4630  * Based on:
4631  * Ext JS Library 1.1.1
4632  * Copyright(c) 2006-2007, Ext JS, LLC.
4633  *
4634  * Originally Released Under LGPL - original licence link has changed is not relivant.
4635  *
4636  * Fork - LGPL
4637  * <script type="text/javascript">
4638  */
4639  
4640
4641 /**
4642  * @class Roo.ColorPalette
4643  * @extends Roo.Component
4644  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4645  * Here's an example of typical usage:
4646  * <pre><code>
4647 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4648 cp.render('my-div');
4649
4650 cp.on('select', function(palette, selColor){
4651     // do something with selColor
4652 });
4653 </code></pre>
4654  * @constructor
4655  * Create a new ColorPalette
4656  * @param {Object} config The config object
4657  */
4658 Roo.ColorPalette = function(config){
4659     Roo.ColorPalette.superclass.constructor.call(this, config);
4660     this.addEvents({
4661         /**
4662              * @event select
4663              * Fires when a color is selected
4664              * @param {ColorPalette} this
4665              * @param {String} color The 6-digit color hex code (without the # symbol)
4666              */
4667         select: true
4668     });
4669
4670     if(this.handler){
4671         this.on("select", this.handler, this.scope, true);
4672     }
4673 };
4674 Roo.extend(Roo.ColorPalette, Roo.Component, {
4675     /**
4676      * @cfg {String} itemCls
4677      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4678      */
4679     itemCls : "x-color-palette",
4680     /**
4681      * @cfg {String} value
4682      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4683      * the hex codes are case-sensitive.
4684      */
4685     value : null,
4686     clickEvent:'click',
4687     // private
4688     ctype: "Roo.ColorPalette",
4689
4690     /**
4691      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4692      */
4693     allowReselect : false,
4694
4695     /**
4696      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4697      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4698      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4699      * of colors with the width setting until the box is symmetrical.</p>
4700      * <p>You can override individual colors if needed:</p>
4701      * <pre><code>
4702 var cp = new Roo.ColorPalette();
4703 cp.colors[0] = "FF0000";  // change the first box to red
4704 </code></pre>
4705
4706 Or you can provide a custom array of your own for complete control:
4707 <pre><code>
4708 var cp = new Roo.ColorPalette();
4709 cp.colors = ["000000", "993300", "333300"];
4710 </code></pre>
4711      * @type Array
4712      */
4713     colors : [
4714         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4715         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4716         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4717         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4718         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4719     ],
4720
4721     // private
4722     onRender : function(container, position){
4723         var t = new Roo.MasterTemplate(
4724             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4725         );
4726         var c = this.colors;
4727         for(var i = 0, len = c.length; i < len; i++){
4728             t.add([c[i]]);
4729         }
4730         var el = document.createElement("div");
4731         el.className = this.itemCls;
4732         t.overwrite(el);
4733         container.dom.insertBefore(el, position);
4734         this.el = Roo.get(el);
4735         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4736         if(this.clickEvent != 'click'){
4737             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4738         }
4739     },
4740
4741     // private
4742     afterRender : function(){
4743         Roo.ColorPalette.superclass.afterRender.call(this);
4744         if(this.value){
4745             var s = this.value;
4746             this.value = null;
4747             this.select(s);
4748         }
4749     },
4750
4751     // private
4752     handleClick : function(e, t){
4753         e.preventDefault();
4754         if(!this.disabled){
4755             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4756             this.select(c.toUpperCase());
4757         }
4758     },
4759
4760     /**
4761      * Selects the specified color in the palette (fires the select event)
4762      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4763      */
4764     select : function(color){
4765         color = color.replace("#", "");
4766         if(color != this.value || this.allowReselect){
4767             var el = this.el;
4768             if(this.value){
4769                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4770             }
4771             el.child("a.color-"+color).addClass("x-color-palette-sel");
4772             this.value = color;
4773             this.fireEvent("select", this, color);
4774         }
4775     }
4776 });/*
4777  * Based on:
4778  * Ext JS Library 1.1.1
4779  * Copyright(c) 2006-2007, Ext JS, LLC.
4780  *
4781  * Originally Released Under LGPL - original licence link has changed is not relivant.
4782  *
4783  * Fork - LGPL
4784  * <script type="text/javascript">
4785  */
4786  
4787 /**
4788  * @class Roo.DatePicker
4789  * @extends Roo.Component
4790  * Simple date picker class.
4791  * @constructor
4792  * Create a new DatePicker
4793  * @param {Object} config The config object
4794  */
4795 Roo.DatePicker = function(config){
4796     Roo.DatePicker.superclass.constructor.call(this, config);
4797
4798     this.value = config && config.value ?
4799                  config.value.clearTime() : new Date().clearTime();
4800
4801     this.addEvents({
4802         /**
4803              * @event select
4804              * Fires when a date is selected
4805              * @param {DatePicker} this
4806              * @param {Date} date The selected date
4807              */
4808         'select': true,
4809         /**
4810              * @event monthchange
4811              * Fires when the displayed month changes 
4812              * @param {DatePicker} this
4813              * @param {Date} date The selected month
4814              */
4815         'monthchange': true
4816     });
4817
4818     if(this.handler){
4819         this.on("select", this.handler,  this.scope || this);
4820     }
4821     // build the disabledDatesRE
4822     if(!this.disabledDatesRE && this.disabledDates){
4823         var dd = this.disabledDates;
4824         var re = "(?:";
4825         for(var i = 0; i < dd.length; i++){
4826             re += dd[i];
4827             if(i != dd.length-1) {
4828                 re += "|";
4829             }
4830         }
4831         this.disabledDatesRE = new RegExp(re + ")");
4832     }
4833 };
4834
4835 Roo.extend(Roo.DatePicker, Roo.Component, {
4836     /**
4837      * @cfg {String} todayText
4838      * The text to display on the button that selects the current date (defaults to "Today")
4839      */
4840     todayText : "Today",
4841     /**
4842      * @cfg {String} okText
4843      * The text to display on the ok button
4844      */
4845     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4846     /**
4847      * @cfg {String} cancelText
4848      * The text to display on the cancel button
4849      */
4850     cancelText : "Cancel",
4851     /**
4852      * @cfg {String} todayTip
4853      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4854      */
4855     todayTip : "{0} (Spacebar)",
4856     /**
4857      * @cfg {Date} minDate
4858      * Minimum allowable date (JavaScript date object, defaults to null)
4859      */
4860     minDate : null,
4861     /**
4862      * @cfg {Date} maxDate
4863      * Maximum allowable date (JavaScript date object, defaults to null)
4864      */
4865     maxDate : null,
4866     /**
4867      * @cfg {String} minText
4868      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4869      */
4870     minText : "This date is before the minimum date",
4871     /**
4872      * @cfg {String} maxText
4873      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4874      */
4875     maxText : "This date is after the maximum date",
4876     /**
4877      * @cfg {String} format
4878      * The default date format string which can be overriden for localization support.  The format must be
4879      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4880      */
4881     format : "m/d/y",
4882     /**
4883      * @cfg {Array} disabledDays
4884      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4885      */
4886     disabledDays : null,
4887     /**
4888      * @cfg {String} disabledDaysText
4889      * The tooltip to display when the date falls on a disabled day (defaults to "")
4890      */
4891     disabledDaysText : "",
4892     /**
4893      * @cfg {RegExp} disabledDatesRE
4894      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4895      */
4896     disabledDatesRE : null,
4897     /**
4898      * @cfg {String} disabledDatesText
4899      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4900      */
4901     disabledDatesText : "",
4902     /**
4903      * @cfg {Boolean} constrainToViewport
4904      * True to constrain the date picker to the viewport (defaults to true)
4905      */
4906     constrainToViewport : true,
4907     /**
4908      * @cfg {Array} monthNames
4909      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4910      */
4911     monthNames : Date.monthNames,
4912     /**
4913      * @cfg {Array} dayNames
4914      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4915      */
4916     dayNames : Date.dayNames,
4917     /**
4918      * @cfg {String} nextText
4919      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4920      */
4921     nextText: 'Next Month (Control+Right)',
4922     /**
4923      * @cfg {String} prevText
4924      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4925      */
4926     prevText: 'Previous Month (Control+Left)',
4927     /**
4928      * @cfg {String} monthYearText
4929      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4930      */
4931     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4932     /**
4933      * @cfg {Number} startDay
4934      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4935      */
4936     startDay : 0,
4937     /**
4938      * @cfg {Bool} showClear
4939      * Show a clear button (usefull for date form elements that can be blank.)
4940      */
4941     
4942     showClear: false,
4943     
4944     /**
4945      * Sets the value of the date field
4946      * @param {Date} value The date to set
4947      */
4948     setValue : function(value){
4949         var old = this.value;
4950         
4951         if (typeof(value) == 'string') {
4952          
4953             value = Date.parseDate(value, this.format);
4954         }
4955         if (!value) {
4956             value = new Date();
4957         }
4958         
4959         this.value = value.clearTime(true);
4960         if(this.el){
4961             this.update(this.value);
4962         }
4963     },
4964
4965     /**
4966      * Gets the current selected value of the date field
4967      * @return {Date} The selected date
4968      */
4969     getValue : function(){
4970         return this.value;
4971     },
4972
4973     // private
4974     focus : function(){
4975         if(this.el){
4976             this.update(this.activeDate);
4977         }
4978     },
4979
4980     // privateval
4981     onRender : function(container, position){
4982         
4983         var m = [
4984              '<table cellspacing="0">',
4985                 '<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>',
4986                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4987         var dn = this.dayNames;
4988         for(var i = 0; i < 7; i++){
4989             var d = this.startDay+i;
4990             if(d > 6){
4991                 d = d-7;
4992             }
4993             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4994         }
4995         m[m.length] = "</tr></thead><tbody><tr>";
4996         for(var i = 0; i < 42; i++) {
4997             if(i % 7 == 0 && i != 0){
4998                 m[m.length] = "</tr><tr>";
4999             }
5000             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5001         }
5002         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5003             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5004
5005         var el = document.createElement("div");
5006         el.className = "x-date-picker";
5007         el.innerHTML = m.join("");
5008
5009         container.dom.insertBefore(el, position);
5010
5011         this.el = Roo.get(el);
5012         this.eventEl = Roo.get(el.firstChild);
5013
5014         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5015             handler: this.showPrevMonth,
5016             scope: this,
5017             preventDefault:true,
5018             stopDefault:true
5019         });
5020
5021         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5022             handler: this.showNextMonth,
5023             scope: this,
5024             preventDefault:true,
5025             stopDefault:true
5026         });
5027
5028         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5029
5030         this.monthPicker = this.el.down('div.x-date-mp');
5031         this.monthPicker.enableDisplayMode('block');
5032         
5033         var kn = new Roo.KeyNav(this.eventEl, {
5034             "left" : function(e){
5035                 e.ctrlKey ?
5036                     this.showPrevMonth() :
5037                     this.update(this.activeDate.add("d", -1));
5038             },
5039
5040             "right" : function(e){
5041                 e.ctrlKey ?
5042                     this.showNextMonth() :
5043                     this.update(this.activeDate.add("d", 1));
5044             },
5045
5046             "up" : function(e){
5047                 e.ctrlKey ?
5048                     this.showNextYear() :
5049                     this.update(this.activeDate.add("d", -7));
5050             },
5051
5052             "down" : function(e){
5053                 e.ctrlKey ?
5054                     this.showPrevYear() :
5055                     this.update(this.activeDate.add("d", 7));
5056             },
5057
5058             "pageUp" : function(e){
5059                 this.showNextMonth();
5060             },
5061
5062             "pageDown" : function(e){
5063                 this.showPrevMonth();
5064             },
5065
5066             "enter" : function(e){
5067                 e.stopPropagation();
5068                 return true;
5069             },
5070
5071             scope : this
5072         });
5073
5074         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5075
5076         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5077
5078         this.el.unselectable();
5079         
5080         this.cells = this.el.select("table.x-date-inner tbody td");
5081         this.textNodes = this.el.query("table.x-date-inner tbody span");
5082
5083         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5084             text: "&#160;",
5085             tooltip: this.monthYearText
5086         });
5087
5088         this.mbtn.on('click', this.showMonthPicker, this);
5089         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5090
5091
5092         var today = (new Date()).dateFormat(this.format);
5093         
5094         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5095         if (this.showClear) {
5096             baseTb.add( new Roo.Toolbar.Fill());
5097         }
5098         baseTb.add({
5099             text: String.format(this.todayText, today),
5100             tooltip: String.format(this.todayTip, today),
5101             handler: this.selectToday,
5102             scope: this
5103         });
5104         
5105         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5106             
5107         //});
5108         if (this.showClear) {
5109             
5110             baseTb.add( new Roo.Toolbar.Fill());
5111             baseTb.add({
5112                 text: '&#160;',
5113                 cls: 'x-btn-icon x-btn-clear',
5114                 handler: function() {
5115                     //this.value = '';
5116                     this.fireEvent("select", this, '');
5117                 },
5118                 scope: this
5119             });
5120         }
5121         
5122         
5123         if(Roo.isIE){
5124             this.el.repaint();
5125         }
5126         this.update(this.value);
5127     },
5128
5129     createMonthPicker : function(){
5130         if(!this.monthPicker.dom.firstChild){
5131             var buf = ['<table border="0" cellspacing="0">'];
5132             for(var i = 0; i < 6; i++){
5133                 buf.push(
5134                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5135                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5136                     i == 0 ?
5137                     '<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>' :
5138                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5139                 );
5140             }
5141             buf.push(
5142                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5143                     this.okText,
5144                     '</button><button type="button" class="x-date-mp-cancel">',
5145                     this.cancelText,
5146                     '</button></td></tr>',
5147                 '</table>'
5148             );
5149             this.monthPicker.update(buf.join(''));
5150             this.monthPicker.on('click', this.onMonthClick, this);
5151             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5152
5153             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5154             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5155
5156             this.mpMonths.each(function(m, a, i){
5157                 i += 1;
5158                 if((i%2) == 0){
5159                     m.dom.xmonth = 5 + Math.round(i * .5);
5160                 }else{
5161                     m.dom.xmonth = Math.round((i-1) * .5);
5162                 }
5163             });
5164         }
5165     },
5166
5167     showMonthPicker : function(){
5168         this.createMonthPicker();
5169         var size = this.el.getSize();
5170         this.monthPicker.setSize(size);
5171         this.monthPicker.child('table').setSize(size);
5172
5173         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5174         this.updateMPMonth(this.mpSelMonth);
5175         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5176         this.updateMPYear(this.mpSelYear);
5177
5178         this.monthPicker.slideIn('t', {duration:.2});
5179     },
5180
5181     updateMPYear : function(y){
5182         this.mpyear = y;
5183         var ys = this.mpYears.elements;
5184         for(var i = 1; i <= 10; i++){
5185             var td = ys[i-1], y2;
5186             if((i%2) == 0){
5187                 y2 = y + Math.round(i * .5);
5188                 td.firstChild.innerHTML = y2;
5189                 td.xyear = y2;
5190             }else{
5191                 y2 = y - (5-Math.round(i * .5));
5192                 td.firstChild.innerHTML = y2;
5193                 td.xyear = y2;
5194             }
5195             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5196         }
5197     },
5198
5199     updateMPMonth : function(sm){
5200         this.mpMonths.each(function(m, a, i){
5201             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5202         });
5203     },
5204
5205     selectMPMonth: function(m){
5206         
5207     },
5208
5209     onMonthClick : function(e, t){
5210         e.stopEvent();
5211         var el = new Roo.Element(t), pn;
5212         if(el.is('button.x-date-mp-cancel')){
5213             this.hideMonthPicker();
5214         }
5215         else if(el.is('button.x-date-mp-ok')){
5216             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5217             this.hideMonthPicker();
5218         }
5219         else if(pn = el.up('td.x-date-mp-month', 2)){
5220             this.mpMonths.removeClass('x-date-mp-sel');
5221             pn.addClass('x-date-mp-sel');
5222             this.mpSelMonth = pn.dom.xmonth;
5223         }
5224         else if(pn = el.up('td.x-date-mp-year', 2)){
5225             this.mpYears.removeClass('x-date-mp-sel');
5226             pn.addClass('x-date-mp-sel');
5227             this.mpSelYear = pn.dom.xyear;
5228         }
5229         else if(el.is('a.x-date-mp-prev')){
5230             this.updateMPYear(this.mpyear-10);
5231         }
5232         else if(el.is('a.x-date-mp-next')){
5233             this.updateMPYear(this.mpyear+10);
5234         }
5235     },
5236
5237     onMonthDblClick : function(e, t){
5238         e.stopEvent();
5239         var el = new Roo.Element(t), pn;
5240         if(pn = el.up('td.x-date-mp-month', 2)){
5241             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-year', 2)){
5245             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248     },
5249
5250     hideMonthPicker : function(disableAnim){
5251         if(this.monthPicker){
5252             if(disableAnim === true){
5253                 this.monthPicker.hide();
5254             }else{
5255                 this.monthPicker.slideOut('t', {duration:.2});
5256             }
5257         }
5258     },
5259
5260     // private
5261     showPrevMonth : function(e){
5262         this.update(this.activeDate.add("mo", -1));
5263     },
5264
5265     // private
5266     showNextMonth : function(e){
5267         this.update(this.activeDate.add("mo", 1));
5268     },
5269
5270     // private
5271     showPrevYear : function(){
5272         this.update(this.activeDate.add("y", -1));
5273     },
5274
5275     // private
5276     showNextYear : function(){
5277         this.update(this.activeDate.add("y", 1));
5278     },
5279
5280     // private
5281     handleMouseWheel : function(e){
5282         var delta = e.getWheelDelta();
5283         if(delta > 0){
5284             this.showPrevMonth();
5285             e.stopEvent();
5286         } else if(delta < 0){
5287             this.showNextMonth();
5288             e.stopEvent();
5289         }
5290     },
5291
5292     // private
5293     handleDateClick : function(e, t){
5294         e.stopEvent();
5295         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5296             this.setValue(new Date(t.dateValue));
5297             this.fireEvent("select", this, this.value);
5298         }
5299     },
5300
5301     // private
5302     selectToday : function(){
5303         this.setValue(new Date().clearTime());
5304         this.fireEvent("select", this, this.value);
5305     },
5306
5307     // private
5308     update : function(date)
5309     {
5310         var vd = this.activeDate;
5311         this.activeDate = date;
5312         if(vd && this.el){
5313             var t = date.getTime();
5314             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5315                 this.cells.removeClass("x-date-selected");
5316                 this.cells.each(function(c){
5317                    if(c.dom.firstChild.dateValue == t){
5318                        c.addClass("x-date-selected");
5319                        setTimeout(function(){
5320                             try{c.dom.firstChild.focus();}catch(e){}
5321                        }, 50);
5322                        return false;
5323                    }
5324                 });
5325                 return;
5326             }
5327         }
5328         
5329         var days = date.getDaysInMonth();
5330         var firstOfMonth = date.getFirstDateOfMonth();
5331         var startingPos = firstOfMonth.getDay()-this.startDay;
5332
5333         if(startingPos <= this.startDay){
5334             startingPos += 7;
5335         }
5336
5337         var pm = date.add("mo", -1);
5338         var prevStart = pm.getDaysInMonth()-startingPos;
5339
5340         var cells = this.cells.elements;
5341         var textEls = this.textNodes;
5342         days += startingPos;
5343
5344         // convert everything to numbers so it's fast
5345         var day = 86400000;
5346         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5347         var today = new Date().clearTime().getTime();
5348         var sel = date.clearTime().getTime();
5349         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5350         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5351         var ddMatch = this.disabledDatesRE;
5352         var ddText = this.disabledDatesText;
5353         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5354         var ddaysText = this.disabledDaysText;
5355         var format = this.format;
5356
5357         var setCellClass = function(cal, cell){
5358             cell.title = "";
5359             var t = d.getTime();
5360             cell.firstChild.dateValue = t;
5361             if(t == today){
5362                 cell.className += " x-date-today";
5363                 cell.title = cal.todayText;
5364             }
5365             if(t == sel){
5366                 cell.className += " x-date-selected";
5367                 setTimeout(function(){
5368                     try{cell.firstChild.focus();}catch(e){}
5369                 }, 50);
5370             }
5371             // disabling
5372             if(t < min) {
5373                 cell.className = " x-date-disabled";
5374                 cell.title = cal.minText;
5375                 return;
5376             }
5377             if(t > max) {
5378                 cell.className = " x-date-disabled";
5379                 cell.title = cal.maxText;
5380                 return;
5381             }
5382             if(ddays){
5383                 if(ddays.indexOf(d.getDay()) != -1){
5384                     cell.title = ddaysText;
5385                     cell.className = " x-date-disabled";
5386                 }
5387             }
5388             if(ddMatch && format){
5389                 var fvalue = d.dateFormat(format);
5390                 if(ddMatch.test(fvalue)){
5391                     cell.title = ddText.replace("%0", fvalue);
5392                     cell.className = " x-date-disabled";
5393                 }
5394             }
5395         };
5396
5397         var i = 0;
5398         for(; i < startingPos; i++) {
5399             textEls[i].innerHTML = (++prevStart);
5400             d.setDate(d.getDate()+1);
5401             cells[i].className = "x-date-prevday";
5402             setCellClass(this, cells[i]);
5403         }
5404         for(; i < days; i++){
5405             intDay = i - startingPos + 1;
5406             textEls[i].innerHTML = (intDay);
5407             d.setDate(d.getDate()+1);
5408             cells[i].className = "x-date-active";
5409             setCellClass(this, cells[i]);
5410         }
5411         var extraDays = 0;
5412         for(; i < 42; i++) {
5413              textEls[i].innerHTML = (++extraDays);
5414              d.setDate(d.getDate()+1);
5415              cells[i].className = "x-date-nextday";
5416              setCellClass(this, cells[i]);
5417         }
5418
5419         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5420         this.fireEvent('monthchange', this, date);
5421         
5422         if(!this.internalRender){
5423             var main = this.el.dom.firstChild;
5424             var w = main.offsetWidth;
5425             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5426             Roo.fly(main).setWidth(w);
5427             this.internalRender = true;
5428             // opera does not respect the auto grow header center column
5429             // then, after it gets a width opera refuses to recalculate
5430             // without a second pass
5431             if(Roo.isOpera && !this.secondPass){
5432                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5433                 this.secondPass = true;
5434                 this.update.defer(10, this, [date]);
5435             }
5436         }
5437         
5438         
5439     }
5440 });        /*
5441  * Based on:
5442  * Ext JS Library 1.1.1
5443  * Copyright(c) 2006-2007, Ext JS, LLC.
5444  *
5445  * Originally Released Under LGPL - original licence link has changed is not relivant.
5446  *
5447  * Fork - LGPL
5448  * <script type="text/javascript">
5449  */
5450 /**
5451  * @class Roo.TabPanel
5452  * @extends Roo.util.Observable
5453  * A lightweight tab container.
5454  * <br><br>
5455  * Usage:
5456  * <pre><code>
5457 // basic tabs 1, built from existing content
5458 var tabs = new Roo.TabPanel("tabs1");
5459 tabs.addTab("script", "View Script");
5460 tabs.addTab("markup", "View Markup");
5461 tabs.activate("script");
5462
5463 // more advanced tabs, built from javascript
5464 var jtabs = new Roo.TabPanel("jtabs");
5465 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5466
5467 // set up the UpdateManager
5468 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5469 var updater = tab2.getUpdateManager();
5470 updater.setDefaultUrl("ajax1.htm");
5471 tab2.on('activate', updater.refresh, updater, true);
5472
5473 // Use setUrl for Ajax loading
5474 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5475 tab3.setUrl("ajax2.htm", null, true);
5476
5477 // Disabled tab
5478 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5479 tab4.disable();
5480
5481 jtabs.activate("jtabs-1");
5482  * </code></pre>
5483  * @constructor
5484  * Create a new TabPanel.
5485  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5486  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5487  */
5488 Roo.TabPanel = function(container, config){
5489     /**
5490     * The container element for this TabPanel.
5491     * @type Roo.Element
5492     */
5493     this.el = Roo.get(container, true);
5494     if(config){
5495         if(typeof config == "boolean"){
5496             this.tabPosition = config ? "bottom" : "top";
5497         }else{
5498             Roo.apply(this, config);
5499         }
5500     }
5501     if(this.tabPosition == "bottom"){
5502         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5503         this.el.addClass("x-tabs-bottom");
5504     }
5505     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5506     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5507     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5508     if(Roo.isIE){
5509         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5510     }
5511     if(this.tabPosition != "bottom"){
5512         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5513          * @type Roo.Element
5514          */
5515         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5516         this.el.addClass("x-tabs-top");
5517     }
5518     this.items = [];
5519
5520     this.bodyEl.setStyle("position", "relative");
5521
5522     this.active = null;
5523     this.activateDelegate = this.activate.createDelegate(this);
5524
5525     this.addEvents({
5526         /**
5527          * @event tabchange
5528          * Fires when the active tab changes
5529          * @param {Roo.TabPanel} this
5530          * @param {Roo.TabPanelItem} activePanel The new active tab
5531          */
5532         "tabchange": true,
5533         /**
5534          * @event beforetabchange
5535          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5536          * @param {Roo.TabPanel} this
5537          * @param {Object} e Set cancel to true on this object to cancel the tab change
5538          * @param {Roo.TabPanelItem} tab The tab being changed to
5539          */
5540         "beforetabchange" : true
5541     });
5542
5543     Roo.EventManager.onWindowResize(this.onResize, this);
5544     this.cpad = this.el.getPadding("lr");
5545     this.hiddenCount = 0;
5546
5547
5548     // toolbar on the tabbar support...
5549     if (this.toolbar) {
5550         var tcfg = this.toolbar;
5551         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5552         this.toolbar = new Roo.Toolbar(tcfg);
5553         if (Roo.isSafari) {
5554             var tbl = tcfg.container.child('table', true);
5555             tbl.setAttribute('width', '100%');
5556         }
5557         
5558     }
5559    
5560
5561
5562     Roo.TabPanel.superclass.constructor.call(this);
5563 };
5564
5565 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5566     /*
5567      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5568      */
5569     tabPosition : "top",
5570     /*
5571      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5572      */
5573     currentTabWidth : 0,
5574     /*
5575      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5576      */
5577     minTabWidth : 40,
5578     /*
5579      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5580      */
5581     maxTabWidth : 250,
5582     /*
5583      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5584      */
5585     preferredTabWidth : 175,
5586     /*
5587      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5588      */
5589     resizeTabs : false,
5590     /*
5591      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5592      */
5593     monitorResize : true,
5594     /*
5595      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5596      */
5597     toolbar : false,
5598
5599     /**
5600      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5601      * @param {String} id The id of the div to use <b>or create</b>
5602      * @param {String} text The text for the tab
5603      * @param {String} content (optional) Content to put in the TabPanelItem body
5604      * @param {Boolean} closable (optional) True to create a close icon on the tab
5605      * @return {Roo.TabPanelItem} The created TabPanelItem
5606      */
5607     addTab : function(id, text, content, closable){
5608         var item = new Roo.TabPanelItem(this, id, text, closable);
5609         this.addTabItem(item);
5610         if(content){
5611             item.setContent(content);
5612         }
5613         return item;
5614     },
5615
5616     /**
5617      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5618      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5619      * @return {Roo.TabPanelItem}
5620      */
5621     getTab : function(id){
5622         return this.items[id];
5623     },
5624
5625     /**
5626      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5627      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5628      */
5629     hideTab : function(id){
5630         var t = this.items[id];
5631         if(!t.isHidden()){
5632            t.setHidden(true);
5633            this.hiddenCount++;
5634            this.autoSizeTabs();
5635         }
5636     },
5637
5638     /**
5639      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5640      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5641      */
5642     unhideTab : function(id){
5643         var t = this.items[id];
5644         if(t.isHidden()){
5645            t.setHidden(false);
5646            this.hiddenCount--;
5647            this.autoSizeTabs();
5648         }
5649     },
5650
5651     /**
5652      * Adds an existing {@link Roo.TabPanelItem}.
5653      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5654      */
5655     addTabItem : function(item){
5656         this.items[item.id] = item;
5657         this.items.push(item);
5658         if(this.resizeTabs){
5659            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5660            this.autoSizeTabs();
5661         }else{
5662             item.autoSize();
5663         }
5664     },
5665
5666     /**
5667      * Removes a {@link Roo.TabPanelItem}.
5668      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5669      */
5670     removeTab : function(id){
5671         var items = this.items;
5672         var tab = items[id];
5673         if(!tab) { return; }
5674         var index = items.indexOf(tab);
5675         if(this.active == tab && items.length > 1){
5676             var newTab = this.getNextAvailable(index);
5677             if(newTab) {
5678                 newTab.activate();
5679             }
5680         }
5681         this.stripEl.dom.removeChild(tab.pnode.dom);
5682         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5683             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5684         }
5685         items.splice(index, 1);
5686         delete this.items[tab.id];
5687         tab.fireEvent("close", tab);
5688         tab.purgeListeners();
5689         this.autoSizeTabs();
5690     },
5691
5692     getNextAvailable : function(start){
5693         var items = this.items;
5694         var index = start;
5695         // look for a next tab that will slide over to
5696         // replace the one being removed
5697         while(index < items.length){
5698             var item = items[++index];
5699             if(item && !item.isHidden()){
5700                 return item;
5701             }
5702         }
5703         // if one isn't found select the previous tab (on the left)
5704         index = start;
5705         while(index >= 0){
5706             var item = items[--index];
5707             if(item && !item.isHidden()){
5708                 return item;
5709             }
5710         }
5711         return null;
5712     },
5713
5714     /**
5715      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5716      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5717      */
5718     disableTab : function(id){
5719         var tab = this.items[id];
5720         if(tab && this.active != tab){
5721             tab.disable();
5722         }
5723     },
5724
5725     /**
5726      * Enables a {@link Roo.TabPanelItem} that is disabled.
5727      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5728      */
5729     enableTab : function(id){
5730         var tab = this.items[id];
5731         tab.enable();
5732     },
5733
5734     /**
5735      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5736      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5737      * @return {Roo.TabPanelItem} The TabPanelItem.
5738      */
5739     activate : function(id){
5740         var tab = this.items[id];
5741         if(!tab){
5742             return null;
5743         }
5744         if(tab == this.active || tab.disabled){
5745             return tab;
5746         }
5747         var e = {};
5748         this.fireEvent("beforetabchange", this, e, tab);
5749         if(e.cancel !== true && !tab.disabled){
5750             if(this.active){
5751                 this.active.hide();
5752             }
5753             this.active = this.items[id];
5754             this.active.show();
5755             this.fireEvent("tabchange", this, this.active);
5756         }
5757         return tab;
5758     },
5759
5760     /**
5761      * Gets the active {@link Roo.TabPanelItem}.
5762      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5763      */
5764     getActiveTab : function(){
5765         return this.active;
5766     },
5767
5768     /**
5769      * Updates the tab body element to fit the height of the container element
5770      * for overflow scrolling
5771      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5772      */
5773     syncHeight : function(targetHeight){
5774         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5775         var bm = this.bodyEl.getMargins();
5776         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5777         this.bodyEl.setHeight(newHeight);
5778         return newHeight;
5779     },
5780
5781     onResize : function(){
5782         if(this.monitorResize){
5783             this.autoSizeTabs();
5784         }
5785     },
5786
5787     /**
5788      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5789      */
5790     beginUpdate : function(){
5791         this.updating = true;
5792     },
5793
5794     /**
5795      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5796      */
5797     endUpdate : function(){
5798         this.updating = false;
5799         this.autoSizeTabs();
5800     },
5801
5802     /**
5803      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5804      */
5805     autoSizeTabs : function(){
5806         var count = this.items.length;
5807         var vcount = count - this.hiddenCount;
5808         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5809             return;
5810         }
5811         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5812         var availWidth = Math.floor(w / vcount);
5813         var b = this.stripBody;
5814         if(b.getWidth() > w){
5815             var tabs = this.items;
5816             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5817             if(availWidth < this.minTabWidth){
5818                 /*if(!this.sleft){    // incomplete scrolling code
5819                     this.createScrollButtons();
5820                 }
5821                 this.showScroll();
5822                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5823             }
5824         }else{
5825             if(this.currentTabWidth < this.preferredTabWidth){
5826                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5827             }
5828         }
5829     },
5830
5831     /**
5832      * Returns the number of tabs in this TabPanel.
5833      * @return {Number}
5834      */
5835      getCount : function(){
5836          return this.items.length;
5837      },
5838
5839     /**
5840      * Resizes all the tabs to the passed width
5841      * @param {Number} The new width
5842      */
5843     setTabWidth : function(width){
5844         this.currentTabWidth = width;
5845         for(var i = 0, len = this.items.length; i < len; i++) {
5846                 if(!this.items[i].isHidden()) {
5847                 this.items[i].setWidth(width);
5848             }
5849         }
5850     },
5851
5852     /**
5853      * Destroys this TabPanel
5854      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5855      */
5856     destroy : function(removeEl){
5857         Roo.EventManager.removeResizeListener(this.onResize, this);
5858         for(var i = 0, len = this.items.length; i < len; i++){
5859             this.items[i].purgeListeners();
5860         }
5861         if(removeEl === true){
5862             this.el.update("");
5863             this.el.remove();
5864         }
5865     }
5866 });
5867
5868 /**
5869  * @class Roo.TabPanelItem
5870  * @extends Roo.util.Observable
5871  * Represents an individual item (tab plus body) in a TabPanel.
5872  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5873  * @param {String} id The id of this TabPanelItem
5874  * @param {String} text The text for the tab of this TabPanelItem
5875  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5876  */
5877 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5878     /**
5879      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5880      * @type Roo.TabPanel
5881      */
5882     this.tabPanel = tabPanel;
5883     /**
5884      * The id for this TabPanelItem
5885      * @type String
5886      */
5887     this.id = id;
5888     /** @private */
5889     this.disabled = false;
5890     /** @private */
5891     this.text = text;
5892     /** @private */
5893     this.loaded = false;
5894     this.closable = closable;
5895
5896     /**
5897      * The body element for this TabPanelItem.
5898      * @type Roo.Element
5899      */
5900     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5901     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5902     this.bodyEl.setStyle("display", "block");
5903     this.bodyEl.setStyle("zoom", "1");
5904     this.hideAction();
5905
5906     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5907     /** @private */
5908     this.el = Roo.get(els.el, true);
5909     this.inner = Roo.get(els.inner, true);
5910     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5911     this.pnode = Roo.get(els.el.parentNode, true);
5912     this.el.on("mousedown", this.onTabMouseDown, this);
5913     this.el.on("click", this.onTabClick, this);
5914     /** @private */
5915     if(closable){
5916         var c = Roo.get(els.close, true);
5917         c.dom.title = this.closeText;
5918         c.addClassOnOver("close-over");
5919         c.on("click", this.closeClick, this);
5920      }
5921
5922     this.addEvents({
5923          /**
5924          * @event activate
5925          * Fires when this tab becomes the active tab.
5926          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5927          * @param {Roo.TabPanelItem} this
5928          */
5929         "activate": true,
5930         /**
5931          * @event beforeclose
5932          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5933          * @param {Roo.TabPanelItem} this
5934          * @param {Object} e Set cancel to true on this object to cancel the close.
5935          */
5936         "beforeclose": true,
5937         /**
5938          * @event close
5939          * Fires when this tab is closed.
5940          * @param {Roo.TabPanelItem} this
5941          */
5942          "close": true,
5943         /**
5944          * @event deactivate
5945          * Fires when this tab is no longer the active tab.
5946          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5947          * @param {Roo.TabPanelItem} this
5948          */
5949          "deactivate" : true
5950     });
5951     this.hidden = false;
5952
5953     Roo.TabPanelItem.superclass.constructor.call(this);
5954 };
5955
5956 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5957     purgeListeners : function(){
5958        Roo.util.Observable.prototype.purgeListeners.call(this);
5959        this.el.removeAllListeners();
5960     },
5961     /**
5962      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5963      */
5964     show : function(){
5965         this.pnode.addClass("on");
5966         this.showAction();
5967         if(Roo.isOpera){
5968             this.tabPanel.stripWrap.repaint();
5969         }
5970         this.fireEvent("activate", this.tabPanel, this);
5971     },
5972
5973     /**
5974      * Returns true if this tab is the active tab.
5975      * @return {Boolean}
5976      */
5977     isActive : function(){
5978         return this.tabPanel.getActiveTab() == this;
5979     },
5980
5981     /**
5982      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5983      */
5984     hide : function(){
5985         this.pnode.removeClass("on");
5986         this.hideAction();
5987         this.fireEvent("deactivate", this.tabPanel, this);
5988     },
5989
5990     hideAction : function(){
5991         this.bodyEl.hide();
5992         this.bodyEl.setStyle("position", "absolute");
5993         this.bodyEl.setLeft("-20000px");
5994         this.bodyEl.setTop("-20000px");
5995     },
5996
5997     showAction : function(){
5998         this.bodyEl.setStyle("position", "relative");
5999         this.bodyEl.setTop("");
6000         this.bodyEl.setLeft("");
6001         this.bodyEl.show();
6002     },
6003
6004     /**
6005      * Set the tooltip for the tab.
6006      * @param {String} tooltip The tab's tooltip
6007      */
6008     setTooltip : function(text){
6009         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6010             this.textEl.dom.qtip = text;
6011             this.textEl.dom.removeAttribute('title');
6012         }else{
6013             this.textEl.dom.title = text;
6014         }
6015     },
6016
6017     onTabClick : function(e){
6018         e.preventDefault();
6019         this.tabPanel.activate(this.id);
6020     },
6021
6022     onTabMouseDown : function(e){
6023         e.preventDefault();
6024         this.tabPanel.activate(this.id);
6025     },
6026
6027     getWidth : function(){
6028         return this.inner.getWidth();
6029     },
6030
6031     setWidth : function(width){
6032         var iwidth = width - this.pnode.getPadding("lr");
6033         this.inner.setWidth(iwidth);
6034         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6035         this.pnode.setWidth(width);
6036     },
6037
6038     /**
6039      * Show or hide the tab
6040      * @param {Boolean} hidden True to hide or false to show.
6041      */
6042     setHidden : function(hidden){
6043         this.hidden = hidden;
6044         this.pnode.setStyle("display", hidden ? "none" : "");
6045     },
6046
6047     /**
6048      * Returns true if this tab is "hidden"
6049      * @return {Boolean}
6050      */
6051     isHidden : function(){
6052         return this.hidden;
6053     },
6054
6055     /**
6056      * Returns the text for this tab
6057      * @return {String}
6058      */
6059     getText : function(){
6060         return this.text;
6061     },
6062
6063     autoSize : function(){
6064         //this.el.beginMeasure();
6065         this.textEl.setWidth(1);
6066         /*
6067          *  #2804 [new] Tabs in Roojs
6068          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6069          */
6070         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6071         //this.el.endMeasure();
6072     },
6073
6074     /**
6075      * Sets the text for the tab (Note: this also sets the tooltip text)
6076      * @param {String} text The tab's text and tooltip
6077      */
6078     setText : function(text){
6079         this.text = text;
6080         this.textEl.update(text);
6081         this.setTooltip(text);
6082         if(!this.tabPanel.resizeTabs){
6083             this.autoSize();
6084         }
6085     },
6086     /**
6087      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6088      */
6089     activate : function(){
6090         this.tabPanel.activate(this.id);
6091     },
6092
6093     /**
6094      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6095      */
6096     disable : function(){
6097         if(this.tabPanel.active != this){
6098             this.disabled = true;
6099             this.pnode.addClass("disabled");
6100         }
6101     },
6102
6103     /**
6104      * Enables this TabPanelItem if it was previously disabled.
6105      */
6106     enable : function(){
6107         this.disabled = false;
6108         this.pnode.removeClass("disabled");
6109     },
6110
6111     /**
6112      * Sets the content for this TabPanelItem.
6113      * @param {String} content The content
6114      * @param {Boolean} loadScripts true to look for and load scripts
6115      */
6116     setContent : function(content, loadScripts){
6117         this.bodyEl.update(content, loadScripts);
6118     },
6119
6120     /**
6121      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6122      * @return {Roo.UpdateManager} The UpdateManager
6123      */
6124     getUpdateManager : function(){
6125         return this.bodyEl.getUpdateManager();
6126     },
6127
6128     /**
6129      * Set a URL to be used to load the content for this TabPanelItem.
6130      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6131      * @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)
6132      * @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)
6133      * @return {Roo.UpdateManager} The UpdateManager
6134      */
6135     setUrl : function(url, params, loadOnce){
6136         if(this.refreshDelegate){
6137             this.un('activate', this.refreshDelegate);
6138         }
6139         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6140         this.on("activate", this.refreshDelegate);
6141         return this.bodyEl.getUpdateManager();
6142     },
6143
6144     /** @private */
6145     _handleRefresh : function(url, params, loadOnce){
6146         if(!loadOnce || !this.loaded){
6147             var updater = this.bodyEl.getUpdateManager();
6148             updater.update(url, params, this._setLoaded.createDelegate(this));
6149         }
6150     },
6151
6152     /**
6153      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6154      *   Will fail silently if the setUrl method has not been called.
6155      *   This does not activate the panel, just updates its content.
6156      */
6157     refresh : function(){
6158         if(this.refreshDelegate){
6159            this.loaded = false;
6160            this.refreshDelegate();
6161         }
6162     },
6163
6164     /** @private */
6165     _setLoaded : function(){
6166         this.loaded = true;
6167     },
6168
6169     /** @private */
6170     closeClick : function(e){
6171         var o = {};
6172         e.stopEvent();
6173         this.fireEvent("beforeclose", this, o);
6174         if(o.cancel !== true){
6175             this.tabPanel.removeTab(this.id);
6176         }
6177     },
6178     /**
6179      * The text displayed in the tooltip for the close icon.
6180      * @type String
6181      */
6182     closeText : "Close this tab"
6183 });
6184
6185 /** @private */
6186 Roo.TabPanel.prototype.createStrip = function(container){
6187     var strip = document.createElement("div");
6188     strip.className = "x-tabs-wrap";
6189     container.appendChild(strip);
6190     return strip;
6191 };
6192 /** @private */
6193 Roo.TabPanel.prototype.createStripList = function(strip){
6194     // div wrapper for retard IE
6195     // returns the "tr" element.
6196     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6197         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6198         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6199     return strip.firstChild.firstChild.firstChild.firstChild;
6200 };
6201 /** @private */
6202 Roo.TabPanel.prototype.createBody = function(container){
6203     var body = document.createElement("div");
6204     Roo.id(body, "tab-body");
6205     Roo.fly(body).addClass("x-tabs-body");
6206     container.appendChild(body);
6207     return body;
6208 };
6209 /** @private */
6210 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6211     var body = Roo.getDom(id);
6212     if(!body){
6213         body = document.createElement("div");
6214         body.id = id;
6215     }
6216     Roo.fly(body).addClass("x-tabs-item-body");
6217     bodyEl.insertBefore(body, bodyEl.firstChild);
6218     return body;
6219 };
6220 /** @private */
6221 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6222     var td = document.createElement("td");
6223     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6224     //stripEl.appendChild(td);
6225     if(closable){
6226         td.className = "x-tabs-closable";
6227         if(!this.closeTpl){
6228             this.closeTpl = new Roo.Template(
6229                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6230                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6231                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6232             );
6233         }
6234         var el = this.closeTpl.overwrite(td, {"text": text});
6235         var close = el.getElementsByTagName("div")[0];
6236         var inner = el.getElementsByTagName("em")[0];
6237         return {"el": el, "close": close, "inner": inner};
6238     } else {
6239         if(!this.tabTpl){
6240             this.tabTpl = new Roo.Template(
6241                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6242                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6243             );
6244         }
6245         var el = this.tabTpl.overwrite(td, {"text": text});
6246         var inner = el.getElementsByTagName("em")[0];
6247         return {"el": el, "inner": inner};
6248     }
6249 };/*
6250  * Based on:
6251  * Ext JS Library 1.1.1
6252  * Copyright(c) 2006-2007, Ext JS, LLC.
6253  *
6254  * Originally Released Under LGPL - original licence link has changed is not relivant.
6255  *
6256  * Fork - LGPL
6257  * <script type="text/javascript">
6258  */
6259
6260 /**
6261  * @class Roo.Button
6262  * @extends Roo.util.Observable
6263  * Simple Button class
6264  * @cfg {String} text The button text
6265  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6266  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6267  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6268  * @cfg {Object} scope The scope of the handler
6269  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6270  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6271  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6272  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6273  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6274  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6275    applies if enableToggle = true)
6276  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6277  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6278   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6279  * @constructor
6280  * Create a new button
6281  * @param {Object} config The config object
6282  */
6283 Roo.Button = function(renderTo, config)
6284 {
6285     if (!config) {
6286         config = renderTo;
6287         renderTo = config.renderTo || false;
6288     }
6289     
6290     Roo.apply(this, config);
6291     this.addEvents({
6292         /**
6293              * @event click
6294              * Fires when this button is clicked
6295              * @param {Button} this
6296              * @param {EventObject} e The click event
6297              */
6298             "click" : true,
6299         /**
6300              * @event toggle
6301              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6302              * @param {Button} this
6303              * @param {Boolean} pressed
6304              */
6305             "toggle" : true,
6306         /**
6307              * @event mouseover
6308              * Fires when the mouse hovers over the button
6309              * @param {Button} this
6310              * @param {Event} e The event object
6311              */
6312         'mouseover' : true,
6313         /**
6314              * @event mouseout
6315              * Fires when the mouse exits the button
6316              * @param {Button} this
6317              * @param {Event} e The event object
6318              */
6319         'mouseout': true,
6320          /**
6321              * @event render
6322              * Fires when the button is rendered
6323              * @param {Button} this
6324              */
6325         'render': true
6326     });
6327     if(this.menu){
6328         this.menu = Roo.menu.MenuMgr.get(this.menu);
6329     }
6330     // register listeners first!!  - so render can be captured..
6331     Roo.util.Observable.call(this);
6332     if(renderTo){
6333         this.render(renderTo);
6334     }
6335     
6336   
6337 };
6338
6339 Roo.extend(Roo.Button, Roo.util.Observable, {
6340     /**
6341      * 
6342      */
6343     
6344     /**
6345      * Read-only. True if this button is hidden
6346      * @type Boolean
6347      */
6348     hidden : false,
6349     /**
6350      * Read-only. True if this button is disabled
6351      * @type Boolean
6352      */
6353     disabled : false,
6354     /**
6355      * Read-only. True if this button is pressed (only if enableToggle = true)
6356      * @type Boolean
6357      */
6358     pressed : false,
6359
6360     /**
6361      * @cfg {Number} tabIndex 
6362      * The DOM tabIndex for this button (defaults to undefined)
6363      */
6364     tabIndex : undefined,
6365
6366     /**
6367      * @cfg {Boolean} enableToggle
6368      * True to enable pressed/not pressed toggling (defaults to false)
6369      */
6370     enableToggle: false,
6371     /**
6372      * @cfg {Roo.menu.Menu} menu
6373      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6374      */
6375     menu : undefined,
6376     /**
6377      * @cfg {String} menuAlign
6378      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6379      */
6380     menuAlign : "tl-bl?",
6381
6382     /**
6383      * @cfg {String} iconCls
6384      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6385      */
6386     iconCls : undefined,
6387     /**
6388      * @cfg {String} type
6389      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6390      */
6391     type : 'button',
6392
6393     // private
6394     menuClassTarget: 'tr',
6395
6396     /**
6397      * @cfg {String} clickEvent
6398      * The type of event to map to the button's event handler (defaults to 'click')
6399      */
6400     clickEvent : 'click',
6401
6402     /**
6403      * @cfg {Boolean} handleMouseEvents
6404      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6405      */
6406     handleMouseEvents : true,
6407
6408     /**
6409      * @cfg {String} tooltipType
6410      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6411      */
6412     tooltipType : 'qtip',
6413
6414     /**
6415      * @cfg {String} cls
6416      * A CSS class to apply to the button's main element.
6417      */
6418     
6419     /**
6420      * @cfg {Roo.Template} template (Optional)
6421      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6422      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6423      * require code modifications if required elements (e.g. a button) aren't present.
6424      */
6425
6426     // private
6427     render : function(renderTo){
6428         var btn;
6429         if(this.hideParent){
6430             this.parentEl = Roo.get(renderTo);
6431         }
6432         if(!this.dhconfig){
6433             if(!this.template){
6434                 if(!Roo.Button.buttonTemplate){
6435                     // hideous table template
6436                     Roo.Button.buttonTemplate = new Roo.Template(
6437                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6438                         '<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>',
6439                         "</tr></tbody></table>");
6440                 }
6441                 this.template = Roo.Button.buttonTemplate;
6442             }
6443             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6444             var btnEl = btn.child("button:first");
6445             btnEl.on('focus', this.onFocus, this);
6446             btnEl.on('blur', this.onBlur, this);
6447             if(this.cls){
6448                 btn.addClass(this.cls);
6449             }
6450             if(this.icon){
6451                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6452             }
6453             if(this.iconCls){
6454                 btnEl.addClass(this.iconCls);
6455                 if(!this.cls){
6456                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6457                 }
6458             }
6459             if(this.tabIndex !== undefined){
6460                 btnEl.dom.tabIndex = this.tabIndex;
6461             }
6462             if(this.tooltip){
6463                 if(typeof this.tooltip == 'object'){
6464                     Roo.QuickTips.tips(Roo.apply({
6465                           target: btnEl.id
6466                     }, this.tooltip));
6467                 } else {
6468                     btnEl.dom[this.tooltipType] = this.tooltip;
6469                 }
6470             }
6471         }else{
6472             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6473         }
6474         this.el = btn;
6475         if(this.id){
6476             this.el.dom.id = this.el.id = this.id;
6477         }
6478         if(this.menu){
6479             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6480             this.menu.on("show", this.onMenuShow, this);
6481             this.menu.on("hide", this.onMenuHide, this);
6482         }
6483         btn.addClass("x-btn");
6484         if(Roo.isIE && !Roo.isIE7){
6485             this.autoWidth.defer(1, this);
6486         }else{
6487             this.autoWidth();
6488         }
6489         if(this.handleMouseEvents){
6490             btn.on("mouseover", this.onMouseOver, this);
6491             btn.on("mouseout", this.onMouseOut, this);
6492             btn.on("mousedown", this.onMouseDown, this);
6493         }
6494         btn.on(this.clickEvent, this.onClick, this);
6495         //btn.on("mouseup", this.onMouseUp, this);
6496         if(this.hidden){
6497             this.hide();
6498         }
6499         if(this.disabled){
6500             this.disable();
6501         }
6502         Roo.ButtonToggleMgr.register(this);
6503         if(this.pressed){
6504             this.el.addClass("x-btn-pressed");
6505         }
6506         if(this.repeat){
6507             var repeater = new Roo.util.ClickRepeater(btn,
6508                 typeof this.repeat == "object" ? this.repeat : {}
6509             );
6510             repeater.on("click", this.onClick,  this);
6511         }
6512         
6513         this.fireEvent('render', this);
6514         
6515     },
6516     /**
6517      * Returns the button's underlying element
6518      * @return {Roo.Element} The element
6519      */
6520     getEl : function(){
6521         return this.el;  
6522     },
6523     
6524     /**
6525      * Destroys this Button and removes any listeners.
6526      */
6527     destroy : function(){
6528         Roo.ButtonToggleMgr.unregister(this);
6529         this.el.removeAllListeners();
6530         this.purgeListeners();
6531         this.el.remove();
6532     },
6533
6534     // private
6535     autoWidth : function(){
6536         if(this.el){
6537             this.el.setWidth("auto");
6538             if(Roo.isIE7 && Roo.isStrict){
6539                 var ib = this.el.child('button');
6540                 if(ib && ib.getWidth() > 20){
6541                     ib.clip();
6542                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6543                 }
6544             }
6545             if(this.minWidth){
6546                 if(this.hidden){
6547                     this.el.beginMeasure();
6548                 }
6549                 if(this.el.getWidth() < this.minWidth){
6550                     this.el.setWidth(this.minWidth);
6551                 }
6552                 if(this.hidden){
6553                     this.el.endMeasure();
6554                 }
6555             }
6556         }
6557     },
6558
6559     /**
6560      * Assigns this button's click handler
6561      * @param {Function} handler The function to call when the button is clicked
6562      * @param {Object} scope (optional) Scope for the function passed in
6563      */
6564     setHandler : function(handler, scope){
6565         this.handler = handler;
6566         this.scope = scope;  
6567     },
6568     
6569     /**
6570      * Sets this button's text
6571      * @param {String} text The button text
6572      */
6573     setText : function(text){
6574         this.text = text;
6575         if(this.el){
6576             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6577         }
6578         this.autoWidth();
6579     },
6580     
6581     /**
6582      * Gets the text for this button
6583      * @return {String} The button text
6584      */
6585     getText : function(){
6586         return this.text;  
6587     },
6588     
6589     /**
6590      * Show this button
6591      */
6592     show: function(){
6593         this.hidden = false;
6594         if(this.el){
6595             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6596         }
6597     },
6598     
6599     /**
6600      * Hide this button
6601      */
6602     hide: function(){
6603         this.hidden = true;
6604         if(this.el){
6605             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6606         }
6607     },
6608     
6609     /**
6610      * Convenience function for boolean show/hide
6611      * @param {Boolean} visible True to show, false to hide
6612      */
6613     setVisible: function(visible){
6614         if(visible) {
6615             this.show();
6616         }else{
6617             this.hide();
6618         }
6619     },
6620     
6621     /**
6622      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6623      * @param {Boolean} state (optional) Force a particular state
6624      */
6625     toggle : function(state){
6626         state = state === undefined ? !this.pressed : state;
6627         if(state != this.pressed){
6628             if(state){
6629                 this.el.addClass("x-btn-pressed");
6630                 this.pressed = true;
6631                 this.fireEvent("toggle", this, true);
6632             }else{
6633                 this.el.removeClass("x-btn-pressed");
6634                 this.pressed = false;
6635                 this.fireEvent("toggle", this, false);
6636             }
6637             if(this.toggleHandler){
6638                 this.toggleHandler.call(this.scope || this, this, state);
6639             }
6640         }
6641     },
6642     
6643     /**
6644      * Focus the button
6645      */
6646     focus : function(){
6647         this.el.child('button:first').focus();
6648     },
6649     
6650     /**
6651      * Disable this button
6652      */
6653     disable : function(){
6654         if(this.el){
6655             this.el.addClass("x-btn-disabled");
6656         }
6657         this.disabled = true;
6658     },
6659     
6660     /**
6661      * Enable this button
6662      */
6663     enable : function(){
6664         if(this.el){
6665             this.el.removeClass("x-btn-disabled");
6666         }
6667         this.disabled = false;
6668     },
6669
6670     /**
6671      * Convenience function for boolean enable/disable
6672      * @param {Boolean} enabled True to enable, false to disable
6673      */
6674     setDisabled : function(v){
6675         this[v !== true ? "enable" : "disable"]();
6676     },
6677
6678     // private
6679     onClick : function(e)
6680     {
6681         if(e){
6682             e.preventDefault();
6683         }
6684         if(e.button != 0){
6685             return;
6686         }
6687         if(!this.disabled){
6688             if(this.enableToggle){
6689                 this.toggle();
6690             }
6691             if(this.menu && !this.menu.isVisible()){
6692                 this.menu.show(this.el, this.menuAlign);
6693             }
6694             this.fireEvent("click", this, e);
6695             if(this.handler){
6696                 this.el.removeClass("x-btn-over");
6697                 this.handler.call(this.scope || this, this, e);
6698             }
6699         }
6700     },
6701     // private
6702     onMouseOver : function(e){
6703         if(!this.disabled){
6704             this.el.addClass("x-btn-over");
6705             this.fireEvent('mouseover', this, e);
6706         }
6707     },
6708     // private
6709     onMouseOut : function(e){
6710         if(!e.within(this.el,  true)){
6711             this.el.removeClass("x-btn-over");
6712             this.fireEvent('mouseout', this, e);
6713         }
6714     },
6715     // private
6716     onFocus : function(e){
6717         if(!this.disabled){
6718             this.el.addClass("x-btn-focus");
6719         }
6720     },
6721     // private
6722     onBlur : function(e){
6723         this.el.removeClass("x-btn-focus");
6724     },
6725     // private
6726     onMouseDown : function(e){
6727         if(!this.disabled && e.button == 0){
6728             this.el.addClass("x-btn-click");
6729             Roo.get(document).on('mouseup', this.onMouseUp, this);
6730         }
6731     },
6732     // private
6733     onMouseUp : function(e){
6734         if(e.button == 0){
6735             this.el.removeClass("x-btn-click");
6736             Roo.get(document).un('mouseup', this.onMouseUp, this);
6737         }
6738     },
6739     // private
6740     onMenuShow : function(e){
6741         this.el.addClass("x-btn-menu-active");
6742     },
6743     // private
6744     onMenuHide : function(e){
6745         this.el.removeClass("x-btn-menu-active");
6746     }   
6747 });
6748
6749 // Private utility class used by Button
6750 Roo.ButtonToggleMgr = function(){
6751    var groups = {};
6752    
6753    function toggleGroup(btn, state){
6754        if(state){
6755            var g = groups[btn.toggleGroup];
6756            for(var i = 0, l = g.length; i < l; i++){
6757                if(g[i] != btn){
6758                    g[i].toggle(false);
6759                }
6760            }
6761        }
6762    }
6763    
6764    return {
6765        register : function(btn){
6766            if(!btn.toggleGroup){
6767                return;
6768            }
6769            var g = groups[btn.toggleGroup];
6770            if(!g){
6771                g = groups[btn.toggleGroup] = [];
6772            }
6773            g.push(btn);
6774            btn.on("toggle", toggleGroup);
6775        },
6776        
6777        unregister : function(btn){
6778            if(!btn.toggleGroup){
6779                return;
6780            }
6781            var g = groups[btn.toggleGroup];
6782            if(g){
6783                g.remove(btn);
6784                btn.un("toggle", toggleGroup);
6785            }
6786        }
6787    };
6788 }();/*
6789  * Based on:
6790  * Ext JS Library 1.1.1
6791  * Copyright(c) 2006-2007, Ext JS, LLC.
6792  *
6793  * Originally Released Under LGPL - original licence link has changed is not relivant.
6794  *
6795  * Fork - LGPL
6796  * <script type="text/javascript">
6797  */
6798  
6799 /**
6800  * @class Roo.SplitButton
6801  * @extends Roo.Button
6802  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6803  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6804  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6805  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6806  * @cfg {String} arrowTooltip The title attribute of the arrow
6807  * @constructor
6808  * Create a new menu button
6809  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6810  * @param {Object} config The config object
6811  */
6812 Roo.SplitButton = function(renderTo, config){
6813     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6814     /**
6815      * @event arrowclick
6816      * Fires when this button's arrow is clicked
6817      * @param {SplitButton} this
6818      * @param {EventObject} e The click event
6819      */
6820     this.addEvents({"arrowclick":true});
6821 };
6822
6823 Roo.extend(Roo.SplitButton, Roo.Button, {
6824     render : function(renderTo){
6825         // this is one sweet looking template!
6826         var tpl = new Roo.Template(
6827             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6828             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6829             '<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>',
6830             "</tbody></table></td><td>",
6831             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6832             '<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>',
6833             "</tbody></table></td></tr></table>"
6834         );
6835         var btn = tpl.append(renderTo, [this.text, this.type], true);
6836         var btnEl = btn.child("button");
6837         if(this.cls){
6838             btn.addClass(this.cls);
6839         }
6840         if(this.icon){
6841             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6842         }
6843         if(this.iconCls){
6844             btnEl.addClass(this.iconCls);
6845             if(!this.cls){
6846                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6847             }
6848         }
6849         this.el = btn;
6850         if(this.handleMouseEvents){
6851             btn.on("mouseover", this.onMouseOver, this);
6852             btn.on("mouseout", this.onMouseOut, this);
6853             btn.on("mousedown", this.onMouseDown, this);
6854             btn.on("mouseup", this.onMouseUp, this);
6855         }
6856         btn.on(this.clickEvent, this.onClick, this);
6857         if(this.tooltip){
6858             if(typeof this.tooltip == 'object'){
6859                 Roo.QuickTips.tips(Roo.apply({
6860                       target: btnEl.id
6861                 }, this.tooltip));
6862             } else {
6863                 btnEl.dom[this.tooltipType] = this.tooltip;
6864             }
6865         }
6866         if(this.arrowTooltip){
6867             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6868         }
6869         if(this.hidden){
6870             this.hide();
6871         }
6872         if(this.disabled){
6873             this.disable();
6874         }
6875         if(this.pressed){
6876             this.el.addClass("x-btn-pressed");
6877         }
6878         if(Roo.isIE && !Roo.isIE7){
6879             this.autoWidth.defer(1, this);
6880         }else{
6881             this.autoWidth();
6882         }
6883         if(this.menu){
6884             this.menu.on("show", this.onMenuShow, this);
6885             this.menu.on("hide", this.onMenuHide, this);
6886         }
6887         this.fireEvent('render', this);
6888     },
6889
6890     // private
6891     autoWidth : function(){
6892         if(this.el){
6893             var tbl = this.el.child("table:first");
6894             var tbl2 = this.el.child("table:last");
6895             this.el.setWidth("auto");
6896             tbl.setWidth("auto");
6897             if(Roo.isIE7 && Roo.isStrict){
6898                 var ib = this.el.child('button:first');
6899                 if(ib && ib.getWidth() > 20){
6900                     ib.clip();
6901                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6902                 }
6903             }
6904             if(this.minWidth){
6905                 if(this.hidden){
6906                     this.el.beginMeasure();
6907                 }
6908                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6909                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6910                 }
6911                 if(this.hidden){
6912                     this.el.endMeasure();
6913                 }
6914             }
6915             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6916         } 
6917     },
6918     /**
6919      * Sets this button's click handler
6920      * @param {Function} handler The function to call when the button is clicked
6921      * @param {Object} scope (optional) Scope for the function passed above
6922      */
6923     setHandler : function(handler, scope){
6924         this.handler = handler;
6925         this.scope = scope;  
6926     },
6927     
6928     /**
6929      * Sets this button's arrow click handler
6930      * @param {Function} handler The function to call when the arrow is clicked
6931      * @param {Object} scope (optional) Scope for the function passed above
6932      */
6933     setArrowHandler : function(handler, scope){
6934         this.arrowHandler = handler;
6935         this.scope = scope;  
6936     },
6937     
6938     /**
6939      * Focus the button
6940      */
6941     focus : function(){
6942         if(this.el){
6943             this.el.child("button:first").focus();
6944         }
6945     },
6946
6947     // private
6948     onClick : function(e){
6949         e.preventDefault();
6950         if(!this.disabled){
6951             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6952                 if(this.menu && !this.menu.isVisible()){
6953                     this.menu.show(this.el, this.menuAlign);
6954                 }
6955                 this.fireEvent("arrowclick", this, e);
6956                 if(this.arrowHandler){
6957                     this.arrowHandler.call(this.scope || this, this, e);
6958                 }
6959             }else{
6960                 this.fireEvent("click", this, e);
6961                 if(this.handler){
6962                     this.handler.call(this.scope || this, this, e);
6963                 }
6964             }
6965         }
6966     },
6967     // private
6968     onMouseDown : function(e){
6969         if(!this.disabled){
6970             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6971         }
6972     },
6973     // private
6974     onMouseUp : function(e){
6975         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6976     }   
6977 });
6978
6979
6980 // backwards compat
6981 Roo.MenuButton = Roo.SplitButton;/*
6982  * Based on:
6983  * Ext JS Library 1.1.1
6984  * Copyright(c) 2006-2007, Ext JS, LLC.
6985  *
6986  * Originally Released Under LGPL - original licence link has changed is not relivant.
6987  *
6988  * Fork - LGPL
6989  * <script type="text/javascript">
6990  */
6991
6992 /**
6993  * @class Roo.Toolbar
6994  * @children   Roo.Toolbar.Item Roo.form.Field
6995  * Basic Toolbar class.
6996  * @constructor
6997  * Creates a new Toolbar
6998  * @param {Object} container The config object
6999  */ 
7000 Roo.Toolbar = function(container, buttons, config)
7001 {
7002     /// old consturctor format still supported..
7003     if(container instanceof Array){ // omit the container for later rendering
7004         buttons = container;
7005         config = buttons;
7006         container = null;
7007     }
7008     if (typeof(container) == 'object' && container.xtype) {
7009         config = container;
7010         container = config.container;
7011         buttons = config.buttons || []; // not really - use items!!
7012     }
7013     var xitems = [];
7014     if (config && config.items) {
7015         xitems = config.items;
7016         delete config.items;
7017     }
7018     Roo.apply(this, config);
7019     this.buttons = buttons;
7020     
7021     if(container){
7022         this.render(container);
7023     }
7024     this.xitems = xitems;
7025     Roo.each(xitems, function(b) {
7026         this.add(b);
7027     }, this);
7028     
7029 };
7030
7031 Roo.Toolbar.prototype = {
7032     /**
7033      * @cfg {Array} items
7034      * array of button configs or elements to add (will be converted to a MixedCollection)
7035      */
7036     items: false,
7037     /**
7038      * @cfg {String/HTMLElement/Element} container
7039      * The id or element that will contain the toolbar
7040      */
7041     // private
7042     render : function(ct){
7043         this.el = Roo.get(ct);
7044         if(this.cls){
7045             this.el.addClass(this.cls);
7046         }
7047         // using a table allows for vertical alignment
7048         // 100% width is needed by Safari...
7049         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7050         this.tr = this.el.child("tr", true);
7051         var autoId = 0;
7052         this.items = new Roo.util.MixedCollection(false, function(o){
7053             return o.id || ("item" + (++autoId));
7054         });
7055         if(this.buttons){
7056             this.add.apply(this, this.buttons);
7057             delete this.buttons;
7058         }
7059     },
7060
7061     /**
7062      * Adds element(s) to the toolbar -- this function takes a variable number of 
7063      * arguments of mixed type and adds them to the toolbar.
7064      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7065      * <ul>
7066      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7067      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7068      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7069      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7070      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7071      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7072      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7073      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7074      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7075      * </ul>
7076      * @param {Mixed} arg2
7077      * @param {Mixed} etc.
7078      */
7079     add : function(){
7080         var a = arguments, l = a.length;
7081         for(var i = 0; i < l; i++){
7082             this._add(a[i]);
7083         }
7084     },
7085     // private..
7086     _add : function(el) {
7087         
7088         if (el.xtype) {
7089             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7090         }
7091         
7092         if (el.applyTo){ // some kind of form field
7093             return this.addField(el);
7094         } 
7095         if (el.render){ // some kind of Toolbar.Item
7096             return this.addItem(el);
7097         }
7098         if (typeof el == "string"){ // string
7099             if(el == "separator" || el == "-"){
7100                 return this.addSeparator();
7101             }
7102             if (el == " "){
7103                 return this.addSpacer();
7104             }
7105             if(el == "->"){
7106                 return this.addFill();
7107             }
7108             return this.addText(el);
7109             
7110         }
7111         if(el.tagName){ // element
7112             return this.addElement(el);
7113         }
7114         if(typeof el == "object"){ // must be button config?
7115             return this.addButton(el);
7116         }
7117         // and now what?!?!
7118         return false;
7119         
7120     },
7121     
7122     /**
7123      * Add an Xtype element
7124      * @param {Object} xtype Xtype Object
7125      * @return {Object} created Object
7126      */
7127     addxtype : function(e){
7128         return this.add(e);  
7129     },
7130     
7131     /**
7132      * Returns the Element for this toolbar.
7133      * @return {Roo.Element}
7134      */
7135     getEl : function(){
7136         return this.el;  
7137     },
7138     
7139     /**
7140      * Adds a separator
7141      * @return {Roo.Toolbar.Item} The separator item
7142      */
7143     addSeparator : function(){
7144         return this.addItem(new Roo.Toolbar.Separator());
7145     },
7146
7147     /**
7148      * Adds a spacer element
7149      * @return {Roo.Toolbar.Spacer} The spacer item
7150      */
7151     addSpacer : function(){
7152         return this.addItem(new Roo.Toolbar.Spacer());
7153     },
7154
7155     /**
7156      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7157      * @return {Roo.Toolbar.Fill} The fill item
7158      */
7159     addFill : function(){
7160         return this.addItem(new Roo.Toolbar.Fill());
7161     },
7162
7163     /**
7164      * Adds any standard HTML element to the toolbar
7165      * @param {String/HTMLElement/Element} el The element or id of the element to add
7166      * @return {Roo.Toolbar.Item} The element's item
7167      */
7168     addElement : function(el){
7169         return this.addItem(new Roo.Toolbar.Item(el));
7170     },
7171     /**
7172      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7173      * @type Roo.util.MixedCollection  
7174      */
7175     items : false,
7176      
7177     /**
7178      * Adds any Toolbar.Item or subclass
7179      * @param {Roo.Toolbar.Item} item
7180      * @return {Roo.Toolbar.Item} The item
7181      */
7182     addItem : function(item){
7183         var td = this.nextBlock();
7184         item.render(td);
7185         this.items.add(item);
7186         return item;
7187     },
7188     
7189     /**
7190      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7191      * @param {Object/Array} config A button config or array of configs
7192      * @return {Roo.Toolbar.Button/Array}
7193      */
7194     addButton : function(config){
7195         if(config instanceof Array){
7196             var buttons = [];
7197             for(var i = 0, len = config.length; i < len; i++) {
7198                 buttons.push(this.addButton(config[i]));
7199             }
7200             return buttons;
7201         }
7202         var b = config;
7203         if(!(config instanceof Roo.Toolbar.Button)){
7204             b = config.split ?
7205                 new Roo.Toolbar.SplitButton(config) :
7206                 new Roo.Toolbar.Button(config);
7207         }
7208         var td = this.nextBlock();
7209         b.render(td);
7210         this.items.add(b);
7211         return b;
7212     },
7213     
7214     /**
7215      * Adds text to the toolbar
7216      * @param {String} text The text to add
7217      * @return {Roo.Toolbar.Item} The element's item
7218      */
7219     addText : function(text){
7220         return this.addItem(new Roo.Toolbar.TextItem(text));
7221     },
7222     
7223     /**
7224      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7225      * @param {Number} index The index where the item is to be inserted
7226      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7227      * @return {Roo.Toolbar.Button/Item}
7228      */
7229     insertButton : function(index, item){
7230         if(item instanceof Array){
7231             var buttons = [];
7232             for(var i = 0, len = item.length; i < len; i++) {
7233                buttons.push(this.insertButton(index + i, item[i]));
7234             }
7235             return buttons;
7236         }
7237         if (!(item instanceof Roo.Toolbar.Button)){
7238            item = new Roo.Toolbar.Button(item);
7239         }
7240         var td = document.createElement("td");
7241         this.tr.insertBefore(td, this.tr.childNodes[index]);
7242         item.render(td);
7243         this.items.insert(index, item);
7244         return item;
7245     },
7246     
7247     /**
7248      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7249      * @param {Object} config
7250      * @return {Roo.Toolbar.Item} The element's item
7251      */
7252     addDom : function(config, returnEl){
7253         var td = this.nextBlock();
7254         Roo.DomHelper.overwrite(td, config);
7255         var ti = new Roo.Toolbar.Item(td.firstChild);
7256         ti.render(td);
7257         this.items.add(ti);
7258         return ti;
7259     },
7260
7261     /**
7262      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7263      * @type Roo.util.MixedCollection  
7264      */
7265     fields : false,
7266     
7267     /**
7268      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7269      * Note: the field should not have been rendered yet. For a field that has already been
7270      * rendered, use {@link #addElement}.
7271      * @param {Roo.form.Field} field
7272      * @return {Roo.ToolbarItem}
7273      */
7274      
7275       
7276     addField : function(field) {
7277         if (!this.fields) {
7278             var autoId = 0;
7279             this.fields = new Roo.util.MixedCollection(false, function(o){
7280                 return o.id || ("item" + (++autoId));
7281             });
7282
7283         }
7284         
7285         var td = this.nextBlock();
7286         field.render(td);
7287         var ti = new Roo.Toolbar.Item(td.firstChild);
7288         ti.render(td);
7289         this.items.add(ti);
7290         this.fields.add(field);
7291         return ti;
7292     },
7293     /**
7294      * Hide the toolbar
7295      * @method hide
7296      */
7297      
7298       
7299     hide : function()
7300     {
7301         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7302         this.el.child('div').hide();
7303     },
7304     /**
7305      * Show the toolbar
7306      * @method show
7307      */
7308     show : function()
7309     {
7310         this.el.child('div').show();
7311     },
7312       
7313     // private
7314     nextBlock : function(){
7315         var td = document.createElement("td");
7316         this.tr.appendChild(td);
7317         return td;
7318     },
7319
7320     // private
7321     destroy : function(){
7322         if(this.items){ // rendered?
7323             Roo.destroy.apply(Roo, this.items.items);
7324         }
7325         if(this.fields){ // rendered?
7326             Roo.destroy.apply(Roo, this.fields.items);
7327         }
7328         Roo.Element.uncache(this.el, this.tr);
7329     }
7330 };
7331
7332 /**
7333  * @class Roo.Toolbar.Item
7334  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7335  * @constructor
7336  * Creates a new Item
7337  * @param {HTMLElement} el 
7338  */
7339 Roo.Toolbar.Item = function(el){
7340     var cfg = {};
7341     if (typeof (el.xtype) != 'undefined') {
7342         cfg = el;
7343         el = cfg.el;
7344     }
7345     
7346     this.el = Roo.getDom(el);
7347     this.id = Roo.id(this.el);
7348     this.hidden = false;
7349     
7350     this.addEvents({
7351          /**
7352              * @event render
7353              * Fires when the button is rendered
7354              * @param {Button} this
7355              */
7356         'render': true
7357     });
7358     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7359 };
7360 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7361 //Roo.Toolbar.Item.prototype = {
7362     
7363     /**
7364      * Get this item's HTML Element
7365      * @return {HTMLElement}
7366      */
7367     getEl : function(){
7368        return this.el;  
7369     },
7370
7371     // private
7372     render : function(td){
7373         
7374          this.td = td;
7375         td.appendChild(this.el);
7376         
7377         this.fireEvent('render', this);
7378     },
7379     
7380     /**
7381      * Removes and destroys this item.
7382      */
7383     destroy : function(){
7384         this.td.parentNode.removeChild(this.td);
7385     },
7386     
7387     /**
7388      * Shows this item.
7389      */
7390     show: function(){
7391         this.hidden = false;
7392         this.td.style.display = "";
7393     },
7394     
7395     /**
7396      * Hides this item.
7397      */
7398     hide: function(){
7399         this.hidden = true;
7400         this.td.style.display = "none";
7401     },
7402     
7403     /**
7404      * Convenience function for boolean show/hide.
7405      * @param {Boolean} visible true to show/false to hide
7406      */
7407     setVisible: function(visible){
7408         if(visible) {
7409             this.show();
7410         }else{
7411             this.hide();
7412         }
7413     },
7414     
7415     /**
7416      * Try to focus this item.
7417      */
7418     focus : function(){
7419         Roo.fly(this.el).focus();
7420     },
7421     
7422     /**
7423      * Disables this item.
7424      */
7425     disable : function(){
7426         Roo.fly(this.td).addClass("x-item-disabled");
7427         this.disabled = true;
7428         this.el.disabled = true;
7429     },
7430     
7431     /**
7432      * Enables this item.
7433      */
7434     enable : function(){
7435         Roo.fly(this.td).removeClass("x-item-disabled");
7436         this.disabled = false;
7437         this.el.disabled = false;
7438     }
7439 });
7440
7441
7442 /**
7443  * @class Roo.Toolbar.Separator
7444  * @extends Roo.Toolbar.Item
7445  * A simple toolbar separator class
7446  * @constructor
7447  * Creates a new Separator
7448  */
7449 Roo.Toolbar.Separator = function(cfg){
7450     
7451     var s = document.createElement("span");
7452     s.className = "ytb-sep";
7453     if (cfg) {
7454         cfg.el = s;
7455     }
7456     
7457     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7458 };
7459 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7460     enable:Roo.emptyFn,
7461     disable:Roo.emptyFn,
7462     focus:Roo.emptyFn
7463 });
7464
7465 /**
7466  * @class Roo.Toolbar.Spacer
7467  * @extends Roo.Toolbar.Item
7468  * A simple element that adds extra horizontal space to a toolbar.
7469  * @constructor
7470  * Creates a new Spacer
7471  */
7472 Roo.Toolbar.Spacer = function(cfg){
7473     var s = document.createElement("div");
7474     s.className = "ytb-spacer";
7475     if (cfg) {
7476         cfg.el = s;
7477     }
7478     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7479 };
7480 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7481     enable:Roo.emptyFn,
7482     disable:Roo.emptyFn,
7483     focus:Roo.emptyFn
7484 });
7485
7486 /**
7487  * @class Roo.Toolbar.Fill
7488  * @extends Roo.Toolbar.Spacer
7489  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7490  * @constructor
7491  * Creates a new Spacer
7492  */
7493 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7494     // private
7495     render : function(td){
7496         td.style.width = '100%';
7497         Roo.Toolbar.Fill.superclass.render.call(this, td);
7498     }
7499 });
7500
7501 /**
7502  * @class Roo.Toolbar.TextItem
7503  * @extends Roo.Toolbar.Item
7504  * A simple class that renders text directly into a toolbar.
7505  * @constructor
7506  * Creates a new TextItem
7507  * @cfg {string} text 
7508  */
7509 Roo.Toolbar.TextItem = function(cfg){
7510     var  text = cfg || "";
7511     if (typeof(cfg) == 'object') {
7512         text = cfg.text || "";
7513     }  else {
7514         cfg = null;
7515     }
7516     var s = document.createElement("span");
7517     s.className = "ytb-text";
7518     s.innerHTML = text;
7519     if (cfg) {
7520         cfg.el  = s;
7521     }
7522     
7523     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7524 };
7525 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7526     
7527      
7528     enable:Roo.emptyFn,
7529     disable:Roo.emptyFn,
7530     focus:Roo.emptyFn
7531 });
7532
7533 /**
7534  * @class Roo.Toolbar.Button
7535  * @extends Roo.Button
7536  * A button that renders into a toolbar.
7537  * @constructor
7538  * Creates a new Button
7539  * @param {Object} config A standard {@link Roo.Button} config object
7540  */
7541 Roo.Toolbar.Button = function(config){
7542     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7543 };
7544 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7545 {
7546     
7547     
7548     render : function(td){
7549         this.td = td;
7550         Roo.Toolbar.Button.superclass.render.call(this, td);
7551     },
7552     
7553     /**
7554      * Removes and destroys this button
7555      */
7556     destroy : function(){
7557         Roo.Toolbar.Button.superclass.destroy.call(this);
7558         this.td.parentNode.removeChild(this.td);
7559     },
7560     
7561     /**
7562      * Shows this button
7563      */
7564     show: function(){
7565         this.hidden = false;
7566         this.td.style.display = "";
7567     },
7568     
7569     /**
7570      * Hides this button
7571      */
7572     hide: function(){
7573         this.hidden = true;
7574         this.td.style.display = "none";
7575     },
7576
7577     /**
7578      * Disables this item
7579      */
7580     disable : function(){
7581         Roo.fly(this.td).addClass("x-item-disabled");
7582         this.disabled = true;
7583     },
7584
7585     /**
7586      * Enables this item
7587      */
7588     enable : function(){
7589         Roo.fly(this.td).removeClass("x-item-disabled");
7590         this.disabled = false;
7591     }
7592 });
7593 // backwards compat
7594 Roo.ToolbarButton = Roo.Toolbar.Button;
7595
7596 /**
7597  * @class Roo.Toolbar.SplitButton
7598  * @extends Roo.SplitButton
7599  * A menu button that renders into a toolbar.
7600  * @constructor
7601  * Creates a new SplitButton
7602  * @param {Object} config A standard {@link Roo.SplitButton} config object
7603  */
7604 Roo.Toolbar.SplitButton = function(config){
7605     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7606 };
7607 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7608     render : function(td){
7609         this.td = td;
7610         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7611     },
7612     
7613     /**
7614      * Removes and destroys this button
7615      */
7616     destroy : function(){
7617         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7618         this.td.parentNode.removeChild(this.td);
7619     },
7620     
7621     /**
7622      * Shows this button
7623      */
7624     show: function(){
7625         this.hidden = false;
7626         this.td.style.display = "";
7627     },
7628     
7629     /**
7630      * Hides this button
7631      */
7632     hide: function(){
7633         this.hidden = true;
7634         this.td.style.display = "none";
7635     }
7636 });
7637
7638 // backwards compat
7639 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7640  * Based on:
7641  * Ext JS Library 1.1.1
7642  * Copyright(c) 2006-2007, Ext JS, LLC.
7643  *
7644  * Originally Released Under LGPL - original licence link has changed is not relivant.
7645  *
7646  * Fork - LGPL
7647  * <script type="text/javascript">
7648  */
7649  
7650 /**
7651  * @class Roo.PagingToolbar
7652  * @extends Roo.Toolbar
7653  * @children   Roo.Toolbar.Item Roo.form.Field
7654  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7655  * @constructor
7656  * Create a new PagingToolbar
7657  * @param {Object} config The config object
7658  */
7659 Roo.PagingToolbar = function(el, ds, config)
7660 {
7661     // old args format still supported... - xtype is prefered..
7662     if (typeof(el) == 'object' && el.xtype) {
7663         // created from xtype...
7664         config = el;
7665         ds = el.dataSource;
7666         el = config.container;
7667     }
7668     var items = [];
7669     if (config.items) {
7670         items = config.items;
7671         config.items = [];
7672     }
7673     
7674     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7675     this.ds = ds;
7676     this.cursor = 0;
7677     this.renderButtons(this.el);
7678     this.bind(ds);
7679     
7680     // supprot items array.
7681    
7682     Roo.each(items, function(e) {
7683         this.add(Roo.factory(e));
7684     },this);
7685     
7686 };
7687
7688 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7689     /**
7690      * @cfg {Roo.data.Store} dataSource
7691      * The underlying data store providing the paged data
7692      */
7693     /**
7694      * @cfg {String/HTMLElement/Element} container
7695      * container The id or element that will contain the toolbar
7696      */
7697     /**
7698      * @cfg {Boolean} displayInfo
7699      * True to display the displayMsg (defaults to false)
7700      */
7701     /**
7702      * @cfg {Number} pageSize
7703      * The number of records to display per page (defaults to 20)
7704      */
7705     pageSize: 20,
7706     /**
7707      * @cfg {String} displayMsg
7708      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7709      */
7710     displayMsg : 'Displaying {0} - {1} of {2}',
7711     /**
7712      * @cfg {String} emptyMsg
7713      * The message to display when no records are found (defaults to "No data to display")
7714      */
7715     emptyMsg : 'No data to display',
7716     /**
7717      * Customizable piece of the default paging text (defaults to "Page")
7718      * @type String
7719      */
7720     beforePageText : "Page",
7721     /**
7722      * Customizable piece of the default paging text (defaults to "of %0")
7723      * @type String
7724      */
7725     afterPageText : "of {0}",
7726     /**
7727      * Customizable piece of the default paging text (defaults to "First Page")
7728      * @type String
7729      */
7730     firstText : "First Page",
7731     /**
7732      * Customizable piece of the default paging text (defaults to "Previous Page")
7733      * @type String
7734      */
7735     prevText : "Previous Page",
7736     /**
7737      * Customizable piece of the default paging text (defaults to "Next Page")
7738      * @type String
7739      */
7740     nextText : "Next Page",
7741     /**
7742      * Customizable piece of the default paging text (defaults to "Last Page")
7743      * @type String
7744      */
7745     lastText : "Last Page",
7746     /**
7747      * Customizable piece of the default paging text (defaults to "Refresh")
7748      * @type String
7749      */
7750     refreshText : "Refresh",
7751
7752     // private
7753     renderButtons : function(el){
7754         Roo.PagingToolbar.superclass.render.call(this, el);
7755         this.first = this.addButton({
7756             tooltip: this.firstText,
7757             cls: "x-btn-icon x-grid-page-first",
7758             disabled: true,
7759             handler: this.onClick.createDelegate(this, ["first"])
7760         });
7761         this.prev = this.addButton({
7762             tooltip: this.prevText,
7763             cls: "x-btn-icon x-grid-page-prev",
7764             disabled: true,
7765             handler: this.onClick.createDelegate(this, ["prev"])
7766         });
7767         //this.addSeparator();
7768         this.add(this.beforePageText);
7769         this.field = Roo.get(this.addDom({
7770            tag: "input",
7771            type: "text",
7772            size: "3",
7773            value: "1",
7774            cls: "x-grid-page-number"
7775         }).el);
7776         this.field.on("keydown", this.onPagingKeydown, this);
7777         this.field.on("focus", function(){this.dom.select();});
7778         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7779         this.field.setHeight(18);
7780         //this.addSeparator();
7781         this.next = this.addButton({
7782             tooltip: this.nextText,
7783             cls: "x-btn-icon x-grid-page-next",
7784             disabled: true,
7785             handler: this.onClick.createDelegate(this, ["next"])
7786         });
7787         this.last = this.addButton({
7788             tooltip: this.lastText,
7789             cls: "x-btn-icon x-grid-page-last",
7790             disabled: true,
7791             handler: this.onClick.createDelegate(this, ["last"])
7792         });
7793         //this.addSeparator();
7794         this.loading = this.addButton({
7795             tooltip: this.refreshText,
7796             cls: "x-btn-icon x-grid-loading",
7797             handler: this.onClick.createDelegate(this, ["refresh"])
7798         });
7799
7800         if(this.displayInfo){
7801             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7802         }
7803     },
7804
7805     // private
7806     updateInfo : function(){
7807         if(this.displayEl){
7808             var count = this.ds.getCount();
7809             var msg = count == 0 ?
7810                 this.emptyMsg :
7811                 String.format(
7812                     this.displayMsg,
7813                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7814                 );
7815             this.displayEl.update(msg);
7816         }
7817     },
7818
7819     // private
7820     onLoad : function(ds, r, o){
7821        this.cursor = o.params ? o.params.start : 0;
7822        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7823
7824        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7825        this.field.dom.value = ap;
7826        this.first.setDisabled(ap == 1);
7827        this.prev.setDisabled(ap == 1);
7828        this.next.setDisabled(ap == ps);
7829        this.last.setDisabled(ap == ps);
7830        this.loading.enable();
7831        this.updateInfo();
7832     },
7833
7834     // private
7835     getPageData : function(){
7836         var total = this.ds.getTotalCount();
7837         return {
7838             total : total,
7839             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7840             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7841         };
7842     },
7843
7844     // private
7845     onLoadError : function(){
7846         this.loading.enable();
7847     },
7848
7849     // private
7850     onPagingKeydown : function(e){
7851         var k = e.getKey();
7852         var d = this.getPageData();
7853         if(k == e.RETURN){
7854             var v = this.field.dom.value, pageNum;
7855             if(!v || isNaN(pageNum = parseInt(v, 10))){
7856                 this.field.dom.value = d.activePage;
7857                 return;
7858             }
7859             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7861             e.stopEvent();
7862         }
7863         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))
7864         {
7865           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7866           this.field.dom.value = pageNum;
7867           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7868           e.stopEvent();
7869         }
7870         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7871         {
7872           var v = this.field.dom.value, pageNum; 
7873           var increment = (e.shiftKey) ? 10 : 1;
7874           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7875             increment *= -1;
7876           }
7877           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7878             this.field.dom.value = d.activePage;
7879             return;
7880           }
7881           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7882           {
7883             this.field.dom.value = parseInt(v, 10) + increment;
7884             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7885             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7886           }
7887           e.stopEvent();
7888         }
7889     },
7890
7891     // private
7892     beforeLoad : function(){
7893         if(this.loading){
7894             this.loading.disable();
7895         }
7896     },
7897
7898     // private
7899     onClick : function(which){
7900         var ds = this.ds;
7901         switch(which){
7902             case "first":
7903                 ds.load({params:{start: 0, limit: this.pageSize}});
7904             break;
7905             case "prev":
7906                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7907             break;
7908             case "next":
7909                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7910             break;
7911             case "last":
7912                 var total = ds.getTotalCount();
7913                 var extra = total % this.pageSize;
7914                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7915                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7916             break;
7917             case "refresh":
7918                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7919             break;
7920         }
7921     },
7922
7923     /**
7924      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7925      * @param {Roo.data.Store} store The data store to unbind
7926      */
7927     unbind : function(ds){
7928         ds.un("beforeload", this.beforeLoad, this);
7929         ds.un("load", this.onLoad, this);
7930         ds.un("loadexception", this.onLoadError, this);
7931         ds.un("remove", this.updateInfo, this);
7932         ds.un("add", this.updateInfo, this);
7933         this.ds = undefined;
7934     },
7935
7936     /**
7937      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7938      * @param {Roo.data.Store} store The data store to bind
7939      */
7940     bind : function(ds){
7941         ds.on("beforeload", this.beforeLoad, this);
7942         ds.on("load", this.onLoad, this);
7943         ds.on("loadexception", this.onLoadError, this);
7944         ds.on("remove", this.updateInfo, this);
7945         ds.on("add", this.updateInfo, this);
7946         this.ds = ds;
7947     }
7948 });/*
7949  * Based on:
7950  * Ext JS Library 1.1.1
7951  * Copyright(c) 2006-2007, Ext JS, LLC.
7952  *
7953  * Originally Released Under LGPL - original licence link has changed is not relivant.
7954  *
7955  * Fork - LGPL
7956  * <script type="text/javascript">
7957  */
7958
7959 /**
7960  * @class Roo.Resizable
7961  * @extends Roo.util.Observable
7962  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7963  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7964  * 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
7965  * the element will be wrapped for you automatically.</p>
7966  * <p>Here is the list of valid resize handles:</p>
7967  * <pre>
7968 Value   Description
7969 ------  -------------------
7970  'n'     north
7971  's'     south
7972  'e'     east
7973  'w'     west
7974  'nw'    northwest
7975  'sw'    southwest
7976  'se'    southeast
7977  'ne'    northeast
7978  'hd'    horizontal drag
7979  'all'   all
7980 </pre>
7981  * <p>Here's an example showing the creation of a typical Resizable:</p>
7982  * <pre><code>
7983 var resizer = new Roo.Resizable("element-id", {
7984     handles: 'all',
7985     minWidth: 200,
7986     minHeight: 100,
7987     maxWidth: 500,
7988     maxHeight: 400,
7989     pinned: true
7990 });
7991 resizer.on("resize", myHandler);
7992 </code></pre>
7993  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7994  * resizer.east.setDisplayed(false);</p>
7995  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7996  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7997  * resize operation's new size (defaults to [0, 0])
7998  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
7999  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8000  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8001  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8002  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8003  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8004  * @cfg {Number} width The width of the element in pixels (defaults to null)
8005  * @cfg {Number} height The height of the element in pixels (defaults to null)
8006  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8007  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8008  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8009  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8010  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8011  * in favor of the handles config option (defaults to false)
8012  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8013  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8014  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8015  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8016  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8017  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8018  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8019  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8020  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8021  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8022  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8023  * @constructor
8024  * Create a new resizable component
8025  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8026  * @param {Object} config configuration options
8027   */
8028 Roo.Resizable = function(el, config)
8029 {
8030     this.el = Roo.get(el);
8031
8032     if(config && config.wrap){
8033         config.resizeChild = this.el;
8034         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8035         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8036         this.el.setStyle("overflow", "hidden");
8037         this.el.setPositioning(config.resizeChild.getPositioning());
8038         config.resizeChild.clearPositioning();
8039         if(!config.width || !config.height){
8040             var csize = config.resizeChild.getSize();
8041             this.el.setSize(csize.width, csize.height);
8042         }
8043         if(config.pinned && !config.adjustments){
8044             config.adjustments = "auto";
8045         }
8046     }
8047
8048     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8049     this.proxy.unselectable();
8050     this.proxy.enableDisplayMode('block');
8051
8052     Roo.apply(this, config);
8053
8054     if(this.pinned){
8055         this.disableTrackOver = true;
8056         this.el.addClass("x-resizable-pinned");
8057     }
8058     // if the element isn't positioned, make it relative
8059     var position = this.el.getStyle("position");
8060     if(position != "absolute" && position != "fixed"){
8061         this.el.setStyle("position", "relative");
8062     }
8063     if(!this.handles){ // no handles passed, must be legacy style
8064         this.handles = 's,e,se';
8065         if(this.multiDirectional){
8066             this.handles += ',n,w';
8067         }
8068     }
8069     if(this.handles == "all"){
8070         this.handles = "n s e w ne nw se sw";
8071     }
8072     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8073     var ps = Roo.Resizable.positions;
8074     for(var i = 0, len = hs.length; i < len; i++){
8075         if(hs[i] && ps[hs[i]]){
8076             var pos = ps[hs[i]];
8077             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8078         }
8079     }
8080     // legacy
8081     this.corner = this.southeast;
8082     
8083     // updateBox = the box can move..
8084     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8085         this.updateBox = true;
8086     }
8087
8088     this.activeHandle = null;
8089
8090     if(this.resizeChild){
8091         if(typeof this.resizeChild == "boolean"){
8092             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8093         }else{
8094             this.resizeChild = Roo.get(this.resizeChild, true);
8095         }
8096     }
8097     
8098     if(this.adjustments == "auto"){
8099         var rc = this.resizeChild;
8100         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8101         if(rc && (hw || hn)){
8102             rc.position("relative");
8103             rc.setLeft(hw ? hw.el.getWidth() : 0);
8104             rc.setTop(hn ? hn.el.getHeight() : 0);
8105         }
8106         this.adjustments = [
8107             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8108             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8109         ];
8110     }
8111
8112     if(this.draggable){
8113         this.dd = this.dynamic ?
8114             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8115         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8116     }
8117
8118     // public events
8119     this.addEvents({
8120         /**
8121          * @event beforeresize
8122          * Fired before resize is allowed. Set enabled to false to cancel resize.
8123          * @param {Roo.Resizable} this
8124          * @param {Roo.EventObject} e The mousedown event
8125          */
8126         "beforeresize" : true,
8127         /**
8128          * @event resizing
8129          * Fired a resizing.
8130          * @param {Roo.Resizable} this
8131          * @param {Number} x The new x position
8132          * @param {Number} y The new y position
8133          * @param {Number} w The new w width
8134          * @param {Number} h The new h hight
8135          * @param {Roo.EventObject} e The mouseup event
8136          */
8137         "resizing" : true,
8138         /**
8139          * @event resize
8140          * Fired after a resize.
8141          * @param {Roo.Resizable} this
8142          * @param {Number} width The new width
8143          * @param {Number} height The new height
8144          * @param {Roo.EventObject} e The mouseup event
8145          */
8146         "resize" : true
8147     });
8148
8149     if(this.width !== null && this.height !== null){
8150         this.resizeTo(this.width, this.height);
8151     }else{
8152         this.updateChildSize();
8153     }
8154     if(Roo.isIE){
8155         this.el.dom.style.zoom = 1;
8156     }
8157     Roo.Resizable.superclass.constructor.call(this);
8158 };
8159
8160 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8161         resizeChild : false,
8162         adjustments : [0, 0],
8163         minWidth : 5,
8164         minHeight : 5,
8165         maxWidth : 10000,
8166         maxHeight : 10000,
8167         enabled : true,
8168         animate : false,
8169         duration : .35,
8170         dynamic : false,
8171         handles : false,
8172         multiDirectional : false,
8173         disableTrackOver : false,
8174         easing : 'easeOutStrong',
8175         widthIncrement : 0,
8176         heightIncrement : 0,
8177         pinned : false,
8178         width : null,
8179         height : null,
8180         preserveRatio : false,
8181         transparent: false,
8182         minX: 0,
8183         minY: 0,
8184         draggable: false,
8185
8186         /**
8187          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8188          */
8189         constrainTo: undefined,
8190         /**
8191          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8192          */
8193         resizeRegion: undefined,
8194
8195
8196     /**
8197      * Perform a manual resize
8198      * @param {Number} width
8199      * @param {Number} height
8200      */
8201     resizeTo : function(width, height){
8202         this.el.setSize(width, height);
8203         this.updateChildSize();
8204         this.fireEvent("resize", this, width, height, null);
8205     },
8206
8207     // private
8208     startSizing : function(e, handle){
8209         this.fireEvent("beforeresize", this, e);
8210         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8211
8212             if(!this.overlay){
8213                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8214                 this.overlay.unselectable();
8215                 this.overlay.enableDisplayMode("block");
8216                 this.overlay.on("mousemove", this.onMouseMove, this);
8217                 this.overlay.on("mouseup", this.onMouseUp, this);
8218             }
8219             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8220
8221             this.resizing = true;
8222             this.startBox = this.el.getBox();
8223             this.startPoint = e.getXY();
8224             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8225                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8226
8227             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8228             this.overlay.show();
8229
8230             if(this.constrainTo) {
8231                 var ct = Roo.get(this.constrainTo);
8232                 this.resizeRegion = ct.getRegion().adjust(
8233                     ct.getFrameWidth('t'),
8234                     ct.getFrameWidth('l'),
8235                     -ct.getFrameWidth('b'),
8236                     -ct.getFrameWidth('r')
8237                 );
8238             }
8239
8240             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8241             this.proxy.show();
8242             this.proxy.setBox(this.startBox);
8243             if(!this.dynamic){
8244                 this.proxy.setStyle('visibility', 'visible');
8245             }
8246         }
8247     },
8248
8249     // private
8250     onMouseDown : function(handle, e){
8251         if(this.enabled){
8252             e.stopEvent();
8253             this.activeHandle = handle;
8254             this.startSizing(e, handle);
8255         }
8256     },
8257
8258     // private
8259     onMouseUp : function(e){
8260         var size = this.resizeElement();
8261         this.resizing = false;
8262         this.handleOut();
8263         this.overlay.hide();
8264         this.proxy.hide();
8265         this.fireEvent("resize", this, size.width, size.height, e);
8266     },
8267
8268     // private
8269     updateChildSize : function(){
8270         
8271         if(this.resizeChild){
8272             var el = this.el;
8273             var child = this.resizeChild;
8274             var adj = this.adjustments;
8275             if(el.dom.offsetWidth){
8276                 var b = el.getSize(true);
8277                 child.setSize(b.width+adj[0], b.height+adj[1]);
8278             }
8279             // Second call here for IE
8280             // The first call enables instant resizing and
8281             // the second call corrects scroll bars if they
8282             // exist
8283             if(Roo.isIE){
8284                 setTimeout(function(){
8285                     if(el.dom.offsetWidth){
8286                         var b = el.getSize(true);
8287                         child.setSize(b.width+adj[0], b.height+adj[1]);
8288                     }
8289                 }, 10);
8290             }
8291         }
8292     },
8293
8294     // private
8295     snap : function(value, inc, min){
8296         if(!inc || !value) {
8297             return value;
8298         }
8299         var newValue = value;
8300         var m = value % inc;
8301         if(m > 0){
8302             if(m > (inc/2)){
8303                 newValue = value + (inc-m);
8304             }else{
8305                 newValue = value - m;
8306             }
8307         }
8308         return Math.max(min, newValue);
8309     },
8310
8311     // private
8312     resizeElement : function(){
8313         var box = this.proxy.getBox();
8314         if(this.updateBox){
8315             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8316         }else{
8317             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8318         }
8319         this.updateChildSize();
8320         if(!this.dynamic){
8321             this.proxy.hide();
8322         }
8323         return box;
8324     },
8325
8326     // private
8327     constrain : function(v, diff, m, mx){
8328         if(v - diff < m){
8329             diff = v - m;
8330         }else if(v - diff > mx){
8331             diff = mx - v;
8332         }
8333         return diff;
8334     },
8335
8336     // private
8337     onMouseMove : function(e){
8338         
8339         if(this.enabled){
8340             try{// try catch so if something goes wrong the user doesn't get hung
8341
8342             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8343                 return;
8344             }
8345
8346             //var curXY = this.startPoint;
8347             var curSize = this.curSize || this.startBox;
8348             var x = this.startBox.x, y = this.startBox.y;
8349             var ox = x, oy = y;
8350             var w = curSize.width, h = curSize.height;
8351             var ow = w, oh = h;
8352             var mw = this.minWidth, mh = this.minHeight;
8353             var mxw = this.maxWidth, mxh = this.maxHeight;
8354             var wi = this.widthIncrement;
8355             var hi = this.heightIncrement;
8356
8357             var eventXY = e.getXY();
8358             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8359             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8360
8361             var pos = this.activeHandle.position;
8362
8363             switch(pos){
8364                 case "east":
8365                     w += diffX;
8366                     w = Math.min(Math.max(mw, w), mxw);
8367                     break;
8368              
8369                 case "south":
8370                     h += diffY;
8371                     h = Math.min(Math.max(mh, h), mxh);
8372                     break;
8373                 case "southeast":
8374                     w += diffX;
8375                     h += diffY;
8376                     w = Math.min(Math.max(mw, w), mxw);
8377                     h = Math.min(Math.max(mh, h), mxh);
8378                     break;
8379                 case "north":
8380                     diffY = this.constrain(h, diffY, mh, mxh);
8381                     y += diffY;
8382                     h -= diffY;
8383                     break;
8384                 case "hdrag":
8385                     
8386                     if (wi) {
8387                         var adiffX = Math.abs(diffX);
8388                         var sub = (adiffX % wi); // how much 
8389                         if (sub > (wi/2)) { // far enough to snap
8390                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8391                         } else {
8392                             // remove difference.. 
8393                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8394                         }
8395                     }
8396                     x += diffX;
8397                     x = Math.max(this.minX, x);
8398                     break;
8399                 case "west":
8400                     diffX = this.constrain(w, diffX, mw, mxw);
8401                     x += diffX;
8402                     w -= diffX;
8403                     break;
8404                 case "northeast":
8405                     w += diffX;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     diffY = this.constrain(h, diffY, mh, mxh);
8408                     y += diffY;
8409                     h -= diffY;
8410                     break;
8411                 case "northwest":
8412                     diffX = this.constrain(w, diffX, mw, mxw);
8413                     diffY = this.constrain(h, diffY, mh, mxh);
8414                     y += diffY;
8415                     h -= diffY;
8416                     x += diffX;
8417                     w -= diffX;
8418                     break;
8419                case "southwest":
8420                     diffX = this.constrain(w, diffX, mw, mxw);
8421                     h += diffY;
8422                     h = Math.min(Math.max(mh, h), mxh);
8423                     x += diffX;
8424                     w -= diffX;
8425                     break;
8426             }
8427
8428             var sw = this.snap(w, wi, mw);
8429             var sh = this.snap(h, hi, mh);
8430             if(sw != w || sh != h){
8431                 switch(pos){
8432                     case "northeast":
8433                         y -= sh - h;
8434                     break;
8435                     case "north":
8436                         y -= sh - h;
8437                         break;
8438                     case "southwest":
8439                         x -= sw - w;
8440                     break;
8441                     case "west":
8442                         x -= sw - w;
8443                         break;
8444                     case "northwest":
8445                         x -= sw - w;
8446                         y -= sh - h;
8447                     break;
8448                 }
8449                 w = sw;
8450                 h = sh;
8451             }
8452
8453             if(this.preserveRatio){
8454                 switch(pos){
8455                     case "southeast":
8456                     case "east":
8457                         h = oh * (w/ow);
8458                         h = Math.min(Math.max(mh, h), mxh);
8459                         w = ow * (h/oh);
8460                        break;
8461                     case "south":
8462                         w = ow * (h/oh);
8463                         w = Math.min(Math.max(mw, w), mxw);
8464                         h = oh * (w/ow);
8465                         break;
8466                     case "northeast":
8467                         w = ow * (h/oh);
8468                         w = Math.min(Math.max(mw, w), mxw);
8469                         h = oh * (w/ow);
8470                     break;
8471                     case "north":
8472                         var tw = w;
8473                         w = ow * (h/oh);
8474                         w = Math.min(Math.max(mw, w), mxw);
8475                         h = oh * (w/ow);
8476                         x += (tw - w) / 2;
8477                         break;
8478                     case "southwest":
8479                         h = oh * (w/ow);
8480                         h = Math.min(Math.max(mh, h), mxh);
8481                         var tw = w;
8482                         w = ow * (h/oh);
8483                         x += tw - w;
8484                         break;
8485                     case "west":
8486                         var th = h;
8487                         h = oh * (w/ow);
8488                         h = Math.min(Math.max(mh, h), mxh);
8489                         y += (th - h) / 2;
8490                         var tw = w;
8491                         w = ow * (h/oh);
8492                         x += tw - w;
8493                        break;
8494                     case "northwest":
8495                         var tw = w;
8496                         var th = h;
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         w = ow * (h/oh);
8500                         y += th - h;
8501                         x += tw - w;
8502                        break;
8503
8504                 }
8505             }
8506             if (pos == 'hdrag') {
8507                 w = ow;
8508             }
8509             this.proxy.setBounds(x, y, w, h);
8510             if(this.dynamic){
8511                 this.resizeElement();
8512             }
8513             }catch(e){}
8514         }
8515         this.fireEvent("resizing", this, x, y, w, h, e);
8516     },
8517
8518     // private
8519     handleOver : function(){
8520         if(this.enabled){
8521             this.el.addClass("x-resizable-over");
8522         }
8523     },
8524
8525     // private
8526     handleOut : function(){
8527         if(!this.resizing){
8528             this.el.removeClass("x-resizable-over");
8529         }
8530     },
8531
8532     /**
8533      * Returns the element this component is bound to.
8534      * @return {Roo.Element}
8535      */
8536     getEl : function(){
8537         return this.el;
8538     },
8539
8540     /**
8541      * Returns the resizeChild element (or null).
8542      * @return {Roo.Element}
8543      */
8544     getResizeChild : function(){
8545         return this.resizeChild;
8546     },
8547     groupHandler : function()
8548     {
8549         
8550     },
8551     /**
8552      * Destroys this resizable. If the element was wrapped and
8553      * removeEl is not true then the element remains.
8554      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8555      */
8556     destroy : function(removeEl){
8557         this.proxy.remove();
8558         if(this.overlay){
8559             this.overlay.removeAllListeners();
8560             this.overlay.remove();
8561         }
8562         var ps = Roo.Resizable.positions;
8563         for(var k in ps){
8564             if(typeof ps[k] != "function" && this[ps[k]]){
8565                 var h = this[ps[k]];
8566                 h.el.removeAllListeners();
8567                 h.el.remove();
8568             }
8569         }
8570         if(removeEl){
8571             this.el.update("");
8572             this.el.remove();
8573         }
8574     }
8575 });
8576
8577 // private
8578 // hash to map config positions to true positions
8579 Roo.Resizable.positions = {
8580     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8581     hd: "hdrag"
8582 };
8583
8584 // private
8585 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8586     if(!this.tpl){
8587         // only initialize the template if resizable is used
8588         var tpl = Roo.DomHelper.createTemplate(
8589             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8590         );
8591         tpl.compile();
8592         Roo.Resizable.Handle.prototype.tpl = tpl;
8593     }
8594     this.position = pos;
8595     this.rz = rz;
8596     // show north drag fro topdra
8597     var handlepos = pos == 'hdrag' ? 'north' : pos;
8598     
8599     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8600     if (pos == 'hdrag') {
8601         this.el.setStyle('cursor', 'pointer');
8602     }
8603     this.el.unselectable();
8604     if(transparent){
8605         this.el.setOpacity(0);
8606     }
8607     this.el.on("mousedown", this.onMouseDown, this);
8608     if(!disableTrackOver){
8609         this.el.on("mouseover", this.onMouseOver, this);
8610         this.el.on("mouseout", this.onMouseOut, this);
8611     }
8612 };
8613
8614 // private
8615 Roo.Resizable.Handle.prototype = {
8616     afterResize : function(rz){
8617         Roo.log('after?');
8618         // do nothing
8619     },
8620     // private
8621     onMouseDown : function(e){
8622         this.rz.onMouseDown(this, e);
8623     },
8624     // private
8625     onMouseOver : function(e){
8626         this.rz.handleOver(this, e);
8627     },
8628     // private
8629     onMouseOut : function(e){
8630         this.rz.handleOut(this, e);
8631     }
8632 };/*
8633  * Based on:
8634  * Ext JS Library 1.1.1
8635  * Copyright(c) 2006-2007, Ext JS, LLC.
8636  *
8637  * Originally Released Under LGPL - original licence link has changed is not relivant.
8638  *
8639  * Fork - LGPL
8640  * <script type="text/javascript">
8641  */
8642
8643 /**
8644  * @class Roo.Editor
8645  * @extends Roo.Component
8646  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8647  * @constructor
8648  * Create a new Editor
8649  * @param {Roo.form.Field} field The Field object (or descendant)
8650  * @param {Object} config The config object
8651  */
8652 Roo.Editor = function(field, config){
8653     Roo.Editor.superclass.constructor.call(this, config);
8654     this.field = field;
8655     this.addEvents({
8656         /**
8657              * @event beforestartedit
8658              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8659              * false from the handler of this event.
8660              * @param {Editor} this
8661              * @param {Roo.Element} boundEl The underlying element bound to this editor
8662              * @param {Mixed} value The field value being set
8663              */
8664         "beforestartedit" : true,
8665         /**
8666              * @event startedit
8667              * Fires when this editor is displayed
8668              * @param {Roo.Element} boundEl The underlying element bound to this editor
8669              * @param {Mixed} value The starting field value
8670              */
8671         "startedit" : true,
8672         /**
8673              * @event beforecomplete
8674              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8675              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8676              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8677              * event will not fire since no edit actually occurred.
8678              * @param {Editor} this
8679              * @param {Mixed} value The current field value
8680              * @param {Mixed} startValue The original field value
8681              */
8682         "beforecomplete" : true,
8683         /**
8684              * @event complete
8685              * Fires after editing is complete and any changed value has been written to the underlying field.
8686              * @param {Editor} this
8687              * @param {Mixed} value The current field value
8688              * @param {Mixed} startValue The original field value
8689              */
8690         "complete" : true,
8691         /**
8692          * @event specialkey
8693          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8694          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8695          * @param {Roo.form.Field} this
8696          * @param {Roo.EventObject} e The event object
8697          */
8698         "specialkey" : true
8699     });
8700 };
8701
8702 Roo.extend(Roo.Editor, Roo.Component, {
8703     /**
8704      * @cfg {Boolean/String} autosize
8705      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8706      * or "height" to adopt the height only (defaults to false)
8707      */
8708     /**
8709      * @cfg {Boolean} revertInvalid
8710      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8711      * validation fails (defaults to true)
8712      */
8713     /**
8714      * @cfg {Boolean} ignoreNoChange
8715      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8716      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8717      * will never be ignored.
8718      */
8719     /**
8720      * @cfg {Boolean} hideEl
8721      * False to keep the bound element visible while the editor is displayed (defaults to true)
8722      */
8723     /**
8724      * @cfg {Mixed} value
8725      * The data value of the underlying field (defaults to "")
8726      */
8727     value : "",
8728     /**
8729      * @cfg {String} alignment
8730      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8731      */
8732     alignment: "c-c?",
8733     /**
8734      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8735      * for bottom-right shadow (defaults to "frame")
8736      */
8737     shadow : "frame",
8738     /**
8739      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8740      */
8741     constrain : false,
8742     /**
8743      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8744      */
8745     completeOnEnter : false,
8746     /**
8747      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8748      */
8749     cancelOnEsc : false,
8750     /**
8751      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8752      */
8753     updateEl : false,
8754
8755     // private
8756     onRender : function(ct, position){
8757         this.el = new Roo.Layer({
8758             shadow: this.shadow,
8759             cls: "x-editor",
8760             parentEl : ct,
8761             shim : this.shim,
8762             shadowOffset:4,
8763             id: this.id,
8764             constrain: this.constrain
8765         });
8766         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8767         if(this.field.msgTarget != 'title'){
8768             this.field.msgTarget = 'qtip';
8769         }
8770         this.field.render(this.el);
8771         if(Roo.isGecko){
8772             this.field.el.dom.setAttribute('autocomplete', 'off');
8773         }
8774         this.field.on("specialkey", this.onSpecialKey, this);
8775         if(this.swallowKeys){
8776             this.field.el.swallowEvent(['keydown','keypress']);
8777         }
8778         this.field.show();
8779         this.field.on("blur", this.onBlur, this);
8780         if(this.field.grow){
8781             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8782         }
8783     },
8784
8785     onSpecialKey : function(field, e)
8786     {
8787         //Roo.log('editor onSpecialKey');
8788         if(this.completeOnEnter && e.getKey() == e.ENTER){
8789             e.stopEvent();
8790             this.completeEdit();
8791             return;
8792         }
8793         // do not fire special key otherwise it might hide close the editor...
8794         if(e.getKey() == e.ENTER){    
8795             return;
8796         }
8797         if(this.cancelOnEsc && e.getKey() == e.ESC){
8798             this.cancelEdit();
8799             return;
8800         } 
8801         this.fireEvent('specialkey', field, e);
8802     
8803     },
8804
8805     /**
8806      * Starts the editing process and shows the editor.
8807      * @param {String/HTMLElement/Element} el The element to edit
8808      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8809       * to the innerHTML of el.
8810      */
8811     startEdit : function(el, value){
8812         if(this.editing){
8813             this.completeEdit();
8814         }
8815         this.boundEl = Roo.get(el);
8816         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8817         if(!this.rendered){
8818             this.render(this.parentEl || document.body);
8819         }
8820         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8821             return;
8822         }
8823         this.startValue = v;
8824         this.field.setValue(v);
8825         if(this.autoSize){
8826             var sz = this.boundEl.getSize();
8827             switch(this.autoSize){
8828                 case "width":
8829                 this.setSize(sz.width,  "");
8830                 break;
8831                 case "height":
8832                 this.setSize("",  sz.height);
8833                 break;
8834                 default:
8835                 this.setSize(sz.width,  sz.height);
8836             }
8837         }
8838         this.el.alignTo(this.boundEl, this.alignment);
8839         this.editing = true;
8840         if(Roo.QuickTips){
8841             Roo.QuickTips.disable();
8842         }
8843         this.show();
8844     },
8845
8846     /**
8847      * Sets the height and width of this editor.
8848      * @param {Number} width The new width
8849      * @param {Number} height The new height
8850      */
8851     setSize : function(w, h){
8852         this.field.setSize(w, h);
8853         if(this.el){
8854             this.el.sync();
8855         }
8856     },
8857
8858     /**
8859      * Realigns the editor to the bound field based on the current alignment config value.
8860      */
8861     realign : function(){
8862         this.el.alignTo(this.boundEl, this.alignment);
8863     },
8864
8865     /**
8866      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8867      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8868      */
8869     completeEdit : function(remainVisible){
8870         if(!this.editing){
8871             return;
8872         }
8873         var v = this.getValue();
8874         if(this.revertInvalid !== false && !this.field.isValid()){
8875             v = this.startValue;
8876             this.cancelEdit(true);
8877         }
8878         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8879             this.editing = false;
8880             this.hide();
8881             return;
8882         }
8883         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8884             this.editing = false;
8885             if(this.updateEl && this.boundEl){
8886                 this.boundEl.update(v);
8887             }
8888             if(remainVisible !== true){
8889                 this.hide();
8890             }
8891             this.fireEvent("complete", this, v, this.startValue);
8892         }
8893     },
8894
8895     // private
8896     onShow : function(){
8897         this.el.show();
8898         if(this.hideEl !== false){
8899             this.boundEl.hide();
8900         }
8901         this.field.show();
8902         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8903             this.fixIEFocus = true;
8904             this.deferredFocus.defer(50, this);
8905         }else{
8906             this.field.focus();
8907         }
8908         this.fireEvent("startedit", this.boundEl, this.startValue);
8909     },
8910
8911     deferredFocus : function(){
8912         if(this.editing){
8913             this.field.focus();
8914         }
8915     },
8916
8917     /**
8918      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8919      * reverted to the original starting value.
8920      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8921      * cancel (defaults to false)
8922      */
8923     cancelEdit : function(remainVisible){
8924         if(this.editing){
8925             this.setValue(this.startValue);
8926             if(remainVisible !== true){
8927                 this.hide();
8928             }
8929         }
8930     },
8931
8932     // private
8933     onBlur : function(){
8934         if(this.allowBlur !== true && this.editing){
8935             this.completeEdit();
8936         }
8937     },
8938
8939     // private
8940     onHide : function(){
8941         if(this.editing){
8942             this.completeEdit();
8943             return;
8944         }
8945         this.field.blur();
8946         if(this.field.collapse){
8947             this.field.collapse();
8948         }
8949         this.el.hide();
8950         if(this.hideEl !== false){
8951             this.boundEl.show();
8952         }
8953         if(Roo.QuickTips){
8954             Roo.QuickTips.enable();
8955         }
8956     },
8957
8958     /**
8959      * Sets the data value of the editor
8960      * @param {Mixed} value Any valid value supported by the underlying field
8961      */
8962     setValue : function(v){
8963         this.field.setValue(v);
8964     },
8965
8966     /**
8967      * Gets the data value of the editor
8968      * @return {Mixed} The data value
8969      */
8970     getValue : function(){
8971         return this.field.getValue();
8972     }
8973 });/*
8974  * Based on:
8975  * Ext JS Library 1.1.1
8976  * Copyright(c) 2006-2007, Ext JS, LLC.
8977  *
8978  * Originally Released Under LGPL - original licence link has changed is not relivant.
8979  *
8980  * Fork - LGPL
8981  * <script type="text/javascript">
8982  */
8983  
8984 /**
8985  * @class Roo.BasicDialog
8986  * @extends Roo.util.Observable
8987  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8988  * <pre><code>
8989 var dlg = new Roo.BasicDialog("my-dlg", {
8990     height: 200,
8991     width: 300,
8992     minHeight: 100,
8993     minWidth: 150,
8994     modal: true,
8995     proxyDrag: true,
8996     shadow: true
8997 });
8998 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
8999 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9000 dlg.addButton('Cancel', dlg.hide, dlg);
9001 dlg.show();
9002 </code></pre>
9003   <b>A Dialog should always be a direct child of the body element.</b>
9004  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9005  * @cfg {String} title Default text to display in the title bar (defaults to null)
9006  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9007  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9008  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9009  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9010  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9011  * (defaults to null with no animation)
9012  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9013  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9014  * property for valid values (defaults to 'all')
9015  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9016  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9017  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9018  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9019  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9020  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9021  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9022  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9023  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9024  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9025  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9026  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9027  * draggable = true (defaults to false)
9028  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9029  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9030  * shadow (defaults to false)
9031  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9032  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9033  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9034  * @cfg {Array} buttons Array of buttons
9035  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9036  * @constructor
9037  * Create a new BasicDialog.
9038  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9039  * @param {Object} config Configuration options
9040  */
9041 Roo.BasicDialog = function(el, config){
9042     this.el = Roo.get(el);
9043     var dh = Roo.DomHelper;
9044     if(!this.el && config && config.autoCreate){
9045         if(typeof config.autoCreate == "object"){
9046             if(!config.autoCreate.id){
9047                 config.autoCreate.id = el;
9048             }
9049             this.el = dh.append(document.body,
9050                         config.autoCreate, true);
9051         }else{
9052             this.el = dh.append(document.body,
9053                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9054         }
9055     }
9056     el = this.el;
9057     el.setDisplayed(true);
9058     el.hide = this.hideAction;
9059     this.id = el.id;
9060     el.addClass("x-dlg");
9061
9062     Roo.apply(this, config);
9063
9064     this.proxy = el.createProxy("x-dlg-proxy");
9065     this.proxy.hide = this.hideAction;
9066     this.proxy.setOpacity(.5);
9067     this.proxy.hide();
9068
9069     if(config.width){
9070         el.setWidth(config.width);
9071     }
9072     if(config.height){
9073         el.setHeight(config.height);
9074     }
9075     this.size = el.getSize();
9076     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9077         this.xy = [config.x,config.y];
9078     }else{
9079         this.xy = el.getCenterXY(true);
9080     }
9081     /** The header element @type Roo.Element */
9082     this.header = el.child("> .x-dlg-hd");
9083     /** The body element @type Roo.Element */
9084     this.body = el.child("> .x-dlg-bd");
9085     /** The footer element @type Roo.Element */
9086     this.footer = el.child("> .x-dlg-ft");
9087
9088     if(!this.header){
9089         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9090     }
9091     if(!this.body){
9092         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9093     }
9094
9095     this.header.unselectable();
9096     if(this.title){
9097         this.header.update(this.title);
9098     }
9099     // this element allows the dialog to be focused for keyboard event
9100     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9101     this.focusEl.swallowEvent("click", true);
9102
9103     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9104
9105     // wrap the body and footer for special rendering
9106     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9107     if(this.footer){
9108         this.bwrap.dom.appendChild(this.footer.dom);
9109     }
9110
9111     this.bg = this.el.createChild({
9112         tag: "div", cls:"x-dlg-bg",
9113         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9114     });
9115     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9116
9117
9118     if(this.autoScroll !== false && !this.autoTabs){
9119         this.body.setStyle("overflow", "auto");
9120     }
9121
9122     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9123
9124     if(this.closable !== false){
9125         this.el.addClass("x-dlg-closable");
9126         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9127         this.close.on("click", this.closeClick, this);
9128         this.close.addClassOnOver("x-dlg-close-over");
9129     }
9130     if(this.collapsible !== false){
9131         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9132         this.collapseBtn.on("click", this.collapseClick, this);
9133         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9134         this.header.on("dblclick", this.collapseClick, this);
9135     }
9136     if(this.resizable !== false){
9137         this.el.addClass("x-dlg-resizable");
9138         this.resizer = new Roo.Resizable(el, {
9139             minWidth: this.minWidth || 80,
9140             minHeight:this.minHeight || 80,
9141             handles: this.resizeHandles || "all",
9142             pinned: true
9143         });
9144         this.resizer.on("beforeresize", this.beforeResize, this);
9145         this.resizer.on("resize", this.onResize, this);
9146     }
9147     if(this.draggable !== false){
9148         el.addClass("x-dlg-draggable");
9149         if (!this.proxyDrag) {
9150             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9151         }
9152         else {
9153             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9154         }
9155         dd.setHandleElId(this.header.id);
9156         dd.endDrag = this.endMove.createDelegate(this);
9157         dd.startDrag = this.startMove.createDelegate(this);
9158         dd.onDrag = this.onDrag.createDelegate(this);
9159         dd.scroll = false;
9160         this.dd = dd;
9161     }
9162     if(this.modal){
9163         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9164         this.mask.enableDisplayMode("block");
9165         this.mask.hide();
9166         this.el.addClass("x-dlg-modal");
9167     }
9168     if(this.shadow){
9169         this.shadow = new Roo.Shadow({
9170             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9171             offset : this.shadowOffset
9172         });
9173     }else{
9174         this.shadowOffset = 0;
9175     }
9176     if(Roo.useShims && this.shim !== false){
9177         this.shim = this.el.createShim();
9178         this.shim.hide = this.hideAction;
9179         this.shim.hide();
9180     }else{
9181         this.shim = false;
9182     }
9183     if(this.autoTabs){
9184         this.initTabs();
9185     }
9186     if (this.buttons) { 
9187         var bts= this.buttons;
9188         this.buttons = [];
9189         Roo.each(bts, function(b) {
9190             this.addButton(b);
9191         }, this);
9192     }
9193     
9194     
9195     this.addEvents({
9196         /**
9197          * @event keydown
9198          * Fires when a key is pressed
9199          * @param {Roo.BasicDialog} this
9200          * @param {Roo.EventObject} e
9201          */
9202         "keydown" : true,
9203         /**
9204          * @event move
9205          * Fires when this dialog is moved by the user.
9206          * @param {Roo.BasicDialog} this
9207          * @param {Number} x The new page X
9208          * @param {Number} y The new page Y
9209          */
9210         "move" : true,
9211         /**
9212          * @event resize
9213          * Fires when this dialog is resized by the user.
9214          * @param {Roo.BasicDialog} this
9215          * @param {Number} width The new width
9216          * @param {Number} height The new height
9217          */
9218         "resize" : true,
9219         /**
9220          * @event beforehide
9221          * Fires before this dialog is hidden.
9222          * @param {Roo.BasicDialog} this
9223          */
9224         "beforehide" : true,
9225         /**
9226          * @event hide
9227          * Fires when this dialog is hidden.
9228          * @param {Roo.BasicDialog} this
9229          */
9230         "hide" : true,
9231         /**
9232          * @event beforeshow
9233          * Fires before this dialog is shown.
9234          * @param {Roo.BasicDialog} this
9235          */
9236         "beforeshow" : true,
9237         /**
9238          * @event show
9239          * Fires when this dialog is shown.
9240          * @param {Roo.BasicDialog} this
9241          */
9242         "show" : true
9243     });
9244     el.on("keydown", this.onKeyDown, this);
9245     el.on("mousedown", this.toFront, this);
9246     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9247     this.el.hide();
9248     Roo.DialogManager.register(this);
9249     Roo.BasicDialog.superclass.constructor.call(this);
9250 };
9251
9252 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9253     shadowOffset: Roo.isIE ? 6 : 5,
9254     minHeight: 80,
9255     minWidth: 200,
9256     minButtonWidth: 75,
9257     defaultButton: null,
9258     buttonAlign: "right",
9259     tabTag: 'div',
9260     firstShow: true,
9261
9262     /**
9263      * Sets the dialog title text
9264      * @param {String} text The title text to display
9265      * @return {Roo.BasicDialog} this
9266      */
9267     setTitle : function(text){
9268         this.header.update(text);
9269         return this;
9270     },
9271
9272     // private
9273     closeClick : function(){
9274         this.hide();
9275     },
9276
9277     // private
9278     collapseClick : function(){
9279         this[this.collapsed ? "expand" : "collapse"]();
9280     },
9281
9282     /**
9283      * Collapses the dialog to its minimized state (only the title bar is visible).
9284      * Equivalent to the user clicking the collapse dialog button.
9285      */
9286     collapse : function(){
9287         if(!this.collapsed){
9288             this.collapsed = true;
9289             this.el.addClass("x-dlg-collapsed");
9290             this.restoreHeight = this.el.getHeight();
9291             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9292         }
9293     },
9294
9295     /**
9296      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9297      * clicking the expand dialog button.
9298      */
9299     expand : function(){
9300         if(this.collapsed){
9301             this.collapsed = false;
9302             this.el.removeClass("x-dlg-collapsed");
9303             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9304         }
9305     },
9306
9307     /**
9308      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9309      * @return {Roo.TabPanel} The tabs component
9310      */
9311     initTabs : function(){
9312         var tabs = this.getTabs();
9313         while(tabs.getTab(0)){
9314             tabs.removeTab(0);
9315         }
9316         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9317             var dom = el.dom;
9318             tabs.addTab(Roo.id(dom), dom.title);
9319             dom.title = "";
9320         });
9321         tabs.activate(0);
9322         return tabs;
9323     },
9324
9325     // private
9326     beforeResize : function(){
9327         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9328     },
9329
9330     // private
9331     onResize : function(){
9332         this.refreshSize();
9333         this.syncBodyHeight();
9334         this.adjustAssets();
9335         this.focus();
9336         this.fireEvent("resize", this, this.size.width, this.size.height);
9337     },
9338
9339     // private
9340     onKeyDown : function(e){
9341         if(this.isVisible()){
9342             this.fireEvent("keydown", this, e);
9343         }
9344     },
9345
9346     /**
9347      * Resizes the dialog.
9348      * @param {Number} width
9349      * @param {Number} height
9350      * @return {Roo.BasicDialog} this
9351      */
9352     resizeTo : function(width, height){
9353         this.el.setSize(width, height);
9354         this.size = {width: width, height: height};
9355         this.syncBodyHeight();
9356         if(this.fixedcenter){
9357             this.center();
9358         }
9359         if(this.isVisible()){
9360             this.constrainXY();
9361             this.adjustAssets();
9362         }
9363         this.fireEvent("resize", this, width, height);
9364         return this;
9365     },
9366
9367
9368     /**
9369      * Resizes the dialog to fit the specified content size.
9370      * @param {Number} width
9371      * @param {Number} height
9372      * @return {Roo.BasicDialog} this
9373      */
9374     setContentSize : function(w, h){
9375         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9376         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9377         //if(!this.el.isBorderBox()){
9378             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9379             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9380         //}
9381         if(this.tabs){
9382             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9383             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9384         }
9385         this.resizeTo(w, h);
9386         return this;
9387     },
9388
9389     /**
9390      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9391      * executed in response to a particular key being pressed while the dialog is active.
9392      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9393      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9394      * @param {Function} fn The function to call
9395      * @param {Object} scope (optional) The scope of the function
9396      * @return {Roo.BasicDialog} this
9397      */
9398     addKeyListener : function(key, fn, scope){
9399         var keyCode, shift, ctrl, alt;
9400         if(typeof key == "object" && !(key instanceof Array)){
9401             keyCode = key["key"];
9402             shift = key["shift"];
9403             ctrl = key["ctrl"];
9404             alt = key["alt"];
9405         }else{
9406             keyCode = key;
9407         }
9408         var handler = function(dlg, e){
9409             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9410                 var k = e.getKey();
9411                 if(keyCode instanceof Array){
9412                     for(var i = 0, len = keyCode.length; i < len; i++){
9413                         if(keyCode[i] == k){
9414                           fn.call(scope || window, dlg, k, e);
9415                           return;
9416                         }
9417                     }
9418                 }else{
9419                     if(k == keyCode){
9420                         fn.call(scope || window, dlg, k, e);
9421                     }
9422                 }
9423             }
9424         };
9425         this.on("keydown", handler);
9426         return this;
9427     },
9428
9429     /**
9430      * Returns the TabPanel component (creates it if it doesn't exist).
9431      * Note: If you wish to simply check for the existence of tabs without creating them,
9432      * check for a null 'tabs' property.
9433      * @return {Roo.TabPanel} The tabs component
9434      */
9435     getTabs : function(){
9436         if(!this.tabs){
9437             this.el.addClass("x-dlg-auto-tabs");
9438             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9439             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9440         }
9441         return this.tabs;
9442     },
9443
9444     /**
9445      * Adds a button to the footer section of the dialog.
9446      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9447      * object or a valid Roo.DomHelper element config
9448      * @param {Function} handler The function called when the button is clicked
9449      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9450      * @return {Roo.Button} The new button
9451      */
9452     addButton : function(config, handler, scope){
9453         var dh = Roo.DomHelper;
9454         if(!this.footer){
9455             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9456         }
9457         if(!this.btnContainer){
9458             var tb = this.footer.createChild({
9459
9460                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9461                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9462             }, null, true);
9463             this.btnContainer = tb.firstChild.firstChild.firstChild;
9464         }
9465         var bconfig = {
9466             handler: handler,
9467             scope: scope,
9468             minWidth: this.minButtonWidth,
9469             hideParent:true
9470         };
9471         if(typeof config == "string"){
9472             bconfig.text = config;
9473         }else{
9474             if(config.tag){
9475                 bconfig.dhconfig = config;
9476             }else{
9477                 Roo.apply(bconfig, config);
9478             }
9479         }
9480         var fc = false;
9481         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9482             bconfig.position = Math.max(0, bconfig.position);
9483             fc = this.btnContainer.childNodes[bconfig.position];
9484         }
9485          
9486         var btn = new Roo.Button(
9487             fc ? 
9488                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9489                 : this.btnContainer.appendChild(document.createElement("td")),
9490             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9491             bconfig
9492         );
9493         this.syncBodyHeight();
9494         if(!this.buttons){
9495             /**
9496              * Array of all the buttons that have been added to this dialog via addButton
9497              * @type Array
9498              */
9499             this.buttons = [];
9500         }
9501         this.buttons.push(btn);
9502         return btn;
9503     },
9504
9505     /**
9506      * Sets the default button to be focused when the dialog is displayed.
9507      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9508      * @return {Roo.BasicDialog} this
9509      */
9510     setDefaultButton : function(btn){
9511         this.defaultButton = btn;
9512         return this;
9513     },
9514
9515     // private
9516     getHeaderFooterHeight : function(safe){
9517         var height = 0;
9518         if(this.header){
9519            height += this.header.getHeight();
9520         }
9521         if(this.footer){
9522            var fm = this.footer.getMargins();
9523             height += (this.footer.getHeight()+fm.top+fm.bottom);
9524         }
9525         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9526         height += this.centerBg.getPadding("tb");
9527         return height;
9528     },
9529
9530     // private
9531     syncBodyHeight : function()
9532     {
9533         var bd = this.body, // the text
9534             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9535             bw = this.bwrap;
9536         var height = this.size.height - this.getHeaderFooterHeight(false);
9537         bd.setHeight(height-bd.getMargins("tb"));
9538         var hh = this.header.getHeight();
9539         var h = this.size.height-hh;
9540         cb.setHeight(h);
9541         
9542         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9543         bw.setHeight(h-cb.getPadding("tb"));
9544         
9545         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9546         bd.setWidth(bw.getWidth(true));
9547         if(this.tabs){
9548             this.tabs.syncHeight();
9549             if(Roo.isIE){
9550                 this.tabs.el.repaint();
9551             }
9552         }
9553     },
9554
9555     /**
9556      * Restores the previous state of the dialog if Roo.state is configured.
9557      * @return {Roo.BasicDialog} this
9558      */
9559     restoreState : function(){
9560         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9561         if(box && box.width){
9562             this.xy = [box.x, box.y];
9563             this.resizeTo(box.width, box.height);
9564         }
9565         return this;
9566     },
9567
9568     // private
9569     beforeShow : function(){
9570         this.expand();
9571         if(this.fixedcenter){
9572             this.xy = this.el.getCenterXY(true);
9573         }
9574         if(this.modal){
9575             Roo.get(document.body).addClass("x-body-masked");
9576             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9577             this.mask.show();
9578         }
9579         this.constrainXY();
9580     },
9581
9582     // private
9583     animShow : function(){
9584         var b = Roo.get(this.animateTarget).getBox();
9585         this.proxy.setSize(b.width, b.height);
9586         this.proxy.setLocation(b.x, b.y);
9587         this.proxy.show();
9588         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9589                     true, .35, this.showEl.createDelegate(this));
9590     },
9591
9592     /**
9593      * Shows the dialog.
9594      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9595      * @return {Roo.BasicDialog} this
9596      */
9597     show : function(animateTarget){
9598         if (this.fireEvent("beforeshow", this) === false){
9599             return;
9600         }
9601         if(this.syncHeightBeforeShow){
9602             this.syncBodyHeight();
9603         }else if(this.firstShow){
9604             this.firstShow = false;
9605             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9606         }
9607         this.animateTarget = animateTarget || this.animateTarget;
9608         if(!this.el.isVisible()){
9609             this.beforeShow();
9610             if(this.animateTarget && Roo.get(this.animateTarget)){
9611                 this.animShow();
9612             }else{
9613                 this.showEl();
9614             }
9615         }
9616         return this;
9617     },
9618
9619     // private
9620     showEl : function(){
9621         this.proxy.hide();
9622         this.el.setXY(this.xy);
9623         this.el.show();
9624         this.adjustAssets(true);
9625         this.toFront();
9626         this.focus();
9627         // IE peekaboo bug - fix found by Dave Fenwick
9628         if(Roo.isIE){
9629             this.el.repaint();
9630         }
9631         this.fireEvent("show", this);
9632     },
9633
9634     /**
9635      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9636      * dialog itself will receive focus.
9637      */
9638     focus : function(){
9639         if(this.defaultButton){
9640             this.defaultButton.focus();
9641         }else{
9642             this.focusEl.focus();
9643         }
9644     },
9645
9646     // private
9647     constrainXY : function(){
9648         if(this.constraintoviewport !== false){
9649             if(!this.viewSize){
9650                 if(this.container){
9651                     var s = this.container.getSize();
9652                     this.viewSize = [s.width, s.height];
9653                 }else{
9654                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9655                 }
9656             }
9657             var s = Roo.get(this.container||document).getScroll();
9658
9659             var x = this.xy[0], y = this.xy[1];
9660             var w = this.size.width, h = this.size.height;
9661             var vw = this.viewSize[0], vh = this.viewSize[1];
9662             // only move it if it needs it
9663             var moved = false;
9664             // first validate right/bottom
9665             if(x + w > vw+s.left){
9666                 x = vw - w;
9667                 moved = true;
9668             }
9669             if(y + h > vh+s.top){
9670                 y = vh - h;
9671                 moved = true;
9672             }
9673             // then make sure top/left isn't negative
9674             if(x < s.left){
9675                 x = s.left;
9676                 moved = true;
9677             }
9678             if(y < s.top){
9679                 y = s.top;
9680                 moved = true;
9681             }
9682             if(moved){
9683                 // cache xy
9684                 this.xy = [x, y];
9685                 if(this.isVisible()){
9686                     this.el.setLocation(x, y);
9687                     this.adjustAssets();
9688                 }
9689             }
9690         }
9691     },
9692
9693     // private
9694     onDrag : function(){
9695         if(!this.proxyDrag){
9696             this.xy = this.el.getXY();
9697             this.adjustAssets();
9698         }
9699     },
9700
9701     // private
9702     adjustAssets : function(doShow){
9703         var x = this.xy[0], y = this.xy[1];
9704         var w = this.size.width, h = this.size.height;
9705         if(doShow === true){
9706             if(this.shadow){
9707                 this.shadow.show(this.el);
9708             }
9709             if(this.shim){
9710                 this.shim.show();
9711             }
9712         }
9713         if(this.shadow && this.shadow.isVisible()){
9714             this.shadow.show(this.el);
9715         }
9716         if(this.shim && this.shim.isVisible()){
9717             this.shim.setBounds(x, y, w, h);
9718         }
9719     },
9720
9721     // private
9722     adjustViewport : function(w, h){
9723         if(!w || !h){
9724             w = Roo.lib.Dom.getViewWidth();
9725             h = Roo.lib.Dom.getViewHeight();
9726         }
9727         // cache the size
9728         this.viewSize = [w, h];
9729         if(this.modal && this.mask.isVisible()){
9730             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9731             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9732         }
9733         if(this.isVisible()){
9734             this.constrainXY();
9735         }
9736     },
9737
9738     /**
9739      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9740      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9741      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9742      */
9743     destroy : function(removeEl){
9744         if(this.isVisible()){
9745             this.animateTarget = null;
9746             this.hide();
9747         }
9748         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9749         if(this.tabs){
9750             this.tabs.destroy(removeEl);
9751         }
9752         Roo.destroy(
9753              this.shim,
9754              this.proxy,
9755              this.resizer,
9756              this.close,
9757              this.mask
9758         );
9759         if(this.dd){
9760             this.dd.unreg();
9761         }
9762         if(this.buttons){
9763            for(var i = 0, len = this.buttons.length; i < len; i++){
9764                this.buttons[i].destroy();
9765            }
9766         }
9767         this.el.removeAllListeners();
9768         if(removeEl === true){
9769             this.el.update("");
9770             this.el.remove();
9771         }
9772         Roo.DialogManager.unregister(this);
9773     },
9774
9775     // private
9776     startMove : function(){
9777         if(this.proxyDrag){
9778             this.proxy.show();
9779         }
9780         if(this.constraintoviewport !== false){
9781             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9782         }
9783     },
9784
9785     // private
9786     endMove : function(){
9787         if(!this.proxyDrag){
9788             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9789         }else{
9790             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9791             this.proxy.hide();
9792         }
9793         this.refreshSize();
9794         this.adjustAssets();
9795         this.focus();
9796         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9797     },
9798
9799     /**
9800      * Brings this dialog to the front of any other visible dialogs
9801      * @return {Roo.BasicDialog} this
9802      */
9803     toFront : function(){
9804         Roo.DialogManager.bringToFront(this);
9805         return this;
9806     },
9807
9808     /**
9809      * Sends this dialog to the back (under) of any other visible dialogs
9810      * @return {Roo.BasicDialog} this
9811      */
9812     toBack : function(){
9813         Roo.DialogManager.sendToBack(this);
9814         return this;
9815     },
9816
9817     /**
9818      * Centers this dialog in the viewport
9819      * @return {Roo.BasicDialog} this
9820      */
9821     center : function(){
9822         var xy = this.el.getCenterXY(true);
9823         this.moveTo(xy[0], xy[1]);
9824         return this;
9825     },
9826
9827     /**
9828      * Moves the dialog's top-left corner to the specified point
9829      * @param {Number} x
9830      * @param {Number} y
9831      * @return {Roo.BasicDialog} this
9832      */
9833     moveTo : function(x, y){
9834         this.xy = [x,y];
9835         if(this.isVisible()){
9836             this.el.setXY(this.xy);
9837             this.adjustAssets();
9838         }
9839         return this;
9840     },
9841
9842     /**
9843      * Aligns the dialog to the specified element
9844      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9845      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9846      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9847      * @return {Roo.BasicDialog} this
9848      */
9849     alignTo : function(element, position, offsets){
9850         this.xy = this.el.getAlignToXY(element, position, offsets);
9851         if(this.isVisible()){
9852             this.el.setXY(this.xy);
9853             this.adjustAssets();
9854         }
9855         return this;
9856     },
9857
9858     /**
9859      * Anchors an element to another element and realigns it when the window is resized.
9860      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9861      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9862      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9863      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9864      * is a number, it is used as the buffer delay (defaults to 50ms).
9865      * @return {Roo.BasicDialog} this
9866      */
9867     anchorTo : function(el, alignment, offsets, monitorScroll){
9868         var action = function(){
9869             this.alignTo(el, alignment, offsets);
9870         };
9871         Roo.EventManager.onWindowResize(action, this);
9872         var tm = typeof monitorScroll;
9873         if(tm != 'undefined'){
9874             Roo.EventManager.on(window, 'scroll', action, this,
9875                 {buffer: tm == 'number' ? monitorScroll : 50});
9876         }
9877         action.call(this);
9878         return this;
9879     },
9880
9881     /**
9882      * Returns true if the dialog is visible
9883      * @return {Boolean}
9884      */
9885     isVisible : function(){
9886         return this.el.isVisible();
9887     },
9888
9889     // private
9890     animHide : function(callback){
9891         var b = Roo.get(this.animateTarget).getBox();
9892         this.proxy.show();
9893         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9894         this.el.hide();
9895         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9896                     this.hideEl.createDelegate(this, [callback]));
9897     },
9898
9899     /**
9900      * Hides the dialog.
9901      * @param {Function} callback (optional) Function to call when the dialog is hidden
9902      * @return {Roo.BasicDialog} this
9903      */
9904     hide : function(callback){
9905         if (this.fireEvent("beforehide", this) === false){
9906             return;
9907         }
9908         if(this.shadow){
9909             this.shadow.hide();
9910         }
9911         if(this.shim) {
9912           this.shim.hide();
9913         }
9914         // sometimes animateTarget seems to get set.. causing problems...
9915         // this just double checks..
9916         if(this.animateTarget && Roo.get(this.animateTarget)) {
9917            this.animHide(callback);
9918         }else{
9919             this.el.hide();
9920             this.hideEl(callback);
9921         }
9922         return this;
9923     },
9924
9925     // private
9926     hideEl : function(callback){
9927         this.proxy.hide();
9928         if(this.modal){
9929             this.mask.hide();
9930             Roo.get(document.body).removeClass("x-body-masked");
9931         }
9932         this.fireEvent("hide", this);
9933         if(typeof callback == "function"){
9934             callback();
9935         }
9936     },
9937
9938     // private
9939     hideAction : function(){
9940         this.setLeft("-10000px");
9941         this.setTop("-10000px");
9942         this.setStyle("visibility", "hidden");
9943     },
9944
9945     // private
9946     refreshSize : function(){
9947         this.size = this.el.getSize();
9948         this.xy = this.el.getXY();
9949         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9950     },
9951
9952     // private
9953     // z-index is managed by the DialogManager and may be overwritten at any time
9954     setZIndex : function(index){
9955         if(this.modal){
9956             this.mask.setStyle("z-index", index);
9957         }
9958         if(this.shim){
9959             this.shim.setStyle("z-index", ++index);
9960         }
9961         if(this.shadow){
9962             this.shadow.setZIndex(++index);
9963         }
9964         this.el.setStyle("z-index", ++index);
9965         if(this.proxy){
9966             this.proxy.setStyle("z-index", ++index);
9967         }
9968         if(this.resizer){
9969             this.resizer.proxy.setStyle("z-index", ++index);
9970         }
9971
9972         this.lastZIndex = index;
9973     },
9974
9975     /**
9976      * Returns the element for this dialog
9977      * @return {Roo.Element} The underlying dialog Element
9978      */
9979     getEl : function(){
9980         return this.el;
9981     }
9982 });
9983
9984 /**
9985  * @class Roo.DialogManager
9986  * Provides global access to BasicDialogs that have been created and
9987  * support for z-indexing (layering) multiple open dialogs.
9988  */
9989 Roo.DialogManager = function(){
9990     var list = {};
9991     var accessList = [];
9992     var front = null;
9993
9994     // private
9995     var sortDialogs = function(d1, d2){
9996         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9997     };
9998
9999     // private
10000     var orderDialogs = function(){
10001         accessList.sort(sortDialogs);
10002         var seed = Roo.DialogManager.zseed;
10003         for(var i = 0, len = accessList.length; i < len; i++){
10004             var dlg = accessList[i];
10005             if(dlg){
10006                 dlg.setZIndex(seed + (i*10));
10007             }
10008         }
10009     };
10010
10011     return {
10012         /**
10013          * The starting z-index for BasicDialogs (defaults to 9000)
10014          * @type Number The z-index value
10015          */
10016         zseed : 9000,
10017
10018         // private
10019         register : function(dlg){
10020             list[dlg.id] = dlg;
10021             accessList.push(dlg);
10022         },
10023
10024         // private
10025         unregister : function(dlg){
10026             delete list[dlg.id];
10027             var i=0;
10028             var len=0;
10029             if(!accessList.indexOf){
10030                 for(  i = 0, len = accessList.length; i < len; i++){
10031                     if(accessList[i] == dlg){
10032                         accessList.splice(i, 1);
10033                         return;
10034                     }
10035                 }
10036             }else{
10037                  i = accessList.indexOf(dlg);
10038                 if(i != -1){
10039                     accessList.splice(i, 1);
10040                 }
10041             }
10042         },
10043
10044         /**
10045          * Gets a registered dialog by id
10046          * @param {String/Object} id The id of the dialog or a dialog
10047          * @return {Roo.BasicDialog} this
10048          */
10049         get : function(id){
10050             return typeof id == "object" ? id : list[id];
10051         },
10052
10053         /**
10054          * Brings the specified dialog to the front
10055          * @param {String/Object} dlg The id of the dialog or a dialog
10056          * @return {Roo.BasicDialog} this
10057          */
10058         bringToFront : function(dlg){
10059             dlg = this.get(dlg);
10060             if(dlg != front){
10061                 front = dlg;
10062                 dlg._lastAccess = new Date().getTime();
10063                 orderDialogs();
10064             }
10065             return dlg;
10066         },
10067
10068         /**
10069          * Sends the specified dialog to the back
10070          * @param {String/Object} dlg The id of the dialog or a dialog
10071          * @return {Roo.BasicDialog} this
10072          */
10073         sendToBack : function(dlg){
10074             dlg = this.get(dlg);
10075             dlg._lastAccess = -(new Date().getTime());
10076             orderDialogs();
10077             return dlg;
10078         },
10079
10080         /**
10081          * Hides all dialogs
10082          */
10083         hideAll : function(){
10084             for(var id in list){
10085                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10086                     list[id].hide();
10087                 }
10088             }
10089         }
10090     };
10091 }();
10092
10093 /**
10094  * @class Roo.LayoutDialog
10095  * @extends Roo.BasicDialog
10096  * @children Roo.ContentPanel
10097  * Dialog which provides adjustments for working with a layout in a Dialog.
10098  * Add your necessary layout config options to the dialog's config.<br>
10099  * Example usage (including a nested layout):
10100  * <pre><code>
10101 if(!dialog){
10102     dialog = new Roo.LayoutDialog("download-dlg", {
10103         modal: true,
10104         width:600,
10105         height:450,
10106         shadow:true,
10107         minWidth:500,
10108         minHeight:350,
10109         autoTabs:true,
10110         proxyDrag:true,
10111         // layout config merges with the dialog config
10112         center:{
10113             tabPosition: "top",
10114             alwaysShowTabs: true
10115         }
10116     });
10117     dialog.addKeyListener(27, dialog.hide, dialog);
10118     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10119     dialog.addButton("Build It!", this.getDownload, this);
10120
10121     // we can even add nested layouts
10122     var innerLayout = new Roo.BorderLayout("dl-inner", {
10123         east: {
10124             initialSize: 200,
10125             autoScroll:true,
10126             split:true
10127         },
10128         center: {
10129             autoScroll:true
10130         }
10131     });
10132     innerLayout.beginUpdate();
10133     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10134     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10135     innerLayout.endUpdate(true);
10136
10137     var layout = dialog.getLayout();
10138     layout.beginUpdate();
10139     layout.add("center", new Roo.ContentPanel("standard-panel",
10140                         {title: "Download the Source", fitToFrame:true}));
10141     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10142                {title: "Build your own roo.js"}));
10143     layout.getRegion("center").showPanel(sp);
10144     layout.endUpdate();
10145 }
10146 </code></pre>
10147     * @constructor
10148     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10149     * @param {Object} config configuration options
10150   */
10151 Roo.LayoutDialog = function(el, cfg){
10152     
10153     var config=  cfg;
10154     if (typeof(cfg) == 'undefined') {
10155         config = Roo.apply({}, el);
10156         // not sure why we use documentElement here.. - it should always be body.
10157         // IE7 borks horribly if we use documentElement.
10158         // webkit also does not like documentElement - it creates a body element...
10159         el = Roo.get( document.body || document.documentElement ).createChild();
10160         //config.autoCreate = true;
10161     }
10162     
10163     
10164     config.autoTabs = false;
10165     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10166     this.body.setStyle({overflow:"hidden", position:"relative"});
10167     this.layout = new Roo.BorderLayout(this.body.dom, config);
10168     this.layout.monitorWindowResize = false;
10169     this.el.addClass("x-dlg-auto-layout");
10170     // fix case when center region overwrites center function
10171     this.center = Roo.BasicDialog.prototype.center;
10172     this.on("show", this.layout.layout, this.layout, true);
10173     if (config.items) {
10174         var xitems = config.items;
10175         delete config.items;
10176         Roo.each(xitems, this.addxtype, this);
10177     }
10178     
10179     
10180 };
10181 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10182     
10183     
10184     /**
10185      * @cfg {Roo.LayoutRegion} east  
10186      */
10187     /**
10188      * @cfg {Roo.LayoutRegion} west
10189      */
10190     /**
10191      * @cfg {Roo.LayoutRegion} south
10192      */
10193     /**
10194      * @cfg {Roo.LayoutRegion} north
10195      */
10196     /**
10197      * @cfg {Roo.LayoutRegion} center
10198      */
10199     /**
10200      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10201      */
10202     
10203     
10204     /**
10205      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10206      * @deprecated
10207      */
10208     endUpdate : function(){
10209         this.layout.endUpdate();
10210     },
10211
10212     /**
10213      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10214      *  @deprecated
10215      */
10216     beginUpdate : function(){
10217         this.layout.beginUpdate();
10218     },
10219
10220     /**
10221      * Get the BorderLayout for this dialog
10222      * @return {Roo.BorderLayout}
10223      */
10224     getLayout : function(){
10225         return this.layout;
10226     },
10227
10228     showEl : function(){
10229         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10230         if(Roo.isIE7){
10231             this.layout.layout();
10232         }
10233     },
10234
10235     // private
10236     // Use the syncHeightBeforeShow config option to control this automatically
10237     syncBodyHeight : function(){
10238         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10239         if(this.layout){this.layout.layout();}
10240     },
10241     
10242       /**
10243      * Add an xtype element (actually adds to the layout.)
10244      * @return {Object} xdata xtype object data.
10245      */
10246     
10247     addxtype : function(c) {
10248         return this.layout.addxtype(c);
10249     }
10250 });/*
10251  * Based on:
10252  * Ext JS Library 1.1.1
10253  * Copyright(c) 2006-2007, Ext JS, LLC.
10254  *
10255  * Originally Released Under LGPL - original licence link has changed is not relivant.
10256  *
10257  * Fork - LGPL
10258  * <script type="text/javascript">
10259  */
10260  
10261 /**
10262  * @class Roo.MessageBox
10263  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10264  * Example usage:
10265  *<pre><code>
10266 // Basic alert:
10267 Roo.Msg.alert('Status', 'Changes saved successfully.');
10268
10269 // Prompt for user data:
10270 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10271     if (btn == 'ok'){
10272         // process text value...
10273     }
10274 });
10275
10276 // Show a dialog using config options:
10277 Roo.Msg.show({
10278    title:'Save Changes?',
10279    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10280    buttons: Roo.Msg.YESNOCANCEL,
10281    fn: processResult,
10282    animEl: 'elId'
10283 });
10284 </code></pre>
10285  * @singleton
10286  */
10287 Roo.MessageBox = function(){
10288     var dlg, opt, mask, waitTimer;
10289     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10290     var buttons, activeTextEl, bwidth;
10291
10292     // private
10293     var handleButton = function(button){
10294         dlg.hide();
10295         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10296     };
10297
10298     // private
10299     var handleHide = function(){
10300         if(opt && opt.cls){
10301             dlg.el.removeClass(opt.cls);
10302         }
10303         if(waitTimer){
10304             Roo.TaskMgr.stop(waitTimer);
10305             waitTimer = null;
10306         }
10307     };
10308
10309     // private
10310     var updateButtons = function(b){
10311         var width = 0;
10312         if(!b){
10313             buttons["ok"].hide();
10314             buttons["cancel"].hide();
10315             buttons["yes"].hide();
10316             buttons["no"].hide();
10317             dlg.footer.dom.style.display = 'none';
10318             return width;
10319         }
10320         dlg.footer.dom.style.display = '';
10321         for(var k in buttons){
10322             if(typeof buttons[k] != "function"){
10323                 if(b[k]){
10324                     buttons[k].show();
10325                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10326                     width += buttons[k].el.getWidth()+15;
10327                 }else{
10328                     buttons[k].hide();
10329                 }
10330             }
10331         }
10332         return width;
10333     };
10334
10335     // private
10336     var handleEsc = function(d, k, e){
10337         if(opt && opt.closable !== false){
10338             dlg.hide();
10339         }
10340         if(e){
10341             e.stopEvent();
10342         }
10343     };
10344
10345     return {
10346         /**
10347          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10348          * @return {Roo.BasicDialog} The BasicDialog element
10349          */
10350         getDialog : function(){
10351            if(!dlg){
10352                 dlg = new Roo.BasicDialog("x-msg-box", {
10353                     autoCreate : true,
10354                     shadow: true,
10355                     draggable: true,
10356                     resizable:false,
10357                     constraintoviewport:false,
10358                     fixedcenter:true,
10359                     collapsible : false,
10360                     shim:true,
10361                     modal: true,
10362                     width:400, height:100,
10363                     buttonAlign:"center",
10364                     closeClick : function(){
10365                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10366                             handleButton("no");
10367                         }else{
10368                             handleButton("cancel");
10369                         }
10370                     }
10371                 });
10372                 dlg.on("hide", handleHide);
10373                 mask = dlg.mask;
10374                 dlg.addKeyListener(27, handleEsc);
10375                 buttons = {};
10376                 var bt = this.buttonText;
10377                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10378                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10379                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10380                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10381                 bodyEl = dlg.body.createChild({
10382
10383                     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>'
10384                 });
10385                 msgEl = bodyEl.dom.firstChild;
10386                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10387                 textboxEl.enableDisplayMode();
10388                 textboxEl.addKeyListener([10,13], function(){
10389                     if(dlg.isVisible() && opt && opt.buttons){
10390                         if(opt.buttons.ok){
10391                             handleButton("ok");
10392                         }else if(opt.buttons.yes){
10393                             handleButton("yes");
10394                         }
10395                     }
10396                 });
10397                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10398                 textareaEl.enableDisplayMode();
10399                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10400                 progressEl.enableDisplayMode();
10401                 var pf = progressEl.dom.firstChild;
10402                 if (pf) {
10403                     pp = Roo.get(pf.firstChild);
10404                     pp.setHeight(pf.offsetHeight);
10405                 }
10406                 
10407             }
10408             return dlg;
10409         },
10410
10411         /**
10412          * Updates the message box body text
10413          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10414          * the XHTML-compliant non-breaking space character '&amp;#160;')
10415          * @return {Roo.MessageBox} This message box
10416          */
10417         updateText : function(text){
10418             if(!dlg.isVisible() && !opt.width){
10419                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10420             }
10421             msgEl.innerHTML = text || '&#160;';
10422       
10423             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10424             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10425             var w = Math.max(
10426                     Math.min(opt.width || cw , this.maxWidth), 
10427                     Math.max(opt.minWidth || this.minWidth, bwidth)
10428             );
10429             if(opt.prompt){
10430                 activeTextEl.setWidth(w);
10431             }
10432             if(dlg.isVisible()){
10433                 dlg.fixedcenter = false;
10434             }
10435             // to big, make it scroll. = But as usual stupid IE does not support
10436             // !important..
10437             
10438             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10439                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10440                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10441             } else {
10442                 bodyEl.dom.style.height = '';
10443                 bodyEl.dom.style.overflowY = '';
10444             }
10445             if (cw > w) {
10446                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10447             } else {
10448                 bodyEl.dom.style.overflowX = '';
10449             }
10450             
10451             dlg.setContentSize(w, bodyEl.getHeight());
10452             if(dlg.isVisible()){
10453                 dlg.fixedcenter = true;
10454             }
10455             return this;
10456         },
10457
10458         /**
10459          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10460          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10461          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10462          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10463          * @return {Roo.MessageBox} This message box
10464          */
10465         updateProgress : function(value, text){
10466             if(text){
10467                 this.updateText(text);
10468             }
10469             if (pp) { // weird bug on my firefox - for some reason this is not defined
10470                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10471             }
10472             return this;
10473         },        
10474
10475         /**
10476          * Returns true if the message box is currently displayed
10477          * @return {Boolean} True if the message box is visible, else false
10478          */
10479         isVisible : function(){
10480             return dlg && dlg.isVisible();  
10481         },
10482
10483         /**
10484          * Hides the message box if it is displayed
10485          */
10486         hide : function(){
10487             if(this.isVisible()){
10488                 dlg.hide();
10489             }  
10490         },
10491
10492         /**
10493          * Displays a new message box, or reinitializes an existing message box, based on the config options
10494          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10495          * The following config object properties are supported:
10496          * <pre>
10497 Property    Type             Description
10498 ----------  ---------------  ------------------------------------------------------------------------------------
10499 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10500                                    closes (defaults to undefined)
10501 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10502                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10503 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10504                                    progress and wait dialogs will ignore this property and always hide the
10505                                    close button as they can only be closed programmatically.
10506 cls               String           A custom CSS class to apply to the message box element
10507 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10508                                    displayed (defaults to 75)
10509 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10510                                    function will be btn (the name of the button that was clicked, if applicable,
10511                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10512                                    Progress and wait dialogs will ignore this option since they do not respond to
10513                                    user actions and can only be closed programmatically, so any required function
10514                                    should be called by the same code after it closes the dialog.
10515 icon              String           A CSS class that provides a background image to be used as an icon for
10516                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10517 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10518 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10519 modal             Boolean          False to allow user interaction with the page while the message box is
10520                                    displayed (defaults to true)
10521 msg               String           A string that will replace the existing message box body text (defaults
10522                                    to the XHTML-compliant non-breaking space character '&#160;')
10523 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10524 progress          Boolean          True to display a progress bar (defaults to false)
10525 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10526 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10527 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10528 title             String           The title text
10529 value             String           The string value to set into the active textbox element if displayed
10530 wait              Boolean          True to display a progress bar (defaults to false)
10531 width             Number           The width of the dialog in pixels
10532 </pre>
10533          *
10534          * Example usage:
10535          * <pre><code>
10536 Roo.Msg.show({
10537    title: 'Address',
10538    msg: 'Please enter your address:',
10539    width: 300,
10540    buttons: Roo.MessageBox.OKCANCEL,
10541    multiline: true,
10542    fn: saveAddress,
10543    animEl: 'addAddressBtn'
10544 });
10545 </code></pre>
10546          * @param {Object} config Configuration options
10547          * @return {Roo.MessageBox} This message box
10548          */
10549         show : function(options)
10550         {
10551             
10552             // this causes nightmares if you show one dialog after another
10553             // especially on callbacks..
10554              
10555             if(this.isVisible()){
10556                 
10557                 this.hide();
10558                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10559                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10560                 Roo.log("New Dialog Message:" +  options.msg )
10561                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10562                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10563                 
10564             }
10565             var d = this.getDialog();
10566             opt = options;
10567             d.setTitle(opt.title || "&#160;");
10568             d.close.setDisplayed(opt.closable !== false);
10569             activeTextEl = textboxEl;
10570             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10571             if(opt.prompt){
10572                 if(opt.multiline){
10573                     textboxEl.hide();
10574                     textareaEl.show();
10575                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10576                         opt.multiline : this.defaultTextHeight);
10577                     activeTextEl = textareaEl;
10578                 }else{
10579                     textboxEl.show();
10580                     textareaEl.hide();
10581                 }
10582             }else{
10583                 textboxEl.hide();
10584                 textareaEl.hide();
10585             }
10586             progressEl.setDisplayed(opt.progress === true);
10587             this.updateProgress(0);
10588             activeTextEl.dom.value = opt.value || "";
10589             if(opt.prompt){
10590                 dlg.setDefaultButton(activeTextEl);
10591             }else{
10592                 var bs = opt.buttons;
10593                 var db = null;
10594                 if(bs && bs.ok){
10595                     db = buttons["ok"];
10596                 }else if(bs && bs.yes){
10597                     db = buttons["yes"];
10598                 }
10599                 dlg.setDefaultButton(db);
10600             }
10601             bwidth = updateButtons(opt.buttons);
10602             this.updateText(opt.msg);
10603             if(opt.cls){
10604                 d.el.addClass(opt.cls);
10605             }
10606             d.proxyDrag = opt.proxyDrag === true;
10607             d.modal = opt.modal !== false;
10608             d.mask = opt.modal !== false ? mask : false;
10609             if(!d.isVisible()){
10610                 // force it to the end of the z-index stack so it gets a cursor in FF
10611                 document.body.appendChild(dlg.el.dom);
10612                 d.animateTarget = null;
10613                 d.show(options.animEl);
10614             }
10615             return this;
10616         },
10617
10618         /**
10619          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10620          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10621          * and closing the message box when the process is complete.
10622          * @param {String} title The title bar text
10623          * @param {String} msg The message box body text
10624          * @return {Roo.MessageBox} This message box
10625          */
10626         progress : function(title, msg){
10627             this.show({
10628                 title : title,
10629                 msg : msg,
10630                 buttons: false,
10631                 progress:true,
10632                 closable:false,
10633                 minWidth: this.minProgressWidth,
10634                 modal : true
10635             });
10636             return this;
10637         },
10638
10639         /**
10640          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10641          * If a callback function is passed it will be called after the user clicks the button, and the
10642          * id of the button that was clicked will be passed as the only parameter to the callback
10643          * (could also be the top-right close button).
10644          * @param {String} title The title bar text
10645          * @param {String} msg The message box body text
10646          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10647          * @param {Object} scope (optional) The scope of the callback function
10648          * @return {Roo.MessageBox} This message box
10649          */
10650         alert : function(title, msg, fn, scope){
10651             this.show({
10652                 title : title,
10653                 msg : msg,
10654                 buttons: this.OK,
10655                 fn: fn,
10656                 scope : scope,
10657                 modal : true
10658             });
10659             return this;
10660         },
10661
10662         /**
10663          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10664          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10665          * You are responsible for closing the message box when the process is complete.
10666          * @param {String} msg The message box body text
10667          * @param {String} title (optional) The title bar text
10668          * @return {Roo.MessageBox} This message box
10669          */
10670         wait : function(msg, title){
10671             this.show({
10672                 title : title,
10673                 msg : msg,
10674                 buttons: false,
10675                 closable:false,
10676                 progress:true,
10677                 modal:true,
10678                 width:300,
10679                 wait:true
10680             });
10681             waitTimer = Roo.TaskMgr.start({
10682                 run: function(i){
10683                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10684                 },
10685                 interval: 1000
10686             });
10687             return this;
10688         },
10689
10690         /**
10691          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10692          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10693          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10694          * @param {String} title The title bar text
10695          * @param {String} msg The message box body text
10696          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10697          * @param {Object} scope (optional) The scope of the callback function
10698          * @return {Roo.MessageBox} This message box
10699          */
10700         confirm : function(title, msg, fn, scope){
10701             this.show({
10702                 title : title,
10703                 msg : msg,
10704                 buttons: this.YESNO,
10705                 fn: fn,
10706                 scope : scope,
10707                 modal : true
10708             });
10709             return this;
10710         },
10711
10712         /**
10713          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10714          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10715          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10716          * (could also be the top-right close button) and the text that was entered will be passed as the two
10717          * parameters to the callback.
10718          * @param {String} title The title bar text
10719          * @param {String} msg The message box body text
10720          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10721          * @param {Object} scope (optional) The scope of the callback function
10722          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10723          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10724          * @return {Roo.MessageBox} This message box
10725          */
10726         prompt : function(title, msg, fn, scope, multiline){
10727             this.show({
10728                 title : title,
10729                 msg : msg,
10730                 buttons: this.OKCANCEL,
10731                 fn: fn,
10732                 minWidth:250,
10733                 scope : scope,
10734                 prompt:true,
10735                 multiline: multiline,
10736                 modal : true
10737             });
10738             return this;
10739         },
10740
10741         /**
10742          * Button config that displays a single OK button
10743          * @type Object
10744          */
10745         OK : {ok:true},
10746         /**
10747          * Button config that displays Yes and No buttons
10748          * @type Object
10749          */
10750         YESNO : {yes:true, no:true},
10751         /**
10752          * Button config that displays OK and Cancel buttons
10753          * @type Object
10754          */
10755         OKCANCEL : {ok:true, cancel:true},
10756         /**
10757          * Button config that displays Yes, No and Cancel buttons
10758          * @type Object
10759          */
10760         YESNOCANCEL : {yes:true, no:true, cancel:true},
10761
10762         /**
10763          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10764          * @type Number
10765          */
10766         defaultTextHeight : 75,
10767         /**
10768          * The maximum width in pixels of the message box (defaults to 600)
10769          * @type Number
10770          */
10771         maxWidth : 600,
10772         /**
10773          * The minimum width in pixels of the message box (defaults to 100)
10774          * @type Number
10775          */
10776         minWidth : 100,
10777         /**
10778          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10779          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10780          * @type Number
10781          */
10782         minProgressWidth : 250,
10783         /**
10784          * An object containing the default button text strings that can be overriden for localized language support.
10785          * Supported properties are: ok, cancel, yes and no.
10786          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10787          * @type Object
10788          */
10789         buttonText : {
10790             ok : "OK",
10791             cancel : "Cancel",
10792             yes : "Yes",
10793             no : "No"
10794         }
10795     };
10796 }();
10797
10798 /**
10799  * Shorthand for {@link Roo.MessageBox}
10800  */
10801 Roo.Msg = Roo.MessageBox;/*
10802  * Based on:
10803  * Ext JS Library 1.1.1
10804  * Copyright(c) 2006-2007, Ext JS, LLC.
10805  *
10806  * Originally Released Under LGPL - original licence link has changed is not relivant.
10807  *
10808  * Fork - LGPL
10809  * <script type="text/javascript">
10810  */
10811 /**
10812  * @class Roo.QuickTips
10813  * Provides attractive and customizable tooltips for any element.
10814  * @singleton
10815  */
10816 Roo.QuickTips = function(){
10817     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10818     var ce, bd, xy, dd;
10819     var visible = false, disabled = true, inited = false;
10820     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10821     
10822     var onOver = function(e){
10823         if(disabled){
10824             return;
10825         }
10826         var t = e.getTarget();
10827         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10828             return;
10829         }
10830         if(ce && t == ce.el){
10831             clearTimeout(hideProc);
10832             return;
10833         }
10834         if(t && tagEls[t.id]){
10835             tagEls[t.id].el = t;
10836             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10837             return;
10838         }
10839         var ttp, et = Roo.fly(t);
10840         var ns = cfg.namespace;
10841         if(tm.interceptTitles && t.title){
10842             ttp = t.title;
10843             t.qtip = ttp;
10844             t.removeAttribute("title");
10845             e.preventDefault();
10846         }else{
10847             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10848         }
10849         if(ttp){
10850             showProc = show.defer(tm.showDelay, tm, [{
10851                 el: t, 
10852                 text: ttp.replace(/\\n/g,'<br/>'),
10853                 width: et.getAttributeNS(ns, cfg.width),
10854                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10855                 title: et.getAttributeNS(ns, cfg.title),
10856                     cls: et.getAttributeNS(ns, cfg.cls)
10857             }]);
10858         }
10859     };
10860     
10861     var onOut = function(e){
10862         clearTimeout(showProc);
10863         var t = e.getTarget();
10864         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10865             hideProc = setTimeout(hide, tm.hideDelay);
10866         }
10867     };
10868     
10869     var onMove = function(e){
10870         if(disabled){
10871             return;
10872         }
10873         xy = e.getXY();
10874         xy[1] += 18;
10875         if(tm.trackMouse && ce){
10876             el.setXY(xy);
10877         }
10878     };
10879     
10880     var onDown = function(e){
10881         clearTimeout(showProc);
10882         clearTimeout(hideProc);
10883         if(!e.within(el)){
10884             if(tm.hideOnClick){
10885                 hide();
10886                 tm.disable();
10887                 tm.enable.defer(100, tm);
10888             }
10889         }
10890     };
10891     
10892     var getPad = function(){
10893         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10894     };
10895
10896     var show = function(o){
10897         if(disabled){
10898             return;
10899         }
10900         clearTimeout(dismissProc);
10901         ce = o;
10902         if(removeCls){ // in case manually hidden
10903             el.removeClass(removeCls);
10904             removeCls = null;
10905         }
10906         if(ce.cls){
10907             el.addClass(ce.cls);
10908             removeCls = ce.cls;
10909         }
10910         if(ce.title){
10911             tipTitle.update(ce.title);
10912             tipTitle.show();
10913         }else{
10914             tipTitle.update('');
10915             tipTitle.hide();
10916         }
10917         el.dom.style.width  = tm.maxWidth+'px';
10918         //tipBody.dom.style.width = '';
10919         tipBodyText.update(o.text);
10920         var p = getPad(), w = ce.width;
10921         if(!w){
10922             var td = tipBodyText.dom;
10923             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10924             if(aw > tm.maxWidth){
10925                 w = tm.maxWidth;
10926             }else if(aw < tm.minWidth){
10927                 w = tm.minWidth;
10928             }else{
10929                 w = aw;
10930             }
10931         }
10932         //tipBody.setWidth(w);
10933         el.setWidth(parseInt(w, 10) + p);
10934         if(ce.autoHide === false){
10935             close.setDisplayed(true);
10936             if(dd){
10937                 dd.unlock();
10938             }
10939         }else{
10940             close.setDisplayed(false);
10941             if(dd){
10942                 dd.lock();
10943             }
10944         }
10945         if(xy){
10946             el.avoidY = xy[1]-18;
10947             el.setXY(xy);
10948         }
10949         if(tm.animate){
10950             el.setOpacity(.1);
10951             el.setStyle("visibility", "visible");
10952             el.fadeIn({callback: afterShow});
10953         }else{
10954             afterShow();
10955         }
10956     };
10957     
10958     var afterShow = function(){
10959         if(ce){
10960             el.show();
10961             esc.enable();
10962             if(tm.autoDismiss && ce.autoHide !== false){
10963                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10964             }
10965         }
10966     };
10967     
10968     var hide = function(noanim){
10969         clearTimeout(dismissProc);
10970         clearTimeout(hideProc);
10971         ce = null;
10972         if(el.isVisible()){
10973             esc.disable();
10974             if(noanim !== true && tm.animate){
10975                 el.fadeOut({callback: afterHide});
10976             }else{
10977                 afterHide();
10978             } 
10979         }
10980     };
10981     
10982     var afterHide = function(){
10983         el.hide();
10984         if(removeCls){
10985             el.removeClass(removeCls);
10986             removeCls = null;
10987         }
10988     };
10989     
10990     return {
10991         /**
10992         * @cfg {Number} minWidth
10993         * The minimum width of the quick tip (defaults to 40)
10994         */
10995        minWidth : 40,
10996         /**
10997         * @cfg {Number} maxWidth
10998         * The maximum width of the quick tip (defaults to 300)
10999         */
11000        maxWidth : 300,
11001         /**
11002         * @cfg {Boolean} interceptTitles
11003         * True to automatically use the element's DOM title value if available (defaults to false)
11004         */
11005        interceptTitles : false,
11006         /**
11007         * @cfg {Boolean} trackMouse
11008         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11009         */
11010        trackMouse : false,
11011         /**
11012         * @cfg {Boolean} hideOnClick
11013         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11014         */
11015        hideOnClick : true,
11016         /**
11017         * @cfg {Number} showDelay
11018         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11019         */
11020        showDelay : 500,
11021         /**
11022         * @cfg {Number} hideDelay
11023         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11024         */
11025        hideDelay : 200,
11026         /**
11027         * @cfg {Boolean} autoHide
11028         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11029         * Used in conjunction with hideDelay.
11030         */
11031        autoHide : true,
11032         /**
11033         * @cfg {Boolean}
11034         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11035         * (defaults to true).  Used in conjunction with autoDismissDelay.
11036         */
11037        autoDismiss : true,
11038         /**
11039         * @cfg {Number}
11040         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11041         */
11042        autoDismissDelay : 5000,
11043        /**
11044         * @cfg {Boolean} animate
11045         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11046         */
11047        animate : false,
11048
11049        /**
11050         * @cfg {String} title
11051         * Title text to display (defaults to '').  This can be any valid HTML markup.
11052         */
11053         title: '',
11054        /**
11055         * @cfg {String} text
11056         * Body text to display (defaults to '').  This can be any valid HTML markup.
11057         */
11058         text : '',
11059        /**
11060         * @cfg {String} cls
11061         * A CSS class to apply to the base quick tip element (defaults to '').
11062         */
11063         cls : '',
11064        /**
11065         * @cfg {Number} width
11066         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11067         * minWidth or maxWidth.
11068         */
11069         width : null,
11070
11071     /**
11072      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11073      * or display QuickTips in a page.
11074      */
11075        init : function(){
11076           tm = Roo.QuickTips;
11077           cfg = tm.tagConfig;
11078           if(!inited){
11079               if(!Roo.isReady){ // allow calling of init() before onReady
11080                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11081                   return;
11082               }
11083               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11084               el.fxDefaults = {stopFx: true};
11085               // maximum custom styling
11086               //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>');
11087               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>');              
11088               tipTitle = el.child('h3');
11089               tipTitle.enableDisplayMode("block");
11090               tipBody = el.child('div.x-tip-bd');
11091               tipBodyText = el.child('div.x-tip-bd-inner');
11092               //bdLeft = el.child('div.x-tip-bd-left');
11093               //bdRight = el.child('div.x-tip-bd-right');
11094               close = el.child('div.x-tip-close');
11095               close.enableDisplayMode("block");
11096               close.on("click", hide);
11097               var d = Roo.get(document);
11098               d.on("mousedown", onDown);
11099               d.on("mouseover", onOver);
11100               d.on("mouseout", onOut);
11101               d.on("mousemove", onMove);
11102               esc = d.addKeyListener(27, hide);
11103               esc.disable();
11104               if(Roo.dd.DD){
11105                   dd = el.initDD("default", null, {
11106                       onDrag : function(){
11107                           el.sync();  
11108                       }
11109                   });
11110                   dd.setHandleElId(tipTitle.id);
11111                   dd.lock();
11112               }
11113               inited = true;
11114           }
11115           this.enable(); 
11116        },
11117
11118     /**
11119      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11120      * are supported:
11121      * <pre>
11122 Property    Type                   Description
11123 ----------  ---------------------  ------------------------------------------------------------------------
11124 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11125      * </ul>
11126      * @param {Object} config The config object
11127      */
11128        register : function(config){
11129            var cs = config instanceof Array ? config : arguments;
11130            for(var i = 0, len = cs.length; i < len; i++) {
11131                var c = cs[i];
11132                var target = c.target;
11133                if(target){
11134                    if(target instanceof Array){
11135                        for(var j = 0, jlen = target.length; j < jlen; j++){
11136                            tagEls[target[j]] = c;
11137                        }
11138                    }else{
11139                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11140                    }
11141                }
11142            }
11143        },
11144
11145     /**
11146      * Removes this quick tip from its element and destroys it.
11147      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11148      */
11149        unregister : function(el){
11150            delete tagEls[Roo.id(el)];
11151        },
11152
11153     /**
11154      * Enable this quick tip.
11155      */
11156        enable : function(){
11157            if(inited && disabled){
11158                locks.pop();
11159                if(locks.length < 1){
11160                    disabled = false;
11161                }
11162            }
11163        },
11164
11165     /**
11166      * Disable this quick tip.
11167      */
11168        disable : function(){
11169           disabled = true;
11170           clearTimeout(showProc);
11171           clearTimeout(hideProc);
11172           clearTimeout(dismissProc);
11173           if(ce){
11174               hide(true);
11175           }
11176           locks.push(1);
11177        },
11178
11179     /**
11180      * Returns true if the quick tip is enabled, else false.
11181      */
11182        isEnabled : function(){
11183             return !disabled;
11184        },
11185
11186         // private
11187        tagConfig : {
11188            namespace : "roo", // was ext?? this may break..
11189            alt_namespace : "ext",
11190            attribute : "qtip",
11191            width : "width",
11192            target : "target",
11193            title : "qtitle",
11194            hide : "hide",
11195            cls : "qclass"
11196        }
11197    };
11198 }();
11199
11200 // backwards compat
11201 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11202  * Based on:
11203  * Ext JS Library 1.1.1
11204  * Copyright(c) 2006-2007, Ext JS, LLC.
11205  *
11206  * Originally Released Under LGPL - original licence link has changed is not relivant.
11207  *
11208  * Fork - LGPL
11209  * <script type="text/javascript">
11210  */
11211  
11212
11213 /**
11214  * @class Roo.tree.TreePanel
11215  * @extends Roo.data.Tree
11216
11217  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11218  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11219  * @cfg {Boolean} enableDD true to enable drag and drop
11220  * @cfg {Boolean} enableDrag true to enable just drag
11221  * @cfg {Boolean} enableDrop true to enable just drop
11222  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11223  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11224  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11225  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11226  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11227  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11228  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11229  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11230  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11231  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11232  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11233  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11234  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11235  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11236  * @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>
11237  * @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>
11238  * 
11239  * @constructor
11240  * @param {String/HTMLElement/Element} el The container element
11241  * @param {Object} config
11242  */
11243 Roo.tree.TreePanel = function(el, config){
11244     var root = false;
11245     var loader = false;
11246     if (config.root) {
11247         root = config.root;
11248         delete config.root;
11249     }
11250     if (config.loader) {
11251         loader = config.loader;
11252         delete config.loader;
11253     }
11254     
11255     Roo.apply(this, config);
11256     Roo.tree.TreePanel.superclass.constructor.call(this);
11257     this.el = Roo.get(el);
11258     this.el.addClass('x-tree');
11259     //console.log(root);
11260     if (root) {
11261         this.setRootNode( Roo.factory(root, Roo.tree));
11262     }
11263     if (loader) {
11264         this.loader = Roo.factory(loader, Roo.tree);
11265     }
11266    /**
11267     * Read-only. The id of the container element becomes this TreePanel's id.
11268     */
11269     this.id = this.el.id;
11270     this.addEvents({
11271         /**
11272         * @event beforeload
11273         * Fires before a node is loaded, return false to cancel
11274         * @param {Node} node The node being loaded
11275         */
11276         "beforeload" : true,
11277         /**
11278         * @event load
11279         * Fires when a node is loaded
11280         * @param {Node} node The node that was loaded
11281         */
11282         "load" : true,
11283         /**
11284         * @event textchange
11285         * Fires when the text for a node is changed
11286         * @param {Node} node The node
11287         * @param {String} text The new text
11288         * @param {String} oldText The old text
11289         */
11290         "textchange" : true,
11291         /**
11292         * @event beforeexpand
11293         * Fires before a node is expanded, return false to cancel.
11294         * @param {Node} node The node
11295         * @param {Boolean} deep
11296         * @param {Boolean} anim
11297         */
11298         "beforeexpand" : true,
11299         /**
11300         * @event beforecollapse
11301         * Fires before a node is collapsed, return false to cancel.
11302         * @param {Node} node The node
11303         * @param {Boolean} deep
11304         * @param {Boolean} anim
11305         */
11306         "beforecollapse" : true,
11307         /**
11308         * @event expand
11309         * Fires when a node is expanded
11310         * @param {Node} node The node
11311         */
11312         "expand" : true,
11313         /**
11314         * @event disabledchange
11315         * Fires when the disabled status of a node changes
11316         * @param {Node} node The node
11317         * @param {Boolean} disabled
11318         */
11319         "disabledchange" : true,
11320         /**
11321         * @event collapse
11322         * Fires when a node is collapsed
11323         * @param {Node} node The node
11324         */
11325         "collapse" : true,
11326         /**
11327         * @event beforeclick
11328         * Fires before click processing on a node. Return false to cancel the default action.
11329         * @param {Node} node The node
11330         * @param {Roo.EventObject} e The event object
11331         */
11332         "beforeclick":true,
11333         /**
11334         * @event checkchange
11335         * Fires when a node with a checkbox's checked property changes
11336         * @param {Node} this This node
11337         * @param {Boolean} checked
11338         */
11339         "checkchange":true,
11340         /**
11341         * @event click
11342         * Fires when a node is clicked
11343         * @param {Node} node The node
11344         * @param {Roo.EventObject} e The event object
11345         */
11346         "click":true,
11347         /**
11348         * @event dblclick
11349         * Fires when a node is double clicked
11350         * @param {Node} node The node
11351         * @param {Roo.EventObject} e The event object
11352         */
11353         "dblclick":true,
11354         /**
11355         * @event contextmenu
11356         * Fires when a node is right clicked
11357         * @param {Node} node The node
11358         * @param {Roo.EventObject} e The event object
11359         */
11360         "contextmenu":true,
11361         /**
11362         * @event beforechildrenrendered
11363         * Fires right before the child nodes for a node are rendered
11364         * @param {Node} node The node
11365         */
11366         "beforechildrenrendered":true,
11367         /**
11368         * @event startdrag
11369         * Fires when a node starts being dragged
11370         * @param {Roo.tree.TreePanel} this
11371         * @param {Roo.tree.TreeNode} node
11372         * @param {event} e The raw browser event
11373         */ 
11374        "startdrag" : true,
11375        /**
11376         * @event enddrag
11377         * Fires when a drag operation is complete
11378         * @param {Roo.tree.TreePanel} this
11379         * @param {Roo.tree.TreeNode} node
11380         * @param {event} e The raw browser event
11381         */
11382        "enddrag" : true,
11383        /**
11384         * @event dragdrop
11385         * Fires when a dragged node is dropped on a valid DD target
11386         * @param {Roo.tree.TreePanel} this
11387         * @param {Roo.tree.TreeNode} node
11388         * @param {DD} dd The dd it was dropped on
11389         * @param {event} e The raw browser event
11390         */
11391        "dragdrop" : true,
11392        /**
11393         * @event beforenodedrop
11394         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11395         * passed to handlers has the following properties:<br />
11396         * <ul style="padding:5px;padding-left:16px;">
11397         * <li>tree - The TreePanel</li>
11398         * <li>target - The node being targeted for the drop</li>
11399         * <li>data - The drag data from the drag source</li>
11400         * <li>point - The point of the drop - append, above or below</li>
11401         * <li>source - The drag source</li>
11402         * <li>rawEvent - Raw mouse event</li>
11403         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11404         * to be inserted by setting them on this object.</li>
11405         * <li>cancel - Set this to true to cancel the drop.</li>
11406         * </ul>
11407         * @param {Object} dropEvent
11408         */
11409        "beforenodedrop" : true,
11410        /**
11411         * @event nodedrop
11412         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11413         * passed to handlers has the following properties:<br />
11414         * <ul style="padding:5px;padding-left:16px;">
11415         * <li>tree - The TreePanel</li>
11416         * <li>target - The node being targeted for the drop</li>
11417         * <li>data - The drag data from the drag source</li>
11418         * <li>point - The point of the drop - append, above or below</li>
11419         * <li>source - The drag source</li>
11420         * <li>rawEvent - Raw mouse event</li>
11421         * <li>dropNode - Dropped node(s).</li>
11422         * </ul>
11423         * @param {Object} dropEvent
11424         */
11425        "nodedrop" : true,
11426         /**
11427         * @event nodedragover
11428         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11429         * passed to handlers has the following properties:<br />
11430         * <ul style="padding:5px;padding-left:16px;">
11431         * <li>tree - The TreePanel</li>
11432         * <li>target - The node being targeted for the drop</li>
11433         * <li>data - The drag data from the drag source</li>
11434         * <li>point - The point of the drop - append, above or below</li>
11435         * <li>source - The drag source</li>
11436         * <li>rawEvent - Raw mouse event</li>
11437         * <li>dropNode - Drop node(s) provided by the source.</li>
11438         * <li>cancel - Set this to true to signal drop not allowed.</li>
11439         * </ul>
11440         * @param {Object} dragOverEvent
11441         */
11442        "nodedragover" : true,
11443        /**
11444         * @event appendnode
11445         * Fires when append node to the tree
11446         * @param {Roo.tree.TreePanel} this
11447         * @param {Roo.tree.TreeNode} node
11448         * @param {Number} index The index of the newly appended node
11449         */
11450        "appendnode" : true
11451         
11452     });
11453     if(this.singleExpand){
11454        this.on("beforeexpand", this.restrictExpand, this);
11455     }
11456     if (this.editor) {
11457         this.editor.tree = this;
11458         this.editor = Roo.factory(this.editor, Roo.tree);
11459     }
11460     
11461     if (this.selModel) {
11462         this.selModel = Roo.factory(this.selModel, Roo.tree);
11463     }
11464    
11465 };
11466 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11467     rootVisible : true,
11468     animate: Roo.enableFx,
11469     lines : true,
11470     enableDD : false,
11471     hlDrop : Roo.enableFx,
11472   
11473     renderer: false,
11474     
11475     rendererTip: false,
11476     // private
11477     restrictExpand : function(node){
11478         var p = node.parentNode;
11479         if(p){
11480             if(p.expandedChild && p.expandedChild.parentNode == p){
11481                 p.expandedChild.collapse();
11482             }
11483             p.expandedChild = node;
11484         }
11485     },
11486
11487     // private override
11488     setRootNode : function(node){
11489         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11490         if(!this.rootVisible){
11491             node.ui = new Roo.tree.RootTreeNodeUI(node);
11492         }
11493         return node;
11494     },
11495
11496     /**
11497      * Returns the container element for this TreePanel
11498      */
11499     getEl : function(){
11500         return this.el;
11501     },
11502
11503     /**
11504      * Returns the default TreeLoader for this TreePanel
11505      */
11506     getLoader : function(){
11507         return this.loader;
11508     },
11509
11510     /**
11511      * Expand all nodes
11512      */
11513     expandAll : function(){
11514         this.root.expand(true);
11515     },
11516
11517     /**
11518      * Collapse all nodes
11519      */
11520     collapseAll : function(){
11521         this.root.collapse(true);
11522     },
11523
11524     /**
11525      * Returns the selection model used by this TreePanel
11526      */
11527     getSelectionModel : function(){
11528         if(!this.selModel){
11529             this.selModel = new Roo.tree.DefaultSelectionModel();
11530         }
11531         return this.selModel;
11532     },
11533
11534     /**
11535      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11536      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11537      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11538      * @return {Array}
11539      */
11540     getChecked : function(a, startNode){
11541         startNode = startNode || this.root;
11542         var r = [];
11543         var f = function(){
11544             if(this.attributes.checked){
11545                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11546             }
11547         }
11548         startNode.cascade(f);
11549         return r;
11550     },
11551
11552     /**
11553      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11554      * @param {String} path
11555      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11556      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11557      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11558      */
11559     expandPath : function(path, attr, callback){
11560         attr = attr || "id";
11561         var keys = path.split(this.pathSeparator);
11562         var curNode = this.root;
11563         if(curNode.attributes[attr] != keys[1]){ // invalid root
11564             if(callback){
11565                 callback(false, null);
11566             }
11567             return;
11568         }
11569         var index = 1;
11570         var f = function(){
11571             if(++index == keys.length){
11572                 if(callback){
11573                     callback(true, curNode);
11574                 }
11575                 return;
11576             }
11577             var c = curNode.findChild(attr, keys[index]);
11578             if(!c){
11579                 if(callback){
11580                     callback(false, curNode);
11581                 }
11582                 return;
11583             }
11584             curNode = c;
11585             c.expand(false, false, f);
11586         };
11587         curNode.expand(false, false, f);
11588     },
11589
11590     /**
11591      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11592      * @param {String} path
11593      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11594      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11595      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11596      */
11597     selectPath : function(path, attr, callback){
11598         attr = attr || "id";
11599         var keys = path.split(this.pathSeparator);
11600         var v = keys.pop();
11601         if(keys.length > 0){
11602             var f = function(success, node){
11603                 if(success && node){
11604                     var n = node.findChild(attr, v);
11605                     if(n){
11606                         n.select();
11607                         if(callback){
11608                             callback(true, n);
11609                         }
11610                     }else if(callback){
11611                         callback(false, n);
11612                     }
11613                 }else{
11614                     if(callback){
11615                         callback(false, n);
11616                     }
11617                 }
11618             };
11619             this.expandPath(keys.join(this.pathSeparator), attr, f);
11620         }else{
11621             this.root.select();
11622             if(callback){
11623                 callback(true, this.root);
11624             }
11625         }
11626     },
11627
11628     getTreeEl : function(){
11629         return this.el;
11630     },
11631
11632     /**
11633      * Trigger rendering of this TreePanel
11634      */
11635     render : function(){
11636         if (this.innerCt) {
11637             return this; // stop it rendering more than once!!
11638         }
11639         
11640         this.innerCt = this.el.createChild({tag:"ul",
11641                cls:"x-tree-root-ct " +
11642                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11643
11644         if(this.containerScroll){
11645             Roo.dd.ScrollManager.register(this.el);
11646         }
11647         if((this.enableDD || this.enableDrop) && !this.dropZone){
11648            /**
11649             * The dropZone used by this tree if drop is enabled
11650             * @type Roo.tree.TreeDropZone
11651             */
11652              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11653                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11654            });
11655         }
11656         if((this.enableDD || this.enableDrag) && !this.dragZone){
11657            /**
11658             * The dragZone used by this tree if drag is enabled
11659             * @type Roo.tree.TreeDragZone
11660             */
11661             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11662                ddGroup: this.ddGroup || "TreeDD",
11663                scroll: this.ddScroll
11664            });
11665         }
11666         this.getSelectionModel().init(this);
11667         if (!this.root) {
11668             Roo.log("ROOT not set in tree");
11669             return this;
11670         }
11671         this.root.render();
11672         if(!this.rootVisible){
11673             this.root.renderChildren();
11674         }
11675         return this;
11676     }
11677 });/*
11678  * Based on:
11679  * Ext JS Library 1.1.1
11680  * Copyright(c) 2006-2007, Ext JS, LLC.
11681  *
11682  * Originally Released Under LGPL - original licence link has changed is not relivant.
11683  *
11684  * Fork - LGPL
11685  * <script type="text/javascript">
11686  */
11687  
11688
11689 /**
11690  * @class Roo.tree.DefaultSelectionModel
11691  * @extends Roo.util.Observable
11692  * The default single selection for a TreePanel.
11693  * @param {Object} cfg Configuration
11694  */
11695 Roo.tree.DefaultSelectionModel = function(cfg){
11696    this.selNode = null;
11697    
11698    
11699    
11700    this.addEvents({
11701        /**
11702         * @event selectionchange
11703         * Fires when the selected node changes
11704         * @param {DefaultSelectionModel} this
11705         * @param {TreeNode} node the new selection
11706         */
11707        "selectionchange" : true,
11708
11709        /**
11710         * @event beforeselect
11711         * Fires before the selected node changes, return false to cancel the change
11712         * @param {DefaultSelectionModel} this
11713         * @param {TreeNode} node the new selection
11714         * @param {TreeNode} node the old selection
11715         */
11716        "beforeselect" : true
11717    });
11718    
11719     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11720 };
11721
11722 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11723     init : function(tree){
11724         this.tree = tree;
11725         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11726         tree.on("click", this.onNodeClick, this);
11727     },
11728     
11729     onNodeClick : function(node, e){
11730         if (e.ctrlKey && this.selNode == node)  {
11731             this.unselect(node);
11732             return;
11733         }
11734         this.select(node);
11735     },
11736     
11737     /**
11738      * Select a node.
11739      * @param {TreeNode} node The node to select
11740      * @return {TreeNode} The selected node
11741      */
11742     select : function(node){
11743         var last = this.selNode;
11744         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11745             if(last){
11746                 last.ui.onSelectedChange(false);
11747             }
11748             this.selNode = node;
11749             node.ui.onSelectedChange(true);
11750             this.fireEvent("selectionchange", this, node, last);
11751         }
11752         return node;
11753     },
11754     
11755     /**
11756      * Deselect a node.
11757      * @param {TreeNode} node The node to unselect
11758      */
11759     unselect : function(node){
11760         if(this.selNode == node){
11761             this.clearSelections();
11762         }    
11763     },
11764     
11765     /**
11766      * Clear all selections
11767      */
11768     clearSelections : function(){
11769         var n = this.selNode;
11770         if(n){
11771             n.ui.onSelectedChange(false);
11772             this.selNode = null;
11773             this.fireEvent("selectionchange", this, null);
11774         }
11775         return n;
11776     },
11777     
11778     /**
11779      * Get the selected node
11780      * @return {TreeNode} The selected node
11781      */
11782     getSelectedNode : function(){
11783         return this.selNode;    
11784     },
11785     
11786     /**
11787      * Returns true if the node is selected
11788      * @param {TreeNode} node The node to check
11789      * @return {Boolean}
11790      */
11791     isSelected : function(node){
11792         return this.selNode == node;  
11793     },
11794
11795     /**
11796      * Selects the node above the selected node in the tree, intelligently walking the nodes
11797      * @return TreeNode The new selection
11798      */
11799     selectPrevious : function(){
11800         var s = this.selNode || this.lastSelNode;
11801         if(!s){
11802             return null;
11803         }
11804         var ps = s.previousSibling;
11805         if(ps){
11806             if(!ps.isExpanded() || ps.childNodes.length < 1){
11807                 return this.select(ps);
11808             } else{
11809                 var lc = ps.lastChild;
11810                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11811                     lc = lc.lastChild;
11812                 }
11813                 return this.select(lc);
11814             }
11815         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11816             return this.select(s.parentNode);
11817         }
11818         return null;
11819     },
11820
11821     /**
11822      * Selects the node above the selected node in the tree, intelligently walking the nodes
11823      * @return TreeNode The new selection
11824      */
11825     selectNext : function(){
11826         var s = this.selNode || this.lastSelNode;
11827         if(!s){
11828             return null;
11829         }
11830         if(s.firstChild && s.isExpanded()){
11831              return this.select(s.firstChild);
11832          }else if(s.nextSibling){
11833              return this.select(s.nextSibling);
11834          }else if(s.parentNode){
11835             var newS = null;
11836             s.parentNode.bubble(function(){
11837                 if(this.nextSibling){
11838                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11839                     return false;
11840                 }
11841             });
11842             return newS;
11843          }
11844         return null;
11845     },
11846
11847     onKeyDown : function(e){
11848         var s = this.selNode || this.lastSelNode;
11849         // undesirable, but required
11850         var sm = this;
11851         if(!s){
11852             return;
11853         }
11854         var k = e.getKey();
11855         switch(k){
11856              case e.DOWN:
11857                  e.stopEvent();
11858                  this.selectNext();
11859              break;
11860              case e.UP:
11861                  e.stopEvent();
11862                  this.selectPrevious();
11863              break;
11864              case e.RIGHT:
11865                  e.preventDefault();
11866                  if(s.hasChildNodes()){
11867                      if(!s.isExpanded()){
11868                          s.expand();
11869                      }else if(s.firstChild){
11870                          this.select(s.firstChild, e);
11871                      }
11872                  }
11873              break;
11874              case e.LEFT:
11875                  e.preventDefault();
11876                  if(s.hasChildNodes() && s.isExpanded()){
11877                      s.collapse();
11878                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11879                      this.select(s.parentNode, e);
11880                  }
11881              break;
11882         };
11883     }
11884 });
11885
11886 /**
11887  * @class Roo.tree.MultiSelectionModel
11888  * @extends Roo.util.Observable
11889  * Multi selection for a TreePanel.
11890  * @param {Object} cfg Configuration
11891  */
11892 Roo.tree.MultiSelectionModel = function(){
11893    this.selNodes = [];
11894    this.selMap = {};
11895    this.addEvents({
11896        /**
11897         * @event selectionchange
11898         * Fires when the selected nodes change
11899         * @param {MultiSelectionModel} this
11900         * @param {Array} nodes Array of the selected nodes
11901         */
11902        "selectionchange" : true
11903    });
11904    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11905    
11906 };
11907
11908 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11909     init : function(tree){
11910         this.tree = tree;
11911         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11912         tree.on("click", this.onNodeClick, this);
11913     },
11914     
11915     onNodeClick : function(node, e){
11916         this.select(node, e, e.ctrlKey);
11917     },
11918     
11919     /**
11920      * Select a node.
11921      * @param {TreeNode} node The node to select
11922      * @param {EventObject} e (optional) An event associated with the selection
11923      * @param {Boolean} keepExisting True to retain existing selections
11924      * @return {TreeNode} The selected node
11925      */
11926     select : function(node, e, keepExisting){
11927         if(keepExisting !== true){
11928             this.clearSelections(true);
11929         }
11930         if(this.isSelected(node)){
11931             this.lastSelNode = node;
11932             return node;
11933         }
11934         this.selNodes.push(node);
11935         this.selMap[node.id] = node;
11936         this.lastSelNode = node;
11937         node.ui.onSelectedChange(true);
11938         this.fireEvent("selectionchange", this, this.selNodes);
11939         return node;
11940     },
11941     
11942     /**
11943      * Deselect a node.
11944      * @param {TreeNode} node The node to unselect
11945      */
11946     unselect : function(node){
11947         if(this.selMap[node.id]){
11948             node.ui.onSelectedChange(false);
11949             var sn = this.selNodes;
11950             var index = -1;
11951             if(sn.indexOf){
11952                 index = sn.indexOf(node);
11953             }else{
11954                 for(var i = 0, len = sn.length; i < len; i++){
11955                     if(sn[i] == node){
11956                         index = i;
11957                         break;
11958                     }
11959                 }
11960             }
11961             if(index != -1){
11962                 this.selNodes.splice(index, 1);
11963             }
11964             delete this.selMap[node.id];
11965             this.fireEvent("selectionchange", this, this.selNodes);
11966         }
11967     },
11968     
11969     /**
11970      * Clear all selections
11971      */
11972     clearSelections : function(suppressEvent){
11973         var sn = this.selNodes;
11974         if(sn.length > 0){
11975             for(var i = 0, len = sn.length; i < len; i++){
11976                 sn[i].ui.onSelectedChange(false);
11977             }
11978             this.selNodes = [];
11979             this.selMap = {};
11980             if(suppressEvent !== true){
11981                 this.fireEvent("selectionchange", this, this.selNodes);
11982             }
11983         }
11984     },
11985     
11986     /**
11987      * Returns true if the node is selected
11988      * @param {TreeNode} node The node to check
11989      * @return {Boolean}
11990      */
11991     isSelected : function(node){
11992         return this.selMap[node.id] ? true : false;  
11993     },
11994     
11995     /**
11996      * Returns an array of the selected nodes
11997      * @return {Array}
11998      */
11999     getSelectedNodes : function(){
12000         return this.selNodes;    
12001     },
12002
12003     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12004
12005     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12006
12007     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12008 });/*
12009  * Based on:
12010  * Ext JS Library 1.1.1
12011  * Copyright(c) 2006-2007, Ext JS, LLC.
12012  *
12013  * Originally Released Under LGPL - original licence link has changed is not relivant.
12014  *
12015  * Fork - LGPL
12016  * <script type="text/javascript">
12017  */
12018  
12019 /**
12020  * @class Roo.tree.TreeNode
12021  * @extends Roo.data.Node
12022  * @cfg {String} text The text for this node
12023  * @cfg {Boolean} expanded true to start the node expanded
12024  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12025  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12026  * @cfg {Boolean} disabled true to start the node disabled
12027  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12028  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12029  * @cfg {String} cls A css class to be added to the node
12030  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12031  * @cfg {String} href URL of the link used for the node (defaults to #)
12032  * @cfg {String} hrefTarget target frame for the link
12033  * @cfg {String} qtip An Ext QuickTip for the node
12034  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12035  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12036  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12037  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12038  * (defaults to undefined with no checkbox rendered)
12039  * @constructor
12040  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12041  */
12042 Roo.tree.TreeNode = function(attributes){
12043     attributes = attributes || {};
12044     if(typeof attributes == "string"){
12045         attributes = {text: attributes};
12046     }
12047     this.childrenRendered = false;
12048     this.rendered = false;
12049     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12050     this.expanded = attributes.expanded === true;
12051     this.isTarget = attributes.isTarget !== false;
12052     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12053     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12054
12055     /**
12056      * Read-only. The text for this node. To change it use setText().
12057      * @type String
12058      */
12059     this.text = attributes.text;
12060     /**
12061      * True if this node is disabled.
12062      * @type Boolean
12063      */
12064     this.disabled = attributes.disabled === true;
12065
12066     this.addEvents({
12067         /**
12068         * @event textchange
12069         * Fires when the text for this node is changed
12070         * @param {Node} this This node
12071         * @param {String} text The new text
12072         * @param {String} oldText The old text
12073         */
12074         "textchange" : true,
12075         /**
12076         * @event beforeexpand
12077         * Fires before this node is expanded, return false to cancel.
12078         * @param {Node} this This node
12079         * @param {Boolean} deep
12080         * @param {Boolean} anim
12081         */
12082         "beforeexpand" : true,
12083         /**
12084         * @event beforecollapse
12085         * Fires before this node is collapsed, return false to cancel.
12086         * @param {Node} this This node
12087         * @param {Boolean} deep
12088         * @param {Boolean} anim
12089         */
12090         "beforecollapse" : true,
12091         /**
12092         * @event expand
12093         * Fires when this node is expanded
12094         * @param {Node} this This node
12095         */
12096         "expand" : true,
12097         /**
12098         * @event disabledchange
12099         * Fires when the disabled status of this node changes
12100         * @param {Node} this This node
12101         * @param {Boolean} disabled
12102         */
12103         "disabledchange" : true,
12104         /**
12105         * @event collapse
12106         * Fires when this node is collapsed
12107         * @param {Node} this This node
12108         */
12109         "collapse" : true,
12110         /**
12111         * @event beforeclick
12112         * Fires before click processing. Return false to cancel the default action.
12113         * @param {Node} this This node
12114         * @param {Roo.EventObject} e The event object
12115         */
12116         "beforeclick":true,
12117         /**
12118         * @event checkchange
12119         * Fires when a node with a checkbox's checked property changes
12120         * @param {Node} this This node
12121         * @param {Boolean} checked
12122         */
12123         "checkchange":true,
12124         /**
12125         * @event click
12126         * Fires when this node is clicked
12127         * @param {Node} this This node
12128         * @param {Roo.EventObject} e The event object
12129         */
12130         "click":true,
12131         /**
12132         * @event dblclick
12133         * Fires when this node is double clicked
12134         * @param {Node} this This node
12135         * @param {Roo.EventObject} e The event object
12136         */
12137         "dblclick":true,
12138         /**
12139         * @event contextmenu
12140         * Fires when this node is right clicked
12141         * @param {Node} this This node
12142         * @param {Roo.EventObject} e The event object
12143         */
12144         "contextmenu":true,
12145         /**
12146         * @event beforechildrenrendered
12147         * Fires right before the child nodes for this node are rendered
12148         * @param {Node} this This node
12149         */
12150         "beforechildrenrendered":true
12151     });
12152
12153     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12154
12155     /**
12156      * Read-only. The UI for this node
12157      * @type TreeNodeUI
12158      */
12159     this.ui = new uiClass(this);
12160     
12161     // finally support items[]
12162     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12163         return;
12164     }
12165     
12166     
12167     Roo.each(this.attributes.items, function(c) {
12168         this.appendChild(Roo.factory(c,Roo.Tree));
12169     }, this);
12170     delete this.attributes.items;
12171     
12172     
12173     
12174 };
12175 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12176     preventHScroll: true,
12177     /**
12178      * Returns true if this node is expanded
12179      * @return {Boolean}
12180      */
12181     isExpanded : function(){
12182         return this.expanded;
12183     },
12184
12185     /**
12186      * Returns the UI object for this node
12187      * @return {TreeNodeUI}
12188      */
12189     getUI : function(){
12190         return this.ui;
12191     },
12192
12193     // private override
12194     setFirstChild : function(node){
12195         var of = this.firstChild;
12196         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12197         if(this.childrenRendered && of && node != of){
12198             of.renderIndent(true, true);
12199         }
12200         if(this.rendered){
12201             this.renderIndent(true, true);
12202         }
12203     },
12204
12205     // private override
12206     setLastChild : function(node){
12207         var ol = this.lastChild;
12208         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12209         if(this.childrenRendered && ol && node != ol){
12210             ol.renderIndent(true, true);
12211         }
12212         if(this.rendered){
12213             this.renderIndent(true, true);
12214         }
12215     },
12216
12217     // these methods are overridden to provide lazy rendering support
12218     // private override
12219     appendChild : function()
12220     {
12221         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12222         if(node && this.childrenRendered){
12223             node.render();
12224         }
12225         this.ui.updateExpandIcon();
12226         return node;
12227     },
12228
12229     // private override
12230     removeChild : function(node){
12231         this.ownerTree.getSelectionModel().unselect(node);
12232         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12233         // if it's been rendered remove dom node
12234         if(this.childrenRendered){
12235             node.ui.remove();
12236         }
12237         if(this.childNodes.length < 1){
12238             this.collapse(false, false);
12239         }else{
12240             this.ui.updateExpandIcon();
12241         }
12242         if(!this.firstChild) {
12243             this.childrenRendered = false;
12244         }
12245         return node;
12246     },
12247
12248     // private override
12249     insertBefore : function(node, refNode){
12250         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12251         if(newNode && refNode && this.childrenRendered){
12252             node.render();
12253         }
12254         this.ui.updateExpandIcon();
12255         return newNode;
12256     },
12257
12258     /**
12259      * Sets the text for this node
12260      * @param {String} text
12261      */
12262     setText : function(text){
12263         var oldText = this.text;
12264         this.text = text;
12265         this.attributes.text = text;
12266         if(this.rendered){ // event without subscribing
12267             this.ui.onTextChange(this, text, oldText);
12268         }
12269         this.fireEvent("textchange", this, text, oldText);
12270     },
12271
12272     /**
12273      * Triggers selection of this node
12274      */
12275     select : function(){
12276         this.getOwnerTree().getSelectionModel().select(this);
12277     },
12278
12279     /**
12280      * Triggers deselection of this node
12281      */
12282     unselect : function(){
12283         this.getOwnerTree().getSelectionModel().unselect(this);
12284     },
12285
12286     /**
12287      * Returns true if this node is selected
12288      * @return {Boolean}
12289      */
12290     isSelected : function(){
12291         return this.getOwnerTree().getSelectionModel().isSelected(this);
12292     },
12293
12294     /**
12295      * Expand this node.
12296      * @param {Boolean} deep (optional) True to expand all children as well
12297      * @param {Boolean} anim (optional) false to cancel the default animation
12298      * @param {Function} callback (optional) A callback to be called when
12299      * expanding this node completes (does not wait for deep expand to complete).
12300      * Called with 1 parameter, this node.
12301      */
12302     expand : function(deep, anim, callback){
12303         if(!this.expanded){
12304             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12305                 return;
12306             }
12307             if(!this.childrenRendered){
12308                 this.renderChildren();
12309             }
12310             this.expanded = true;
12311             
12312             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12313                 this.ui.animExpand(function(){
12314                     this.fireEvent("expand", this);
12315                     if(typeof callback == "function"){
12316                         callback(this);
12317                     }
12318                     if(deep === true){
12319                         this.expandChildNodes(true);
12320                     }
12321                 }.createDelegate(this));
12322                 return;
12323             }else{
12324                 this.ui.expand();
12325                 this.fireEvent("expand", this);
12326                 if(typeof callback == "function"){
12327                     callback(this);
12328                 }
12329             }
12330         }else{
12331            if(typeof callback == "function"){
12332                callback(this);
12333            }
12334         }
12335         if(deep === true){
12336             this.expandChildNodes(true);
12337         }
12338     },
12339
12340     isHiddenRoot : function(){
12341         return this.isRoot && !this.getOwnerTree().rootVisible;
12342     },
12343
12344     /**
12345      * Collapse this node.
12346      * @param {Boolean} deep (optional) True to collapse all children as well
12347      * @param {Boolean} anim (optional) false to cancel the default animation
12348      */
12349     collapse : function(deep, anim){
12350         if(this.expanded && !this.isHiddenRoot()){
12351             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12352                 return;
12353             }
12354             this.expanded = false;
12355             if((this.getOwnerTree().animate && anim !== false) || anim){
12356                 this.ui.animCollapse(function(){
12357                     this.fireEvent("collapse", this);
12358                     if(deep === true){
12359                         this.collapseChildNodes(true);
12360                     }
12361                 }.createDelegate(this));
12362                 return;
12363             }else{
12364                 this.ui.collapse();
12365                 this.fireEvent("collapse", this);
12366             }
12367         }
12368         if(deep === true){
12369             var cs = this.childNodes;
12370             for(var i = 0, len = cs.length; i < len; i++) {
12371                 cs[i].collapse(true, false);
12372             }
12373         }
12374     },
12375
12376     // private
12377     delayedExpand : function(delay){
12378         if(!this.expandProcId){
12379             this.expandProcId = this.expand.defer(delay, this);
12380         }
12381     },
12382
12383     // private
12384     cancelExpand : function(){
12385         if(this.expandProcId){
12386             clearTimeout(this.expandProcId);
12387         }
12388         this.expandProcId = false;
12389     },
12390
12391     /**
12392      * Toggles expanded/collapsed state of the node
12393      */
12394     toggle : function(){
12395         if(this.expanded){
12396             this.collapse();
12397         }else{
12398             this.expand();
12399         }
12400     },
12401
12402     /**
12403      * Ensures all parent nodes are expanded
12404      */
12405     ensureVisible : function(callback){
12406         var tree = this.getOwnerTree();
12407         tree.expandPath(this.parentNode.getPath(), false, function(){
12408             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12409             Roo.callback(callback);
12410         }.createDelegate(this));
12411     },
12412
12413     /**
12414      * Expand all child nodes
12415      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12416      */
12417     expandChildNodes : function(deep){
12418         var cs = this.childNodes;
12419         for(var i = 0, len = cs.length; i < len; i++) {
12420                 cs[i].expand(deep);
12421         }
12422     },
12423
12424     /**
12425      * Collapse all child nodes
12426      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12427      */
12428     collapseChildNodes : function(deep){
12429         var cs = this.childNodes;
12430         for(var i = 0, len = cs.length; i < len; i++) {
12431                 cs[i].collapse(deep);
12432         }
12433     },
12434
12435     /**
12436      * Disables this node
12437      */
12438     disable : function(){
12439         this.disabled = true;
12440         this.unselect();
12441         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12442             this.ui.onDisableChange(this, true);
12443         }
12444         this.fireEvent("disabledchange", this, true);
12445     },
12446
12447     /**
12448      * Enables this node
12449      */
12450     enable : function(){
12451         this.disabled = false;
12452         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12453             this.ui.onDisableChange(this, false);
12454         }
12455         this.fireEvent("disabledchange", this, false);
12456     },
12457
12458     // private
12459     renderChildren : function(suppressEvent){
12460         if(suppressEvent !== false){
12461             this.fireEvent("beforechildrenrendered", this);
12462         }
12463         var cs = this.childNodes;
12464         for(var i = 0, len = cs.length; i < len; i++){
12465             cs[i].render(true);
12466         }
12467         this.childrenRendered = true;
12468     },
12469
12470     // private
12471     sort : function(fn, scope){
12472         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12473         if(this.childrenRendered){
12474             var cs = this.childNodes;
12475             for(var i = 0, len = cs.length; i < len; i++){
12476                 cs[i].render(true);
12477             }
12478         }
12479     },
12480
12481     // private
12482     render : function(bulkRender){
12483         this.ui.render(bulkRender);
12484         if(!this.rendered){
12485             this.rendered = true;
12486             if(this.expanded){
12487                 this.expanded = false;
12488                 this.expand(false, false);
12489             }
12490         }
12491     },
12492
12493     // private
12494     renderIndent : function(deep, refresh){
12495         if(refresh){
12496             this.ui.childIndent = null;
12497         }
12498         this.ui.renderIndent();
12499         if(deep === true && this.childrenRendered){
12500             var cs = this.childNodes;
12501             for(var i = 0, len = cs.length; i < len; i++){
12502                 cs[i].renderIndent(true, refresh);
12503             }
12504         }
12505     }
12506 });/*
12507  * Based on:
12508  * Ext JS Library 1.1.1
12509  * Copyright(c) 2006-2007, Ext JS, LLC.
12510  *
12511  * Originally Released Under LGPL - original licence link has changed is not relivant.
12512  *
12513  * Fork - LGPL
12514  * <script type="text/javascript">
12515  */
12516  
12517 /**
12518  * @class Roo.tree.AsyncTreeNode
12519  * @extends Roo.tree.TreeNode
12520  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12521  * @constructor
12522  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12523  */
12524  Roo.tree.AsyncTreeNode = function(config){
12525     this.loaded = false;
12526     this.loading = false;
12527     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12528     /**
12529     * @event beforeload
12530     * Fires before this node is loaded, return false to cancel
12531     * @param {Node} this This node
12532     */
12533     this.addEvents({'beforeload':true, 'load': true});
12534     /**
12535     * @event load
12536     * Fires when this node is loaded
12537     * @param {Node} this This node
12538     */
12539     /**
12540      * The loader used by this node (defaults to using the tree's defined loader)
12541      * @type TreeLoader
12542      * @property loader
12543      */
12544 };
12545 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12546     expand : function(deep, anim, callback){
12547         if(this.loading){ // if an async load is already running, waiting til it's done
12548             var timer;
12549             var f = function(){
12550                 if(!this.loading){ // done loading
12551                     clearInterval(timer);
12552                     this.expand(deep, anim, callback);
12553                 }
12554             }.createDelegate(this);
12555             timer = setInterval(f, 200);
12556             return;
12557         }
12558         if(!this.loaded){
12559             if(this.fireEvent("beforeload", this) === false){
12560                 return;
12561             }
12562             this.loading = true;
12563             this.ui.beforeLoad(this);
12564             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12565             if(loader){
12566                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12567                 return;
12568             }
12569         }
12570         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12571     },
12572     
12573     /**
12574      * Returns true if this node is currently loading
12575      * @return {Boolean}
12576      */
12577     isLoading : function(){
12578         return this.loading;  
12579     },
12580     
12581     loadComplete : function(deep, anim, callback){
12582         this.loading = false;
12583         this.loaded = true;
12584         this.ui.afterLoad(this);
12585         this.fireEvent("load", this);
12586         this.expand(deep, anim, callback);
12587     },
12588     
12589     /**
12590      * Returns true if this node has been loaded
12591      * @return {Boolean}
12592      */
12593     isLoaded : function(){
12594         return this.loaded;
12595     },
12596     
12597     hasChildNodes : function(){
12598         if(!this.isLeaf() && !this.loaded){
12599             return true;
12600         }else{
12601             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12602         }
12603     },
12604
12605     /**
12606      * Trigger a reload for this node
12607      * @param {Function} callback
12608      */
12609     reload : function(callback){
12610         this.collapse(false, false);
12611         while(this.firstChild){
12612             this.removeChild(this.firstChild);
12613         }
12614         this.childrenRendered = false;
12615         this.loaded = false;
12616         if(this.isHiddenRoot()){
12617             this.expanded = false;
12618         }
12619         this.expand(false, false, callback);
12620     }
12621 });/*
12622  * Based on:
12623  * Ext JS Library 1.1.1
12624  * Copyright(c) 2006-2007, Ext JS, LLC.
12625  *
12626  * Originally Released Under LGPL - original licence link has changed is not relivant.
12627  *
12628  * Fork - LGPL
12629  * <script type="text/javascript">
12630  */
12631  
12632 /**
12633  * @class Roo.tree.TreeNodeUI
12634  * @constructor
12635  * @param {Object} node The node to render
12636  * The TreeNode UI implementation is separate from the
12637  * tree implementation. Unless you are customizing the tree UI,
12638  * you should never have to use this directly.
12639  */
12640 Roo.tree.TreeNodeUI = function(node){
12641     this.node = node;
12642     this.rendered = false;
12643     this.animating = false;
12644     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12645 };
12646
12647 Roo.tree.TreeNodeUI.prototype = {
12648     removeChild : function(node){
12649         if(this.rendered){
12650             this.ctNode.removeChild(node.ui.getEl());
12651         }
12652     },
12653
12654     beforeLoad : function(){
12655          this.addClass("x-tree-node-loading");
12656     },
12657
12658     afterLoad : function(){
12659          this.removeClass("x-tree-node-loading");
12660     },
12661
12662     onTextChange : function(node, text, oldText){
12663         if(this.rendered){
12664             this.textNode.innerHTML = text;
12665         }
12666     },
12667
12668     onDisableChange : function(node, state){
12669         this.disabled = state;
12670         if(state){
12671             this.addClass("x-tree-node-disabled");
12672         }else{
12673             this.removeClass("x-tree-node-disabled");
12674         }
12675     },
12676
12677     onSelectedChange : function(state){
12678         if(state){
12679             this.focus();
12680             this.addClass("x-tree-selected");
12681         }else{
12682             //this.blur();
12683             this.removeClass("x-tree-selected");
12684         }
12685     },
12686
12687     onMove : function(tree, node, oldParent, newParent, index, refNode){
12688         this.childIndent = null;
12689         if(this.rendered){
12690             var targetNode = newParent.ui.getContainer();
12691             if(!targetNode){//target not rendered
12692                 this.holder = document.createElement("div");
12693                 this.holder.appendChild(this.wrap);
12694                 return;
12695             }
12696             var insertBefore = refNode ? refNode.ui.getEl() : null;
12697             if(insertBefore){
12698                 targetNode.insertBefore(this.wrap, insertBefore);
12699             }else{
12700                 targetNode.appendChild(this.wrap);
12701             }
12702             this.node.renderIndent(true);
12703         }
12704     },
12705
12706     addClass : function(cls){
12707         if(this.elNode){
12708             Roo.fly(this.elNode).addClass(cls);
12709         }
12710     },
12711
12712     removeClass : function(cls){
12713         if(this.elNode){
12714             Roo.fly(this.elNode).removeClass(cls);
12715         }
12716     },
12717
12718     remove : function(){
12719         if(this.rendered){
12720             this.holder = document.createElement("div");
12721             this.holder.appendChild(this.wrap);
12722         }
12723     },
12724
12725     fireEvent : function(){
12726         return this.node.fireEvent.apply(this.node, arguments);
12727     },
12728
12729     initEvents : function(){
12730         this.node.on("move", this.onMove, this);
12731         var E = Roo.EventManager;
12732         var a = this.anchor;
12733
12734         var el = Roo.fly(a, '_treeui');
12735
12736         if(Roo.isOpera){ // opera render bug ignores the CSS
12737             el.setStyle("text-decoration", "none");
12738         }
12739
12740         el.on("click", this.onClick, this);
12741         el.on("dblclick", this.onDblClick, this);
12742
12743         if(this.checkbox){
12744             Roo.EventManager.on(this.checkbox,
12745                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12746         }
12747
12748         el.on("contextmenu", this.onContextMenu, this);
12749
12750         var icon = Roo.fly(this.iconNode);
12751         icon.on("click", this.onClick, this);
12752         icon.on("dblclick", this.onDblClick, this);
12753         icon.on("contextmenu", this.onContextMenu, this);
12754         E.on(this.ecNode, "click", this.ecClick, this, true);
12755
12756         if(this.node.disabled){
12757             this.addClass("x-tree-node-disabled");
12758         }
12759         if(this.node.hidden){
12760             this.addClass("x-tree-node-disabled");
12761         }
12762         var ot = this.node.getOwnerTree();
12763         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12764         if(dd && (!this.node.isRoot || ot.rootVisible)){
12765             Roo.dd.Registry.register(this.elNode, {
12766                 node: this.node,
12767                 handles: this.getDDHandles(),
12768                 isHandle: false
12769             });
12770         }
12771     },
12772
12773     getDDHandles : function(){
12774         return [this.iconNode, this.textNode];
12775     },
12776
12777     hide : function(){
12778         if(this.rendered){
12779             this.wrap.style.display = "none";
12780         }
12781     },
12782
12783     show : function(){
12784         if(this.rendered){
12785             this.wrap.style.display = "";
12786         }
12787     },
12788
12789     onContextMenu : function(e){
12790         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12791             e.preventDefault();
12792             this.focus();
12793             this.fireEvent("contextmenu", this.node, e);
12794         }
12795     },
12796
12797     onClick : function(e){
12798         if(this.dropping){
12799             e.stopEvent();
12800             return;
12801         }
12802         if(this.fireEvent("beforeclick", this.node, e) !== false){
12803             if(!this.disabled && this.node.attributes.href){
12804                 this.fireEvent("click", this.node, e);
12805                 return;
12806             }
12807             e.preventDefault();
12808             if(this.disabled){
12809                 return;
12810             }
12811
12812             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12813                 this.node.toggle();
12814             }
12815
12816             this.fireEvent("click", this.node, e);
12817         }else{
12818             e.stopEvent();
12819         }
12820     },
12821
12822     onDblClick : function(e){
12823         e.preventDefault();
12824         if(this.disabled){
12825             return;
12826         }
12827         if(this.checkbox){
12828             this.toggleCheck();
12829         }
12830         if(!this.animating && this.node.hasChildNodes()){
12831             this.node.toggle();
12832         }
12833         this.fireEvent("dblclick", this.node, e);
12834     },
12835
12836     onCheckChange : function(){
12837         var checked = this.checkbox.checked;
12838         this.node.attributes.checked = checked;
12839         this.fireEvent('checkchange', this.node, checked);
12840     },
12841
12842     ecClick : function(e){
12843         if(!this.animating && this.node.hasChildNodes()){
12844             this.node.toggle();
12845         }
12846     },
12847
12848     startDrop : function(){
12849         this.dropping = true;
12850     },
12851
12852     // delayed drop so the click event doesn't get fired on a drop
12853     endDrop : function(){
12854        setTimeout(function(){
12855            this.dropping = false;
12856        }.createDelegate(this), 50);
12857     },
12858
12859     expand : function(){
12860         this.updateExpandIcon();
12861         this.ctNode.style.display = "";
12862     },
12863
12864     focus : function(){
12865         if(!this.node.preventHScroll){
12866             try{this.anchor.focus();
12867             }catch(e){}
12868         }else if(!Roo.isIE){
12869             try{
12870                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12871                 var l = noscroll.scrollLeft;
12872                 this.anchor.focus();
12873                 noscroll.scrollLeft = l;
12874             }catch(e){}
12875         }
12876     },
12877
12878     toggleCheck : function(value){
12879         var cb = this.checkbox;
12880         if(cb){
12881             cb.checked = (value === undefined ? !cb.checked : value);
12882         }
12883     },
12884
12885     blur : function(){
12886         try{
12887             this.anchor.blur();
12888         }catch(e){}
12889     },
12890
12891     animExpand : function(callback){
12892         var ct = Roo.get(this.ctNode);
12893         ct.stopFx();
12894         if(!this.node.hasChildNodes()){
12895             this.updateExpandIcon();
12896             this.ctNode.style.display = "";
12897             Roo.callback(callback);
12898             return;
12899         }
12900         this.animating = true;
12901         this.updateExpandIcon();
12902
12903         ct.slideIn('t', {
12904            callback : function(){
12905                this.animating = false;
12906                Roo.callback(callback);
12907             },
12908             scope: this,
12909             duration: this.node.ownerTree.duration || .25
12910         });
12911     },
12912
12913     highlight : function(){
12914         var tree = this.node.getOwnerTree();
12915         Roo.fly(this.wrap).highlight(
12916             tree.hlColor || "C3DAF9",
12917             {endColor: tree.hlBaseColor}
12918         );
12919     },
12920
12921     collapse : function(){
12922         this.updateExpandIcon();
12923         this.ctNode.style.display = "none";
12924     },
12925
12926     animCollapse : function(callback){
12927         var ct = Roo.get(this.ctNode);
12928         ct.enableDisplayMode('block');
12929         ct.stopFx();
12930
12931         this.animating = true;
12932         this.updateExpandIcon();
12933
12934         ct.slideOut('t', {
12935             callback : function(){
12936                this.animating = false;
12937                Roo.callback(callback);
12938             },
12939             scope: this,
12940             duration: this.node.ownerTree.duration || .25
12941         });
12942     },
12943
12944     getContainer : function(){
12945         return this.ctNode;
12946     },
12947
12948     getEl : function(){
12949         return this.wrap;
12950     },
12951
12952     appendDDGhost : function(ghostNode){
12953         ghostNode.appendChild(this.elNode.cloneNode(true));
12954     },
12955
12956     getDDRepairXY : function(){
12957         return Roo.lib.Dom.getXY(this.iconNode);
12958     },
12959
12960     onRender : function(){
12961         this.render();
12962     },
12963
12964     render : function(bulkRender){
12965         var n = this.node, a = n.attributes;
12966         var targetNode = n.parentNode ?
12967               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12968
12969         if(!this.rendered){
12970             this.rendered = true;
12971
12972             this.renderElements(n, a, targetNode, bulkRender);
12973
12974             if(a.qtip){
12975                if(this.textNode.setAttributeNS){
12976                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12977                    if(a.qtipTitle){
12978                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12979                    }
12980                }else{
12981                    this.textNode.setAttribute("ext:qtip", a.qtip);
12982                    if(a.qtipTitle){
12983                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12984                    }
12985                }
12986             }else if(a.qtipCfg){
12987                 a.qtipCfg.target = Roo.id(this.textNode);
12988                 Roo.QuickTips.register(a.qtipCfg);
12989             }
12990             this.initEvents();
12991             if(!this.node.expanded){
12992                 this.updateExpandIcon();
12993             }
12994         }else{
12995             if(bulkRender === true) {
12996                 targetNode.appendChild(this.wrap);
12997             }
12998         }
12999     },
13000
13001     renderElements : function(n, a, targetNode, bulkRender)
13002     {
13003         // add some indent caching, this helps performance when rendering a large tree
13004         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13005         var t = n.getOwnerTree();
13006         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13007         if (typeof(n.attributes.html) != 'undefined') {
13008             txt = n.attributes.html;
13009         }
13010         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13011         var cb = typeof a.checked == 'boolean';
13012         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13013         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13014             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13015             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13016             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13017             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13018             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13019              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13020                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13021             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13022             "</li>"];
13023
13024         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13025             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13026                                 n.nextSibling.ui.getEl(), buf.join(""));
13027         }else{
13028             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13029         }
13030
13031         this.elNode = this.wrap.childNodes[0];
13032         this.ctNode = this.wrap.childNodes[1];
13033         var cs = this.elNode.childNodes;
13034         this.indentNode = cs[0];
13035         this.ecNode = cs[1];
13036         this.iconNode = cs[2];
13037         var index = 3;
13038         if(cb){
13039             this.checkbox = cs[3];
13040             index++;
13041         }
13042         this.anchor = cs[index];
13043         this.textNode = cs[index].firstChild;
13044     },
13045
13046     getAnchor : function(){
13047         return this.anchor;
13048     },
13049
13050     getTextEl : function(){
13051         return this.textNode;
13052     },
13053
13054     getIconEl : function(){
13055         return this.iconNode;
13056     },
13057
13058     isChecked : function(){
13059         return this.checkbox ? this.checkbox.checked : false;
13060     },
13061
13062     updateExpandIcon : function(){
13063         if(this.rendered){
13064             var n = this.node, c1, c2;
13065             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13066             var hasChild = n.hasChildNodes();
13067             if(hasChild){
13068                 if(n.expanded){
13069                     cls += "-minus";
13070                     c1 = "x-tree-node-collapsed";
13071                     c2 = "x-tree-node-expanded";
13072                 }else{
13073                     cls += "-plus";
13074                     c1 = "x-tree-node-expanded";
13075                     c2 = "x-tree-node-collapsed";
13076                 }
13077                 if(this.wasLeaf){
13078                     this.removeClass("x-tree-node-leaf");
13079                     this.wasLeaf = false;
13080                 }
13081                 if(this.c1 != c1 || this.c2 != c2){
13082                     Roo.fly(this.elNode).replaceClass(c1, c2);
13083                     this.c1 = c1; this.c2 = c2;
13084                 }
13085             }else{
13086                 // this changes non-leafs into leafs if they have no children.
13087                 // it's not very rational behaviour..
13088                 
13089                 if(!this.wasLeaf && this.node.leaf){
13090                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13091                     delete this.c1;
13092                     delete this.c2;
13093                     this.wasLeaf = true;
13094                 }
13095             }
13096             var ecc = "x-tree-ec-icon "+cls;
13097             if(this.ecc != ecc){
13098                 this.ecNode.className = ecc;
13099                 this.ecc = ecc;
13100             }
13101         }
13102     },
13103
13104     getChildIndent : function(){
13105         if(!this.childIndent){
13106             var buf = [];
13107             var p = this.node;
13108             while(p){
13109                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13110                     if(!p.isLast()) {
13111                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13112                     } else {
13113                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13114                     }
13115                 }
13116                 p = p.parentNode;
13117             }
13118             this.childIndent = buf.join("");
13119         }
13120         return this.childIndent;
13121     },
13122
13123     renderIndent : function(){
13124         if(this.rendered){
13125             var indent = "";
13126             var p = this.node.parentNode;
13127             if(p){
13128                 indent = p.ui.getChildIndent();
13129             }
13130             if(this.indentMarkup != indent){ // don't rerender if not required
13131                 this.indentNode.innerHTML = indent;
13132                 this.indentMarkup = indent;
13133             }
13134             this.updateExpandIcon();
13135         }
13136     }
13137 };
13138
13139 Roo.tree.RootTreeNodeUI = function(){
13140     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13141 };
13142 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13143     render : function(){
13144         if(!this.rendered){
13145             var targetNode = this.node.ownerTree.innerCt.dom;
13146             this.node.expanded = true;
13147             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13148             this.wrap = this.ctNode = targetNode.firstChild;
13149         }
13150     },
13151     collapse : function(){
13152     },
13153     expand : function(){
13154     }
13155 });/*
13156  * Based on:
13157  * Ext JS Library 1.1.1
13158  * Copyright(c) 2006-2007, Ext JS, LLC.
13159  *
13160  * Originally Released Under LGPL - original licence link has changed is not relivant.
13161  *
13162  * Fork - LGPL
13163  * <script type="text/javascript">
13164  */
13165 /**
13166  * @class Roo.tree.TreeLoader
13167  * @extends Roo.util.Observable
13168  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13169  * nodes from a specified URL. The response must be a javascript Array definition
13170  * who's elements are node definition objects. eg:
13171  * <pre><code>
13172 {  success : true,
13173    data :      [
13174    
13175     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13176     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13177     ]
13178 }
13179
13180
13181 </code></pre>
13182  * <br><br>
13183  * The old style respose with just an array is still supported, but not recommended.
13184  * <br><br>
13185  *
13186  * A server request is sent, and child nodes are loaded only when a node is expanded.
13187  * The loading node's id is passed to the server under the parameter name "node" to
13188  * enable the server to produce the correct child nodes.
13189  * <br><br>
13190  * To pass extra parameters, an event handler may be attached to the "beforeload"
13191  * event, and the parameters specified in the TreeLoader's baseParams property:
13192  * <pre><code>
13193     myTreeLoader.on("beforeload", function(treeLoader, node) {
13194         this.baseParams.category = node.attributes.category;
13195     }, this);
13196     
13197 </code></pre>
13198  *
13199  * This would pass an HTTP parameter called "category" to the server containing
13200  * the value of the Node's "category" attribute.
13201  * @constructor
13202  * Creates a new Treeloader.
13203  * @param {Object} config A config object containing config properties.
13204  */
13205 Roo.tree.TreeLoader = function(config){
13206     this.baseParams = {};
13207     this.requestMethod = "POST";
13208     Roo.apply(this, config);
13209
13210     this.addEvents({
13211     
13212         /**
13213          * @event beforeload
13214          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13215          * @param {Object} This TreeLoader object.
13216          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13217          * @param {Object} callback The callback function specified in the {@link #load} call.
13218          */
13219         beforeload : true,
13220         /**
13221          * @event load
13222          * Fires when the node has been successfuly loaded.
13223          * @param {Object} This TreeLoader object.
13224          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13225          * @param {Object} response The response object containing the data from the server.
13226          */
13227         load : true,
13228         /**
13229          * @event loadexception
13230          * Fires if the network request failed.
13231          * @param {Object} This TreeLoader object.
13232          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13233          * @param {Object} response The response object containing the data from the server.
13234          */
13235         loadexception : true,
13236         /**
13237          * @event create
13238          * Fires before a node is created, enabling you to return custom Node types 
13239          * @param {Object} This TreeLoader object.
13240          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13241          */
13242         create : true
13243     });
13244
13245     Roo.tree.TreeLoader.superclass.constructor.call(this);
13246 };
13247
13248 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13249     /**
13250     * @cfg {String} dataUrl The URL from which to request a Json string which
13251     * specifies an array of node definition object representing the child nodes
13252     * to be loaded.
13253     */
13254     /**
13255     * @cfg {String} requestMethod either GET or POST
13256     * defaults to POST (due to BC)
13257     * to be loaded.
13258     */
13259     /**
13260     * @cfg {Object} baseParams (optional) An object containing properties which
13261     * specify HTTP parameters to be passed to each request for child nodes.
13262     */
13263     /**
13264     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13265     * created by this loader. If the attributes sent by the server have an attribute in this object,
13266     * they take priority.
13267     */
13268     /**
13269     * @cfg {Object} uiProviders (optional) An object containing properties which
13270     * 
13271     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13272     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13273     * <i>uiProvider</i> attribute of a returned child node is a string rather
13274     * than a reference to a TreeNodeUI implementation, this that string value
13275     * is used as a property name in the uiProviders object. You can define the provider named
13276     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13277     */
13278     uiProviders : {},
13279
13280     /**
13281     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13282     * child nodes before loading.
13283     */
13284     clearOnLoad : true,
13285
13286     /**
13287     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13288     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13289     * Grid query { data : [ .....] }
13290     */
13291     
13292     root : false,
13293      /**
13294     * @cfg {String} queryParam (optional) 
13295     * Name of the query as it will be passed on the querystring (defaults to 'node')
13296     * eg. the request will be ?node=[id]
13297     */
13298     
13299     
13300     queryParam: false,
13301     
13302     /**
13303      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13304      * This is called automatically when a node is expanded, but may be used to reload
13305      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13306      * @param {Roo.tree.TreeNode} node
13307      * @param {Function} callback
13308      */
13309     load : function(node, callback){
13310         if(this.clearOnLoad){
13311             while(node.firstChild){
13312                 node.removeChild(node.firstChild);
13313             }
13314         }
13315         if(node.attributes.children){ // preloaded json children
13316             var cs = node.attributes.children;
13317             for(var i = 0, len = cs.length; i < len; i++){
13318                 node.appendChild(this.createNode(cs[i]));
13319             }
13320             if(typeof callback == "function"){
13321                 callback();
13322             }
13323         }else if(this.dataUrl){
13324             this.requestData(node, callback);
13325         }
13326     },
13327
13328     getParams: function(node){
13329         var buf = [], bp = this.baseParams;
13330         for(var key in bp){
13331             if(typeof bp[key] != "function"){
13332                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13333             }
13334         }
13335         var n = this.queryParam === false ? 'node' : this.queryParam;
13336         buf.push(n + "=", encodeURIComponent(node.id));
13337         return buf.join("");
13338     },
13339
13340     requestData : function(node, callback){
13341         if(this.fireEvent("beforeload", this, node, callback) !== false){
13342             this.transId = Roo.Ajax.request({
13343                 method:this.requestMethod,
13344                 url: this.dataUrl||this.url,
13345                 success: this.handleResponse,
13346                 failure: this.handleFailure,
13347                 scope: this,
13348                 argument: {callback: callback, node: node},
13349                 params: this.getParams(node)
13350             });
13351         }else{
13352             // if the load is cancelled, make sure we notify
13353             // the node that we are done
13354             if(typeof callback == "function"){
13355                 callback();
13356             }
13357         }
13358     },
13359
13360     isLoading : function(){
13361         return this.transId ? true : false;
13362     },
13363
13364     abort : function(){
13365         if(this.isLoading()){
13366             Roo.Ajax.abort(this.transId);
13367         }
13368     },
13369
13370     // private
13371     createNode : function(attr)
13372     {
13373         // apply baseAttrs, nice idea Corey!
13374         if(this.baseAttrs){
13375             Roo.applyIf(attr, this.baseAttrs);
13376         }
13377         if(this.applyLoader !== false){
13378             attr.loader = this;
13379         }
13380         // uiProvider = depreciated..
13381         
13382         if(typeof(attr.uiProvider) == 'string'){
13383            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13384                 /**  eval:var:attr */ eval(attr.uiProvider);
13385         }
13386         if(typeof(this.uiProviders['default']) != 'undefined') {
13387             attr.uiProvider = this.uiProviders['default'];
13388         }
13389         
13390         this.fireEvent('create', this, attr);
13391         
13392         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13393         return(attr.leaf ?
13394                         new Roo.tree.TreeNode(attr) :
13395                         new Roo.tree.AsyncTreeNode(attr));
13396     },
13397
13398     processResponse : function(response, node, callback)
13399     {
13400         var json = response.responseText;
13401         try {
13402             
13403             var o = Roo.decode(json);
13404             
13405             if (this.root === false && typeof(o.success) != undefined) {
13406                 this.root = 'data'; // the default behaviour for list like data..
13407                 }
13408                 
13409             if (this.root !== false &&  !o.success) {
13410                 // it's a failure condition.
13411                 var a = response.argument;
13412                 this.fireEvent("loadexception", this, a.node, response);
13413                 Roo.log("Load failed - should have a handler really");
13414                 return;
13415             }
13416             
13417             
13418             
13419             if (this.root !== false) {
13420                  o = o[this.root];
13421             }
13422             
13423             for(var i = 0, len = o.length; i < len; i++){
13424                 var n = this.createNode(o[i]);
13425                 if(n){
13426                     node.appendChild(n);
13427                 }
13428             }
13429             if(typeof callback == "function"){
13430                 callback(this, node);
13431             }
13432         }catch(e){
13433             this.handleFailure(response);
13434         }
13435     },
13436
13437     handleResponse : function(response){
13438         this.transId = false;
13439         var a = response.argument;
13440         this.processResponse(response, a.node, a.callback);
13441         this.fireEvent("load", this, a.node, response);
13442     },
13443
13444     handleFailure : function(response)
13445     {
13446         // should handle failure better..
13447         this.transId = false;
13448         var a = response.argument;
13449         this.fireEvent("loadexception", this, a.node, response);
13450         if(typeof a.callback == "function"){
13451             a.callback(this, a.node);
13452         }
13453     }
13454 });/*
13455  * Based on:
13456  * Ext JS Library 1.1.1
13457  * Copyright(c) 2006-2007, Ext JS, LLC.
13458  *
13459  * Originally Released Under LGPL - original licence link has changed is not relivant.
13460  *
13461  * Fork - LGPL
13462  * <script type="text/javascript">
13463  */
13464
13465 /**
13466 * @class Roo.tree.TreeFilter
13467 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13468 * @param {TreePanel} tree
13469 * @param {Object} config (optional)
13470  */
13471 Roo.tree.TreeFilter = function(tree, config){
13472     this.tree = tree;
13473     this.filtered = {};
13474     Roo.apply(this, config);
13475 };
13476
13477 Roo.tree.TreeFilter.prototype = {
13478     clearBlank:false,
13479     reverse:false,
13480     autoClear:false,
13481     remove:false,
13482
13483      /**
13484      * Filter the data by a specific attribute.
13485      * @param {String/RegExp} value Either string that the attribute value
13486      * should start with or a RegExp to test against the attribute
13487      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13488      * @param {TreeNode} startNode (optional) The node to start the filter at.
13489      */
13490     filter : function(value, attr, startNode){
13491         attr = attr || "text";
13492         var f;
13493         if(typeof value == "string"){
13494             var vlen = value.length;
13495             // auto clear empty filter
13496             if(vlen == 0 && this.clearBlank){
13497                 this.clear();
13498                 return;
13499             }
13500             value = value.toLowerCase();
13501             f = function(n){
13502                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13503             };
13504         }else if(value.exec){ // regex?
13505             f = function(n){
13506                 return value.test(n.attributes[attr]);
13507             };
13508         }else{
13509             throw 'Illegal filter type, must be string or regex';
13510         }
13511         this.filterBy(f, null, startNode);
13512         },
13513
13514     /**
13515      * Filter by a function. The passed function will be called with each
13516      * node in the tree (or from the startNode). If the function returns true, the node is kept
13517      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13518      * @param {Function} fn The filter function
13519      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13520      */
13521     filterBy : function(fn, scope, startNode){
13522         startNode = startNode || this.tree.root;
13523         if(this.autoClear){
13524             this.clear();
13525         }
13526         var af = this.filtered, rv = this.reverse;
13527         var f = function(n){
13528             if(n == startNode){
13529                 return true;
13530             }
13531             if(af[n.id]){
13532                 return false;
13533             }
13534             var m = fn.call(scope || n, n);
13535             if(!m || rv){
13536                 af[n.id] = n;
13537                 n.ui.hide();
13538                 return false;
13539             }
13540             return true;
13541         };
13542         startNode.cascade(f);
13543         if(this.remove){
13544            for(var id in af){
13545                if(typeof id != "function"){
13546                    var n = af[id];
13547                    if(n && n.parentNode){
13548                        n.parentNode.removeChild(n);
13549                    }
13550                }
13551            }
13552         }
13553     },
13554
13555     /**
13556      * Clears the current filter. Note: with the "remove" option
13557      * set a filter cannot be cleared.
13558      */
13559     clear : function(){
13560         var t = this.tree;
13561         var af = this.filtered;
13562         for(var id in af){
13563             if(typeof id != "function"){
13564                 var n = af[id];
13565                 if(n){
13566                     n.ui.show();
13567                 }
13568             }
13569         }
13570         this.filtered = {};
13571     }
13572 };
13573 /*
13574  * Based on:
13575  * Ext JS Library 1.1.1
13576  * Copyright(c) 2006-2007, Ext JS, LLC.
13577  *
13578  * Originally Released Under LGPL - original licence link has changed is not relivant.
13579  *
13580  * Fork - LGPL
13581  * <script type="text/javascript">
13582  */
13583  
13584
13585 /**
13586  * @class Roo.tree.TreeSorter
13587  * Provides sorting of nodes in a TreePanel
13588  * 
13589  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13590  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13591  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13592  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13593  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13594  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13595  * @constructor
13596  * @param {TreePanel} tree
13597  * @param {Object} config
13598  */
13599 Roo.tree.TreeSorter = function(tree, config){
13600     Roo.apply(this, config);
13601     tree.on("beforechildrenrendered", this.doSort, this);
13602     tree.on("append", this.updateSort, this);
13603     tree.on("insert", this.updateSort, this);
13604     
13605     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13606     var p = this.property || "text";
13607     var sortType = this.sortType;
13608     var fs = this.folderSort;
13609     var cs = this.caseSensitive === true;
13610     var leafAttr = this.leafAttr || 'leaf';
13611
13612     this.sortFn = function(n1, n2){
13613         if(fs){
13614             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13615                 return 1;
13616             }
13617             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13618                 return -1;
13619             }
13620         }
13621         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13622         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13623         if(v1 < v2){
13624                         return dsc ? +1 : -1;
13625                 }else if(v1 > v2){
13626                         return dsc ? -1 : +1;
13627         }else{
13628                 return 0;
13629         }
13630     };
13631 };
13632
13633 Roo.tree.TreeSorter.prototype = {
13634     doSort : function(node){
13635         node.sort(this.sortFn);
13636     },
13637     
13638     compareNodes : function(n1, n2){
13639         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13640     },
13641     
13642     updateSort : function(tree, node){
13643         if(node.childrenRendered){
13644             this.doSort.defer(1, this, [node]);
13645         }
13646     }
13647 };/*
13648  * Based on:
13649  * Ext JS Library 1.1.1
13650  * Copyright(c) 2006-2007, Ext JS, LLC.
13651  *
13652  * Originally Released Under LGPL - original licence link has changed is not relivant.
13653  *
13654  * Fork - LGPL
13655  * <script type="text/javascript">
13656  */
13657
13658 if(Roo.dd.DropZone){
13659     
13660 Roo.tree.TreeDropZone = function(tree, config){
13661     this.allowParentInsert = false;
13662     this.allowContainerDrop = false;
13663     this.appendOnly = false;
13664     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13665     this.tree = tree;
13666     this.lastInsertClass = "x-tree-no-status";
13667     this.dragOverData = {};
13668 };
13669
13670 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13671     ddGroup : "TreeDD",
13672     scroll:  true,
13673     
13674     expandDelay : 1000,
13675     
13676     expandNode : function(node){
13677         if(node.hasChildNodes() && !node.isExpanded()){
13678             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13679         }
13680     },
13681     
13682     queueExpand : function(node){
13683         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13684     },
13685     
13686     cancelExpand : function(){
13687         if(this.expandProcId){
13688             clearTimeout(this.expandProcId);
13689             this.expandProcId = false;
13690         }
13691     },
13692     
13693     isValidDropPoint : function(n, pt, dd, e, data){
13694         if(!n || !data){ return false; }
13695         var targetNode = n.node;
13696         var dropNode = data.node;
13697         // default drop rules
13698         if(!(targetNode && targetNode.isTarget && pt)){
13699             return false;
13700         }
13701         if(pt == "append" && targetNode.allowChildren === false){
13702             return false;
13703         }
13704         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13705             return false;
13706         }
13707         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13708             return false;
13709         }
13710         // reuse the object
13711         var overEvent = this.dragOverData;
13712         overEvent.tree = this.tree;
13713         overEvent.target = targetNode;
13714         overEvent.data = data;
13715         overEvent.point = pt;
13716         overEvent.source = dd;
13717         overEvent.rawEvent = e;
13718         overEvent.dropNode = dropNode;
13719         overEvent.cancel = false;  
13720         var result = this.tree.fireEvent("nodedragover", overEvent);
13721         return overEvent.cancel === false && result !== false;
13722     },
13723     
13724     getDropPoint : function(e, n, dd)
13725     {
13726         var tn = n.node;
13727         if(tn.isRoot){
13728             return tn.allowChildren !== false ? "append" : false; // always append for root
13729         }
13730         var dragEl = n.ddel;
13731         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13732         var y = Roo.lib.Event.getPageY(e);
13733         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13734         
13735         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13736         var noAppend = tn.allowChildren === false;
13737         if(this.appendOnly || tn.parentNode.allowChildren === false){
13738             return noAppend ? false : "append";
13739         }
13740         var noBelow = false;
13741         if(!this.allowParentInsert){
13742             noBelow = tn.hasChildNodes() && tn.isExpanded();
13743         }
13744         var q = (b - t) / (noAppend ? 2 : 3);
13745         if(y >= t && y < (t + q)){
13746             return "above";
13747         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13748             return "below";
13749         }else{
13750             return "append";
13751         }
13752     },
13753     
13754     onNodeEnter : function(n, dd, e, data)
13755     {
13756         this.cancelExpand();
13757     },
13758     
13759     onNodeOver : function(n, dd, e, data)
13760     {
13761        
13762         var pt = this.getDropPoint(e, n, dd);
13763         var node = n.node;
13764         
13765         // auto node expand check
13766         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13767             this.queueExpand(node);
13768         }else if(pt != "append"){
13769             this.cancelExpand();
13770         }
13771         
13772         // set the insert point style on the target node
13773         var returnCls = this.dropNotAllowed;
13774         if(this.isValidDropPoint(n, pt, dd, e, data)){
13775            if(pt){
13776                var el = n.ddel;
13777                var cls;
13778                if(pt == "above"){
13779                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13780                    cls = "x-tree-drag-insert-above";
13781                }else if(pt == "below"){
13782                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13783                    cls = "x-tree-drag-insert-below";
13784                }else{
13785                    returnCls = "x-tree-drop-ok-append";
13786                    cls = "x-tree-drag-append";
13787                }
13788                if(this.lastInsertClass != cls){
13789                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13790                    this.lastInsertClass = cls;
13791                }
13792            }
13793        }
13794        return returnCls;
13795     },
13796     
13797     onNodeOut : function(n, dd, e, data){
13798         
13799         this.cancelExpand();
13800         this.removeDropIndicators(n);
13801     },
13802     
13803     onNodeDrop : function(n, dd, e, data){
13804         var point = this.getDropPoint(e, n, dd);
13805         var targetNode = n.node;
13806         targetNode.ui.startDrop();
13807         if(!this.isValidDropPoint(n, point, dd, e, data)){
13808             targetNode.ui.endDrop();
13809             return false;
13810         }
13811         // first try to find the drop node
13812         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13813         var dropEvent = {
13814             tree : this.tree,
13815             target: targetNode,
13816             data: data,
13817             point: point,
13818             source: dd,
13819             rawEvent: e,
13820             dropNode: dropNode,
13821             cancel: !dropNode   
13822         };
13823         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13824         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13825             targetNode.ui.endDrop();
13826             return false;
13827         }
13828         // allow target changing
13829         targetNode = dropEvent.target;
13830         if(point == "append" && !targetNode.isExpanded()){
13831             targetNode.expand(false, null, function(){
13832                 this.completeDrop(dropEvent);
13833             }.createDelegate(this));
13834         }else{
13835             this.completeDrop(dropEvent);
13836         }
13837         return true;
13838     },
13839     
13840     completeDrop : function(de){
13841         var ns = de.dropNode, p = de.point, t = de.target;
13842         if(!(ns instanceof Array)){
13843             ns = [ns];
13844         }
13845         var n;
13846         for(var i = 0, len = ns.length; i < len; i++){
13847             n = ns[i];
13848             if(p == "above"){
13849                 t.parentNode.insertBefore(n, t);
13850             }else if(p == "below"){
13851                 t.parentNode.insertBefore(n, t.nextSibling);
13852             }else{
13853                 t.appendChild(n);
13854             }
13855         }
13856         n.ui.focus();
13857         if(this.tree.hlDrop){
13858             n.ui.highlight();
13859         }
13860         t.ui.endDrop();
13861         this.tree.fireEvent("nodedrop", de);
13862     },
13863     
13864     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13865         if(this.tree.hlDrop){
13866             dropNode.ui.focus();
13867             dropNode.ui.highlight();
13868         }
13869         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13870     },
13871     
13872     getTree : function(){
13873         return this.tree;
13874     },
13875     
13876     removeDropIndicators : function(n){
13877         if(n && n.ddel){
13878             var el = n.ddel;
13879             Roo.fly(el).removeClass([
13880                     "x-tree-drag-insert-above",
13881                     "x-tree-drag-insert-below",
13882                     "x-tree-drag-append"]);
13883             this.lastInsertClass = "_noclass";
13884         }
13885     },
13886     
13887     beforeDragDrop : function(target, e, id){
13888         this.cancelExpand();
13889         return true;
13890     },
13891     
13892     afterRepair : function(data){
13893         if(data && Roo.enableFx){
13894             data.node.ui.highlight();
13895         }
13896         this.hideProxy();
13897     } 
13898     
13899 });
13900
13901 }
13902 /*
13903  * Based on:
13904  * Ext JS Library 1.1.1
13905  * Copyright(c) 2006-2007, Ext JS, LLC.
13906  *
13907  * Originally Released Under LGPL - original licence link has changed is not relivant.
13908  *
13909  * Fork - LGPL
13910  * <script type="text/javascript">
13911  */
13912  
13913
13914 if(Roo.dd.DragZone){
13915 Roo.tree.TreeDragZone = function(tree, config){
13916     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13917     this.tree = tree;
13918 };
13919
13920 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13921     ddGroup : "TreeDD",
13922    
13923     onBeforeDrag : function(data, e){
13924         var n = data.node;
13925         return n && n.draggable && !n.disabled;
13926     },
13927      
13928     
13929     onInitDrag : function(e){
13930         var data = this.dragData;
13931         this.tree.getSelectionModel().select(data.node);
13932         this.proxy.update("");
13933         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13934         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13935     },
13936     
13937     getRepairXY : function(e, data){
13938         return data.node.ui.getDDRepairXY();
13939     },
13940     
13941     onEndDrag : function(data, e){
13942         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13943         
13944         
13945     },
13946     
13947     onValidDrop : function(dd, e, id){
13948         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13949         this.hideProxy();
13950     },
13951     
13952     beforeInvalidDrop : function(e, id){
13953         // this scrolls the original position back into view
13954         var sm = this.tree.getSelectionModel();
13955         sm.clearSelections();
13956         sm.select(this.dragData.node);
13957     }
13958 });
13959 }/*
13960  * Based on:
13961  * Ext JS Library 1.1.1
13962  * Copyright(c) 2006-2007, Ext JS, LLC.
13963  *
13964  * Originally Released Under LGPL - original licence link has changed is not relivant.
13965  *
13966  * Fork - LGPL
13967  * <script type="text/javascript">
13968  */
13969 /**
13970  * @class Roo.tree.TreeEditor
13971  * @extends Roo.Editor
13972  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13973  * as the editor field.
13974  * @constructor
13975  * @param {Object} config (used to be the tree panel.)
13976  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13977  * 
13978  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13979  * @cfg {Roo.form.TextField|Object} field The field configuration
13980  *
13981  * 
13982  */
13983 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13984     var tree = config;
13985     var field;
13986     if (oldconfig) { // old style..
13987         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13988     } else {
13989         // new style..
13990         tree = config.tree;
13991         config.field = config.field  || {};
13992         config.field.xtype = 'TextField';
13993         field = Roo.factory(config.field, Roo.form);
13994     }
13995     config = config || {};
13996     
13997     
13998     this.addEvents({
13999         /**
14000          * @event beforenodeedit
14001          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14002          * false from the handler of this event.
14003          * @param {Editor} this
14004          * @param {Roo.tree.Node} node 
14005          */
14006         "beforenodeedit" : true
14007     });
14008     
14009     //Roo.log(config);
14010     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14011
14012     this.tree = tree;
14013
14014     tree.on('beforeclick', this.beforeNodeClick, this);
14015     tree.getTreeEl().on('mousedown', this.hide, this);
14016     this.on('complete', this.updateNode, this);
14017     this.on('beforestartedit', this.fitToTree, this);
14018     this.on('startedit', this.bindScroll, this, {delay:10});
14019     this.on('specialkey', this.onSpecialKey, this);
14020 };
14021
14022 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14023     /**
14024      * @cfg {String} alignment
14025      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14026      */
14027     alignment: "l-l",
14028     // inherit
14029     autoSize: false,
14030     /**
14031      * @cfg {Boolean} hideEl
14032      * True to hide the bound element while the editor is displayed (defaults to false)
14033      */
14034     hideEl : false,
14035     /**
14036      * @cfg {String} cls
14037      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14038      */
14039     cls: "x-small-editor x-tree-editor",
14040     /**
14041      * @cfg {Boolean} shim
14042      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14043      */
14044     shim:false,
14045     // inherit
14046     shadow:"frame",
14047     /**
14048      * @cfg {Number} maxWidth
14049      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14050      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14051      * scroll and client offsets into account prior to each edit.
14052      */
14053     maxWidth: 250,
14054
14055     editDelay : 350,
14056
14057     // private
14058     fitToTree : function(ed, el){
14059         var td = this.tree.getTreeEl().dom, nd = el.dom;
14060         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14061             td.scrollLeft = nd.offsetLeft;
14062         }
14063         var w = Math.min(
14064                 this.maxWidth,
14065                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14066         this.setSize(w, '');
14067         
14068         return this.fireEvent('beforenodeedit', this, this.editNode);
14069         
14070     },
14071
14072     // private
14073     triggerEdit : function(node){
14074         this.completeEdit();
14075         this.editNode = node;
14076         this.startEdit(node.ui.textNode, node.text);
14077     },
14078
14079     // private
14080     bindScroll : function(){
14081         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14082     },
14083
14084     // private
14085     beforeNodeClick : function(node, e){
14086         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14087         this.lastClick = new Date();
14088         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14089             e.stopEvent();
14090             this.triggerEdit(node);
14091             return false;
14092         }
14093         return true;
14094     },
14095
14096     // private
14097     updateNode : function(ed, value){
14098         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14099         this.editNode.setText(value);
14100     },
14101
14102     // private
14103     onHide : function(){
14104         Roo.tree.TreeEditor.superclass.onHide.call(this);
14105         if(this.editNode){
14106             this.editNode.ui.focus();
14107         }
14108     },
14109
14110     // private
14111     onSpecialKey : function(field, e){
14112         var k = e.getKey();
14113         if(k == e.ESC){
14114             e.stopEvent();
14115             this.cancelEdit();
14116         }else if(k == e.ENTER && !e.hasModifier()){
14117             e.stopEvent();
14118             this.completeEdit();
14119         }
14120     }
14121 });//<Script type="text/javascript">
14122 /*
14123  * Based on:
14124  * Ext JS Library 1.1.1
14125  * Copyright(c) 2006-2007, Ext JS, LLC.
14126  *
14127  * Originally Released Under LGPL - original licence link has changed is not relivant.
14128  *
14129  * Fork - LGPL
14130  * <script type="text/javascript">
14131  */
14132  
14133 /**
14134  * Not documented??? - probably should be...
14135  */
14136
14137 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14138     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14139     
14140     renderElements : function(n, a, targetNode, bulkRender){
14141         //consel.log("renderElements?");
14142         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14143
14144         var t = n.getOwnerTree();
14145         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14146         
14147         var cols = t.columns;
14148         var bw = t.borderWidth;
14149         var c = cols[0];
14150         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14151          var cb = typeof a.checked == "boolean";
14152         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14153         var colcls = 'x-t-' + tid + '-c0';
14154         var buf = [
14155             '<li class="x-tree-node">',
14156             
14157                 
14158                 '<div class="x-tree-node-el ', a.cls,'">',
14159                     // extran...
14160                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14161                 
14162                 
14163                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14164                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14165                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14166                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14167                            (a.iconCls ? ' '+a.iconCls : ''),
14168                            '" unselectable="on" />',
14169                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14170                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14171                              
14172                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14173                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14174                             '<span unselectable="on" qtip="' + tx + '">',
14175                              tx,
14176                              '</span></a>' ,
14177                     '</div>',
14178                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14179                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14180                  ];
14181         for(var i = 1, len = cols.length; i < len; i++){
14182             c = cols[i];
14183             colcls = 'x-t-' + tid + '-c' +i;
14184             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14185             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14186                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14187                       "</div>");
14188          }
14189          
14190          buf.push(
14191             '</a>',
14192             '<div class="x-clear"></div></div>',
14193             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14194             "</li>");
14195         
14196         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14197             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14198                                 n.nextSibling.ui.getEl(), buf.join(""));
14199         }else{
14200             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14201         }
14202         var el = this.wrap.firstChild;
14203         this.elRow = el;
14204         this.elNode = el.firstChild;
14205         this.ranchor = el.childNodes[1];
14206         this.ctNode = this.wrap.childNodes[1];
14207         var cs = el.firstChild.childNodes;
14208         this.indentNode = cs[0];
14209         this.ecNode = cs[1];
14210         this.iconNode = cs[2];
14211         var index = 3;
14212         if(cb){
14213             this.checkbox = cs[3];
14214             index++;
14215         }
14216         this.anchor = cs[index];
14217         
14218         this.textNode = cs[index].firstChild;
14219         
14220         //el.on("click", this.onClick, this);
14221         //el.on("dblclick", this.onDblClick, this);
14222         
14223         
14224        // console.log(this);
14225     },
14226     initEvents : function(){
14227         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14228         
14229             
14230         var a = this.ranchor;
14231
14232         var el = Roo.get(a);
14233
14234         if(Roo.isOpera){ // opera render bug ignores the CSS
14235             el.setStyle("text-decoration", "none");
14236         }
14237
14238         el.on("click", this.onClick, this);
14239         el.on("dblclick", this.onDblClick, this);
14240         el.on("contextmenu", this.onContextMenu, this);
14241         
14242     },
14243     
14244     /*onSelectedChange : function(state){
14245         if(state){
14246             this.focus();
14247             this.addClass("x-tree-selected");
14248         }else{
14249             //this.blur();
14250             this.removeClass("x-tree-selected");
14251         }
14252     },*/
14253     addClass : function(cls){
14254         if(this.elRow){
14255             Roo.fly(this.elRow).addClass(cls);
14256         }
14257         
14258     },
14259     
14260     
14261     removeClass : function(cls){
14262         if(this.elRow){
14263             Roo.fly(this.elRow).removeClass(cls);
14264         }
14265     }
14266
14267     
14268     
14269 });//<Script type="text/javascript">
14270
14271 /*
14272  * Based on:
14273  * Ext JS Library 1.1.1
14274  * Copyright(c) 2006-2007, Ext JS, LLC.
14275  *
14276  * Originally Released Under LGPL - original licence link has changed is not relivant.
14277  *
14278  * Fork - LGPL
14279  * <script type="text/javascript">
14280  */
14281  
14282
14283 /**
14284  * @class Roo.tree.ColumnTree
14285  * @extends Roo.data.TreePanel
14286  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14287  * @cfg {int} borderWidth  compined right/left border allowance
14288  * @constructor
14289  * @param {String/HTMLElement/Element} el The container element
14290  * @param {Object} config
14291  */
14292 Roo.tree.ColumnTree =  function(el, config)
14293 {
14294    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14295    this.addEvents({
14296         /**
14297         * @event resize
14298         * Fire this event on a container when it resizes
14299         * @param {int} w Width
14300         * @param {int} h Height
14301         */
14302        "resize" : true
14303     });
14304     this.on('resize', this.onResize, this);
14305 };
14306
14307 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14308     //lines:false,
14309     
14310     
14311     borderWidth: Roo.isBorderBox ? 0 : 2, 
14312     headEls : false,
14313     
14314     render : function(){
14315         // add the header.....
14316        
14317         Roo.tree.ColumnTree.superclass.render.apply(this);
14318         
14319         this.el.addClass('x-column-tree');
14320         
14321         this.headers = this.el.createChild(
14322             {cls:'x-tree-headers'},this.innerCt.dom);
14323    
14324         var cols = this.columns, c;
14325         var totalWidth = 0;
14326         this.headEls = [];
14327         var  len = cols.length;
14328         for(var i = 0; i < len; i++){
14329              c = cols[i];
14330              totalWidth += c.width;
14331             this.headEls.push(this.headers.createChild({
14332                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14333                  cn: {
14334                      cls:'x-tree-hd-text',
14335                      html: c.header
14336                  },
14337                  style:'width:'+(c.width-this.borderWidth)+'px;'
14338              }));
14339         }
14340         this.headers.createChild({cls:'x-clear'});
14341         // prevent floats from wrapping when clipped
14342         this.headers.setWidth(totalWidth);
14343         //this.innerCt.setWidth(totalWidth);
14344         this.innerCt.setStyle({ overflow: 'auto' });
14345         this.onResize(this.width, this.height);
14346              
14347         
14348     },
14349     onResize : function(w,h)
14350     {
14351         this.height = h;
14352         this.width = w;
14353         // resize cols..
14354         this.innerCt.setWidth(this.width);
14355         this.innerCt.setHeight(this.height-20);
14356         
14357         // headers...
14358         var cols = this.columns, c;
14359         var totalWidth = 0;
14360         var expEl = false;
14361         var len = cols.length;
14362         for(var i = 0; i < len; i++){
14363             c = cols[i];
14364             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14365                 // it's the expander..
14366                 expEl  = this.headEls[i];
14367                 continue;
14368             }
14369             totalWidth += c.width;
14370             
14371         }
14372         if (expEl) {
14373             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14374         }
14375         this.headers.setWidth(w-20);
14376
14377         
14378         
14379         
14380     }
14381 });
14382 /*
14383  * Based on:
14384  * Ext JS Library 1.1.1
14385  * Copyright(c) 2006-2007, Ext JS, LLC.
14386  *
14387  * Originally Released Under LGPL - original licence link has changed is not relivant.
14388  *
14389  * Fork - LGPL
14390  * <script type="text/javascript">
14391  */
14392  
14393 /**
14394  * @class Roo.menu.Menu
14395  * @extends Roo.util.Observable
14396  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14397  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14398  * @constructor
14399  * Creates a new Menu
14400  * @param {Object} config Configuration options
14401  */
14402 Roo.menu.Menu = function(config){
14403     
14404     Roo.menu.Menu.superclass.constructor.call(this, config);
14405     
14406     this.id = this.id || Roo.id();
14407     this.addEvents({
14408         /**
14409          * @event beforeshow
14410          * Fires before this menu is displayed
14411          * @param {Roo.menu.Menu} this
14412          */
14413         beforeshow : true,
14414         /**
14415          * @event beforehide
14416          * Fires before this menu is hidden
14417          * @param {Roo.menu.Menu} this
14418          */
14419         beforehide : true,
14420         /**
14421          * @event show
14422          * Fires after this menu is displayed
14423          * @param {Roo.menu.Menu} this
14424          */
14425         show : true,
14426         /**
14427          * @event hide
14428          * Fires after this menu is hidden
14429          * @param {Roo.menu.Menu} this
14430          */
14431         hide : true,
14432         /**
14433          * @event click
14434          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14435          * @param {Roo.menu.Menu} this
14436          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14437          * @param {Roo.EventObject} e
14438          */
14439         click : true,
14440         /**
14441          * @event mouseover
14442          * Fires when the mouse is hovering over this menu
14443          * @param {Roo.menu.Menu} this
14444          * @param {Roo.EventObject} e
14445          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14446          */
14447         mouseover : true,
14448         /**
14449          * @event mouseout
14450          * Fires when the mouse exits this menu
14451          * @param {Roo.menu.Menu} this
14452          * @param {Roo.EventObject} e
14453          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14454          */
14455         mouseout : true,
14456         /**
14457          * @event itemclick
14458          * Fires when a menu item contained in this menu is clicked
14459          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14460          * @param {Roo.EventObject} e
14461          */
14462         itemclick: true
14463     });
14464     if (this.registerMenu) {
14465         Roo.menu.MenuMgr.register(this);
14466     }
14467     
14468     var mis = this.items;
14469     this.items = new Roo.util.MixedCollection();
14470     if(mis){
14471         this.add.apply(this, mis);
14472     }
14473 };
14474
14475 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14476     /**
14477      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14478      */
14479     minWidth : 120,
14480     /**
14481      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14482      * for bottom-right shadow (defaults to "sides")
14483      */
14484     shadow : "sides",
14485     /**
14486      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14487      * this menu (defaults to "tl-tr?")
14488      */
14489     subMenuAlign : "tl-tr?",
14490     /**
14491      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14492      * relative to its element of origin (defaults to "tl-bl?")
14493      */
14494     defaultAlign : "tl-bl?",
14495     /**
14496      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14497      */
14498     allowOtherMenus : false,
14499     /**
14500      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14501      */
14502     registerMenu : true,
14503
14504     hidden:true,
14505
14506     // private
14507     render : function(){
14508         if(this.el){
14509             return;
14510         }
14511         var el = this.el = new Roo.Layer({
14512             cls: "x-menu",
14513             shadow:this.shadow,
14514             constrain: false,
14515             parentEl: this.parentEl || document.body,
14516             zindex:15000
14517         });
14518
14519         this.keyNav = new Roo.menu.MenuNav(this);
14520
14521         if(this.plain){
14522             el.addClass("x-menu-plain");
14523         }
14524         if(this.cls){
14525             el.addClass(this.cls);
14526         }
14527         // generic focus element
14528         this.focusEl = el.createChild({
14529             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14530         });
14531         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14532         //disabling touch- as it's causing issues ..
14533         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14534         ul.on('click'   , this.onClick, this);
14535         
14536         
14537         ul.on("mouseover", this.onMouseOver, this);
14538         ul.on("mouseout", this.onMouseOut, this);
14539         this.items.each(function(item){
14540             if (item.hidden) {
14541                 return;
14542             }
14543             
14544             var li = document.createElement("li");
14545             li.className = "x-menu-list-item";
14546             ul.dom.appendChild(li);
14547             item.render(li, this);
14548         }, this);
14549         this.ul = ul;
14550         this.autoWidth();
14551     },
14552
14553     // private
14554     autoWidth : function(){
14555         var el = this.el, ul = this.ul;
14556         if(!el){
14557             return;
14558         }
14559         var w = this.width;
14560         if(w){
14561             el.setWidth(w);
14562         }else if(Roo.isIE){
14563             el.setWidth(this.minWidth);
14564             var t = el.dom.offsetWidth; // force recalc
14565             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14566         }
14567     },
14568
14569     // private
14570     delayAutoWidth : function(){
14571         if(this.rendered){
14572             if(!this.awTask){
14573                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14574             }
14575             this.awTask.delay(20);
14576         }
14577     },
14578
14579     // private
14580     findTargetItem : function(e){
14581         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14582         if(t && t.menuItemId){
14583             return this.items.get(t.menuItemId);
14584         }
14585     },
14586
14587     // private
14588     onClick : function(e){
14589         Roo.log("menu.onClick");
14590         var t = this.findTargetItem(e);
14591         if(!t){
14592             return;
14593         }
14594         Roo.log(e);
14595         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14596             if(t == this.activeItem && t.shouldDeactivate(e)){
14597                 this.activeItem.deactivate();
14598                 delete this.activeItem;
14599                 return;
14600             }
14601             if(t.canActivate){
14602                 this.setActiveItem(t, true);
14603             }
14604             return;
14605             
14606             
14607         }
14608         
14609         t.onClick(e);
14610         this.fireEvent("click", this, t, e);
14611     },
14612
14613     // private
14614     setActiveItem : function(item, autoExpand){
14615         if(item != this.activeItem){
14616             if(this.activeItem){
14617                 this.activeItem.deactivate();
14618             }
14619             this.activeItem = item;
14620             item.activate(autoExpand);
14621         }else if(autoExpand){
14622             item.expandMenu();
14623         }
14624     },
14625
14626     // private
14627     tryActivate : function(start, step){
14628         var items = this.items;
14629         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14630             var item = items.get(i);
14631             if(!item.disabled && item.canActivate){
14632                 this.setActiveItem(item, false);
14633                 return item;
14634             }
14635         }
14636         return false;
14637     },
14638
14639     // private
14640     onMouseOver : function(e){
14641         var t;
14642         if(t = this.findTargetItem(e)){
14643             if(t.canActivate && !t.disabled){
14644                 this.setActiveItem(t, true);
14645             }
14646         }
14647         this.fireEvent("mouseover", this, e, t);
14648     },
14649
14650     // private
14651     onMouseOut : function(e){
14652         var t;
14653         if(t = this.findTargetItem(e)){
14654             if(t == this.activeItem && t.shouldDeactivate(e)){
14655                 this.activeItem.deactivate();
14656                 delete this.activeItem;
14657             }
14658         }
14659         this.fireEvent("mouseout", this, e, t);
14660     },
14661
14662     /**
14663      * Read-only.  Returns true if the menu is currently displayed, else false.
14664      * @type Boolean
14665      */
14666     isVisible : function(){
14667         return this.el && !this.hidden;
14668     },
14669
14670     /**
14671      * Displays this menu relative to another element
14672      * @param {String/HTMLElement/Roo.Element} element The element to align to
14673      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14674      * the element (defaults to this.defaultAlign)
14675      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14676      */
14677     show : function(el, pos, parentMenu){
14678         this.parentMenu = parentMenu;
14679         if(!this.el){
14680             this.render();
14681         }
14682         this.fireEvent("beforeshow", this);
14683         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14684     },
14685
14686     /**
14687      * Displays this menu at a specific xy position
14688      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14689      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14690      */
14691     showAt : function(xy, parentMenu, /* private: */_e){
14692         this.parentMenu = parentMenu;
14693         if(!this.el){
14694             this.render();
14695         }
14696         if(_e !== false){
14697             this.fireEvent("beforeshow", this);
14698             xy = this.el.adjustForConstraints(xy);
14699         }
14700         this.el.setXY(xy);
14701         this.el.show();
14702         this.hidden = false;
14703         this.focus();
14704         this.fireEvent("show", this);
14705     },
14706
14707     focus : function(){
14708         if(!this.hidden){
14709             this.doFocus.defer(50, this);
14710         }
14711     },
14712
14713     doFocus : function(){
14714         if(!this.hidden){
14715             this.focusEl.focus();
14716         }
14717     },
14718
14719     /**
14720      * Hides this menu and optionally all parent menus
14721      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14722      */
14723     hide : function(deep){
14724         if(this.el && this.isVisible()){
14725             this.fireEvent("beforehide", this);
14726             if(this.activeItem){
14727                 this.activeItem.deactivate();
14728                 this.activeItem = null;
14729             }
14730             this.el.hide();
14731             this.hidden = true;
14732             this.fireEvent("hide", this);
14733         }
14734         if(deep === true && this.parentMenu){
14735             this.parentMenu.hide(true);
14736         }
14737     },
14738
14739     /**
14740      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14741      * Any of the following are valid:
14742      * <ul>
14743      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14744      * <li>An HTMLElement object which will be converted to a menu item</li>
14745      * <li>A menu item config object that will be created as a new menu item</li>
14746      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14747      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14748      * </ul>
14749      * Usage:
14750      * <pre><code>
14751 // Create the menu
14752 var menu = new Roo.menu.Menu();
14753
14754 // Create a menu item to add by reference
14755 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14756
14757 // Add a bunch of items at once using different methods.
14758 // Only the last item added will be returned.
14759 var item = menu.add(
14760     menuItem,                // add existing item by ref
14761     'Dynamic Item',          // new TextItem
14762     '-',                     // new separator
14763     { text: 'Config Item' }  // new item by config
14764 );
14765 </code></pre>
14766      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14767      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14768      */
14769     add : function(){
14770         var a = arguments, l = a.length, item;
14771         for(var i = 0; i < l; i++){
14772             var el = a[i];
14773             if ((typeof(el) == "object") && el.xtype && el.xns) {
14774                 el = Roo.factory(el, Roo.menu);
14775             }
14776             
14777             if(el.render){ // some kind of Item
14778                 item = this.addItem(el);
14779             }else if(typeof el == "string"){ // string
14780                 if(el == "separator" || el == "-"){
14781                     item = this.addSeparator();
14782                 }else{
14783                     item = this.addText(el);
14784                 }
14785             }else if(el.tagName || el.el){ // element
14786                 item = this.addElement(el);
14787             }else if(typeof el == "object"){ // must be menu item config?
14788                 item = this.addMenuItem(el);
14789             }
14790         }
14791         return item;
14792     },
14793
14794     /**
14795      * Returns this menu's underlying {@link Roo.Element} object
14796      * @return {Roo.Element} The element
14797      */
14798     getEl : function(){
14799         if(!this.el){
14800             this.render();
14801         }
14802         return this.el;
14803     },
14804
14805     /**
14806      * Adds a separator bar to the menu
14807      * @return {Roo.menu.Item} The menu item that was added
14808      */
14809     addSeparator : function(){
14810         return this.addItem(new Roo.menu.Separator());
14811     },
14812
14813     /**
14814      * Adds an {@link Roo.Element} object to the menu
14815      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14816      * @return {Roo.menu.Item} The menu item that was added
14817      */
14818     addElement : function(el){
14819         return this.addItem(new Roo.menu.BaseItem(el));
14820     },
14821
14822     /**
14823      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14824      * @param {Roo.menu.Item} item The menu item to add
14825      * @return {Roo.menu.Item} The menu item that was added
14826      */
14827     addItem : function(item){
14828         this.items.add(item);
14829         if(this.ul){
14830             var li = document.createElement("li");
14831             li.className = "x-menu-list-item";
14832             this.ul.dom.appendChild(li);
14833             item.render(li, this);
14834             this.delayAutoWidth();
14835         }
14836         return item;
14837     },
14838
14839     /**
14840      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14841      * @param {Object} config A MenuItem config object
14842      * @return {Roo.menu.Item} The menu item that was added
14843      */
14844     addMenuItem : function(config){
14845         if(!(config instanceof Roo.menu.Item)){
14846             if(typeof config.checked == "boolean"){ // must be check menu item config?
14847                 config = new Roo.menu.CheckItem(config);
14848             }else{
14849                 config = new Roo.menu.Item(config);
14850             }
14851         }
14852         return this.addItem(config);
14853     },
14854
14855     /**
14856      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14857      * @param {String} text The text to display in the menu item
14858      * @return {Roo.menu.Item} The menu item that was added
14859      */
14860     addText : function(text){
14861         return this.addItem(new Roo.menu.TextItem({ text : text }));
14862     },
14863
14864     /**
14865      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14866      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14867      * @param {Roo.menu.Item} item The menu item to add
14868      * @return {Roo.menu.Item} The menu item that was added
14869      */
14870     insert : function(index, item){
14871         this.items.insert(index, item);
14872         if(this.ul){
14873             var li = document.createElement("li");
14874             li.className = "x-menu-list-item";
14875             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14876             item.render(li, this);
14877             this.delayAutoWidth();
14878         }
14879         return item;
14880     },
14881
14882     /**
14883      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14884      * @param {Roo.menu.Item} item The menu item to remove
14885      */
14886     remove : function(item){
14887         this.items.removeKey(item.id);
14888         item.destroy();
14889     },
14890
14891     /**
14892      * Removes and destroys all items in the menu
14893      */
14894     removeAll : function(){
14895         var f;
14896         while(f = this.items.first()){
14897             this.remove(f);
14898         }
14899     }
14900 });
14901
14902 // MenuNav is a private utility class used internally by the Menu
14903 Roo.menu.MenuNav = function(menu){
14904     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14905     this.scope = this.menu = menu;
14906 };
14907
14908 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14909     doRelay : function(e, h){
14910         var k = e.getKey();
14911         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14912             this.menu.tryActivate(0, 1);
14913             return false;
14914         }
14915         return h.call(this.scope || this, e, this.menu);
14916     },
14917
14918     up : function(e, m){
14919         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14920             m.tryActivate(m.items.length-1, -1);
14921         }
14922     },
14923
14924     down : function(e, m){
14925         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14926             m.tryActivate(0, 1);
14927         }
14928     },
14929
14930     right : function(e, m){
14931         if(m.activeItem){
14932             m.activeItem.expandMenu(true);
14933         }
14934     },
14935
14936     left : function(e, m){
14937         m.hide();
14938         if(m.parentMenu && m.parentMenu.activeItem){
14939             m.parentMenu.activeItem.activate();
14940         }
14941     },
14942
14943     enter : function(e, m){
14944         if(m.activeItem){
14945             e.stopPropagation();
14946             m.activeItem.onClick(e);
14947             m.fireEvent("click", this, m.activeItem);
14948             return true;
14949         }
14950     }
14951 });/*
14952  * Based on:
14953  * Ext JS Library 1.1.1
14954  * Copyright(c) 2006-2007, Ext JS, LLC.
14955  *
14956  * Originally Released Under LGPL - original licence link has changed is not relivant.
14957  *
14958  * Fork - LGPL
14959  * <script type="text/javascript">
14960  */
14961  
14962 /**
14963  * @class Roo.menu.MenuMgr
14964  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14965  * @singleton
14966  */
14967 Roo.menu.MenuMgr = function(){
14968    var menus, active, groups = {}, attached = false, lastShow = new Date();
14969
14970    // private - called when first menu is created
14971    function init(){
14972        menus = {};
14973        active = new Roo.util.MixedCollection();
14974        Roo.get(document).addKeyListener(27, function(){
14975            if(active.length > 0){
14976                hideAll();
14977            }
14978        });
14979    }
14980
14981    // private
14982    function hideAll(){
14983        if(active && active.length > 0){
14984            var c = active.clone();
14985            c.each(function(m){
14986                m.hide();
14987            });
14988        }
14989    }
14990
14991    // private
14992    function onHide(m){
14993        active.remove(m);
14994        if(active.length < 1){
14995            Roo.get(document).un("mousedown", onMouseDown);
14996            attached = false;
14997        }
14998    }
14999
15000    // private
15001    function onShow(m){
15002        var last = active.last();
15003        lastShow = new Date();
15004        active.add(m);
15005        if(!attached){
15006            Roo.get(document).on("mousedown", onMouseDown);
15007            attached = true;
15008        }
15009        if(m.parentMenu){
15010           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15011           m.parentMenu.activeChild = m;
15012        }else if(last && last.isVisible()){
15013           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15014        }
15015    }
15016
15017    // private
15018    function onBeforeHide(m){
15019        if(m.activeChild){
15020            m.activeChild.hide();
15021        }
15022        if(m.autoHideTimer){
15023            clearTimeout(m.autoHideTimer);
15024            delete m.autoHideTimer;
15025        }
15026    }
15027
15028    // private
15029    function onBeforeShow(m){
15030        var pm = m.parentMenu;
15031        if(!pm && !m.allowOtherMenus){
15032            hideAll();
15033        }else if(pm && pm.activeChild && active != m){
15034            pm.activeChild.hide();
15035        }
15036    }
15037
15038    // private
15039    function onMouseDown(e){
15040        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15041            hideAll();
15042        }
15043    }
15044
15045    // private
15046    function onBeforeCheck(mi, state){
15047        if(state){
15048            var g = groups[mi.group];
15049            for(var i = 0, l = g.length; i < l; i++){
15050                if(g[i] != mi){
15051                    g[i].setChecked(false);
15052                }
15053            }
15054        }
15055    }
15056
15057    return {
15058
15059        /**
15060         * Hides all menus that are currently visible
15061         */
15062        hideAll : function(){
15063             hideAll();  
15064        },
15065
15066        // private
15067        register : function(menu){
15068            if(!menus){
15069                init();
15070            }
15071            menus[menu.id] = menu;
15072            menu.on("beforehide", onBeforeHide);
15073            menu.on("hide", onHide);
15074            menu.on("beforeshow", onBeforeShow);
15075            menu.on("show", onShow);
15076            var g = menu.group;
15077            if(g && menu.events["checkchange"]){
15078                if(!groups[g]){
15079                    groups[g] = [];
15080                }
15081                groups[g].push(menu);
15082                menu.on("checkchange", onCheck);
15083            }
15084        },
15085
15086         /**
15087          * Returns a {@link Roo.menu.Menu} object
15088          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15089          * be used to generate and return a new Menu instance.
15090          */
15091        get : function(menu){
15092            if(typeof menu == "string"){ // menu id
15093                return menus[menu];
15094            }else if(menu.events){  // menu instance
15095                return menu;
15096            }else if(typeof menu.length == 'number'){ // array of menu items?
15097                return new Roo.menu.Menu({items:menu});
15098            }else{ // otherwise, must be a config
15099                return new Roo.menu.Menu(menu);
15100            }
15101        },
15102
15103        // private
15104        unregister : function(menu){
15105            delete menus[menu.id];
15106            menu.un("beforehide", onBeforeHide);
15107            menu.un("hide", onHide);
15108            menu.un("beforeshow", onBeforeShow);
15109            menu.un("show", onShow);
15110            var g = menu.group;
15111            if(g && menu.events["checkchange"]){
15112                groups[g].remove(menu);
15113                menu.un("checkchange", onCheck);
15114            }
15115        },
15116
15117        // private
15118        registerCheckable : function(menuItem){
15119            var g = menuItem.group;
15120            if(g){
15121                if(!groups[g]){
15122                    groups[g] = [];
15123                }
15124                groups[g].push(menuItem);
15125                menuItem.on("beforecheckchange", onBeforeCheck);
15126            }
15127        },
15128
15129        // private
15130        unregisterCheckable : function(menuItem){
15131            var g = menuItem.group;
15132            if(g){
15133                groups[g].remove(menuItem);
15134                menuItem.un("beforecheckchange", onBeforeCheck);
15135            }
15136        }
15137    };
15138 }();/*
15139  * Based on:
15140  * Ext JS Library 1.1.1
15141  * Copyright(c) 2006-2007, Ext JS, LLC.
15142  *
15143  * Originally Released Under LGPL - original licence link has changed is not relivant.
15144  *
15145  * Fork - LGPL
15146  * <script type="text/javascript">
15147  */
15148  
15149
15150 /**
15151  * @class Roo.menu.BaseItem
15152  * @extends Roo.Component
15153  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15154  * management and base configuration options shared by all menu components.
15155  * @constructor
15156  * Creates a new BaseItem
15157  * @param {Object} config Configuration options
15158  */
15159 Roo.menu.BaseItem = function(config){
15160     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15161
15162     this.addEvents({
15163         /**
15164          * @event click
15165          * Fires when this item is clicked
15166          * @param {Roo.menu.BaseItem} this
15167          * @param {Roo.EventObject} e
15168          */
15169         click: true,
15170         /**
15171          * @event activate
15172          * Fires when this item is activated
15173          * @param {Roo.menu.BaseItem} this
15174          */
15175         activate : true,
15176         /**
15177          * @event deactivate
15178          * Fires when this item is deactivated
15179          * @param {Roo.menu.BaseItem} this
15180          */
15181         deactivate : true
15182     });
15183
15184     if(this.handler){
15185         this.on("click", this.handler, this.scope, true);
15186     }
15187 };
15188
15189 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15190     /**
15191      * @cfg {Function} handler
15192      * A function that will handle the click event of this menu item (defaults to undefined)
15193      */
15194     /**
15195      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15196      */
15197     canActivate : false,
15198     
15199      /**
15200      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15201      */
15202     hidden: false,
15203     
15204     /**
15205      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15206      */
15207     activeClass : "x-menu-item-active",
15208     /**
15209      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15210      */
15211     hideOnClick : true,
15212     /**
15213      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15214      */
15215     hideDelay : 100,
15216
15217     // private
15218     ctype: "Roo.menu.BaseItem",
15219
15220     // private
15221     actionMode : "container",
15222
15223     // private
15224     render : function(container, parentMenu){
15225         this.parentMenu = parentMenu;
15226         Roo.menu.BaseItem.superclass.render.call(this, container);
15227         this.container.menuItemId = this.id;
15228     },
15229
15230     // private
15231     onRender : function(container, position){
15232         this.el = Roo.get(this.el);
15233         container.dom.appendChild(this.el.dom);
15234     },
15235
15236     // private
15237     onClick : function(e){
15238         if(!this.disabled && this.fireEvent("click", this, e) !== false
15239                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15240             this.handleClick(e);
15241         }else{
15242             e.stopEvent();
15243         }
15244     },
15245
15246     // private
15247     activate : function(){
15248         if(this.disabled){
15249             return false;
15250         }
15251         var li = this.container;
15252         li.addClass(this.activeClass);
15253         this.region = li.getRegion().adjust(2, 2, -2, -2);
15254         this.fireEvent("activate", this);
15255         return true;
15256     },
15257
15258     // private
15259     deactivate : function(){
15260         this.container.removeClass(this.activeClass);
15261         this.fireEvent("deactivate", this);
15262     },
15263
15264     // private
15265     shouldDeactivate : function(e){
15266         return !this.region || !this.region.contains(e.getPoint());
15267     },
15268
15269     // private
15270     handleClick : function(e){
15271         if(this.hideOnClick){
15272             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15273         }
15274     },
15275
15276     // private
15277     expandMenu : function(autoActivate){
15278         // do nothing
15279     },
15280
15281     // private
15282     hideMenu : function(){
15283         // do nothing
15284     }
15285 });/*
15286  * Based on:
15287  * Ext JS Library 1.1.1
15288  * Copyright(c) 2006-2007, Ext JS, LLC.
15289  *
15290  * Originally Released Under LGPL - original licence link has changed is not relivant.
15291  *
15292  * Fork - LGPL
15293  * <script type="text/javascript">
15294  */
15295  
15296 /**
15297  * @class Roo.menu.Adapter
15298  * @extends Roo.menu.BaseItem
15299  * 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.
15300  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15301  * @constructor
15302  * Creates a new Adapter
15303  * @param {Object} config Configuration options
15304  */
15305 Roo.menu.Adapter = function(component, config){
15306     Roo.menu.Adapter.superclass.constructor.call(this, config);
15307     this.component = component;
15308 };
15309 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15310     // private
15311     canActivate : true,
15312
15313     // private
15314     onRender : function(container, position){
15315         this.component.render(container);
15316         this.el = this.component.getEl();
15317     },
15318
15319     // private
15320     activate : function(){
15321         if(this.disabled){
15322             return false;
15323         }
15324         this.component.focus();
15325         this.fireEvent("activate", this);
15326         return true;
15327     },
15328
15329     // private
15330     deactivate : function(){
15331         this.fireEvent("deactivate", this);
15332     },
15333
15334     // private
15335     disable : function(){
15336         this.component.disable();
15337         Roo.menu.Adapter.superclass.disable.call(this);
15338     },
15339
15340     // private
15341     enable : function(){
15342         this.component.enable();
15343         Roo.menu.Adapter.superclass.enable.call(this);
15344     }
15345 });/*
15346  * Based on:
15347  * Ext JS Library 1.1.1
15348  * Copyright(c) 2006-2007, Ext JS, LLC.
15349  *
15350  * Originally Released Under LGPL - original licence link has changed is not relivant.
15351  *
15352  * Fork - LGPL
15353  * <script type="text/javascript">
15354  */
15355
15356 /**
15357  * @class Roo.menu.TextItem
15358  * @extends Roo.menu.BaseItem
15359  * Adds a static text string to a menu, usually used as either a heading or group separator.
15360  * Note: old style constructor with text is still supported.
15361  * 
15362  * @constructor
15363  * Creates a new TextItem
15364  * @param {Object} cfg Configuration
15365  */
15366 Roo.menu.TextItem = function(cfg){
15367     if (typeof(cfg) == 'string') {
15368         this.text = cfg;
15369     } else {
15370         Roo.apply(this,cfg);
15371     }
15372     
15373     Roo.menu.TextItem.superclass.constructor.call(this);
15374 };
15375
15376 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15377     /**
15378      * @cfg {String} text Text to show on item.
15379      */
15380     text : '',
15381     
15382     /**
15383      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15384      */
15385     hideOnClick : false,
15386     /**
15387      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15388      */
15389     itemCls : "x-menu-text",
15390
15391     // private
15392     onRender : function(){
15393         var s = document.createElement("span");
15394         s.className = this.itemCls;
15395         s.innerHTML = this.text;
15396         this.el = s;
15397         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15398     }
15399 });/*
15400  * Based on:
15401  * Ext JS Library 1.1.1
15402  * Copyright(c) 2006-2007, Ext JS, LLC.
15403  *
15404  * Originally Released Under LGPL - original licence link has changed is not relivant.
15405  *
15406  * Fork - LGPL
15407  * <script type="text/javascript">
15408  */
15409
15410 /**
15411  * @class Roo.menu.Separator
15412  * @extends Roo.menu.BaseItem
15413  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15414  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15415  * @constructor
15416  * @param {Object} config Configuration options
15417  */
15418 Roo.menu.Separator = function(config){
15419     Roo.menu.Separator.superclass.constructor.call(this, config);
15420 };
15421
15422 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15423     /**
15424      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15425      */
15426     itemCls : "x-menu-sep",
15427     /**
15428      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15429      */
15430     hideOnClick : false,
15431
15432     // private
15433     onRender : function(li){
15434         var s = document.createElement("span");
15435         s.className = this.itemCls;
15436         s.innerHTML = "&#160;";
15437         this.el = s;
15438         li.addClass("x-menu-sep-li");
15439         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15440     }
15441 });/*
15442  * Based on:
15443  * Ext JS Library 1.1.1
15444  * Copyright(c) 2006-2007, Ext JS, LLC.
15445  *
15446  * Originally Released Under LGPL - original licence link has changed is not relivant.
15447  *
15448  * Fork - LGPL
15449  * <script type="text/javascript">
15450  */
15451 /**
15452  * @class Roo.menu.Item
15453  * @extends Roo.menu.BaseItem
15454  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15455  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15456  * activation and click handling.
15457  * @constructor
15458  * Creates a new Item
15459  * @param {Object} config Configuration options
15460  */
15461 Roo.menu.Item = function(config){
15462     Roo.menu.Item.superclass.constructor.call(this, config);
15463     if(this.menu){
15464         this.menu = Roo.menu.MenuMgr.get(this.menu);
15465     }
15466 };
15467 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15468     /**
15469      * @cfg {Roo.menu.Menu} menu
15470      * A Sub menu
15471      */
15472     /**
15473      * @cfg {String} text
15474      * The text to show on the menu item.
15475      */
15476     text: '',
15477      /**
15478      * @cfg {String} HTML to render in menu
15479      * The text to show on the menu item (HTML version).
15480      */
15481     html: '',
15482     /**
15483      * @cfg {String} icon
15484      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15485      */
15486     icon: undefined,
15487     /**
15488      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15489      */
15490     itemCls : "x-menu-item",
15491     /**
15492      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15493      */
15494     canActivate : true,
15495     /**
15496      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15497      */
15498     showDelay: 200,
15499     // doc'd in BaseItem
15500     hideDelay: 200,
15501
15502     // private
15503     ctype: "Roo.menu.Item",
15504     
15505     // private
15506     onRender : function(container, position){
15507         var el = document.createElement("a");
15508         el.hideFocus = true;
15509         el.unselectable = "on";
15510         el.href = this.href || "#";
15511         if(this.hrefTarget){
15512             el.target = this.hrefTarget;
15513         }
15514         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15515         
15516         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15517         
15518         el.innerHTML = String.format(
15519                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15520                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15521         this.el = el;
15522         Roo.menu.Item.superclass.onRender.call(this, container, position);
15523     },
15524
15525     /**
15526      * Sets the text to display in this menu item
15527      * @param {String} text The text to display
15528      * @param {Boolean} isHTML true to indicate text is pure html.
15529      */
15530     setText : function(text, isHTML){
15531         if (isHTML) {
15532             this.html = text;
15533         } else {
15534             this.text = text;
15535             this.html = '';
15536         }
15537         if(this.rendered){
15538             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15539      
15540             this.el.update(String.format(
15541                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15542                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15543             this.parentMenu.autoWidth();
15544         }
15545     },
15546
15547     // private
15548     handleClick : function(e){
15549         if(!this.href){ // if no link defined, stop the event automatically
15550             e.stopEvent();
15551         }
15552         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15553     },
15554
15555     // private
15556     activate : function(autoExpand){
15557         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15558             this.focus();
15559             if(autoExpand){
15560                 this.expandMenu();
15561             }
15562         }
15563         return true;
15564     },
15565
15566     // private
15567     shouldDeactivate : function(e){
15568         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15569             if(this.menu && this.menu.isVisible()){
15570                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15571             }
15572             return true;
15573         }
15574         return false;
15575     },
15576
15577     // private
15578     deactivate : function(){
15579         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15580         this.hideMenu();
15581     },
15582
15583     // private
15584     expandMenu : function(autoActivate){
15585         if(!this.disabled && this.menu){
15586             clearTimeout(this.hideTimer);
15587             delete this.hideTimer;
15588             if(!this.menu.isVisible() && !this.showTimer){
15589                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15590             }else if (this.menu.isVisible() && autoActivate){
15591                 this.menu.tryActivate(0, 1);
15592             }
15593         }
15594     },
15595
15596     // private
15597     deferExpand : function(autoActivate){
15598         delete this.showTimer;
15599         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15600         if(autoActivate){
15601             this.menu.tryActivate(0, 1);
15602         }
15603     },
15604
15605     // private
15606     hideMenu : function(){
15607         clearTimeout(this.showTimer);
15608         delete this.showTimer;
15609         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15610             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15611         }
15612     },
15613
15614     // private
15615     deferHide : function(){
15616         delete this.hideTimer;
15617         this.menu.hide();
15618     }
15619 });/*
15620  * Based on:
15621  * Ext JS Library 1.1.1
15622  * Copyright(c) 2006-2007, Ext JS, LLC.
15623  *
15624  * Originally Released Under LGPL - original licence link has changed is not relivant.
15625  *
15626  * Fork - LGPL
15627  * <script type="text/javascript">
15628  */
15629  
15630 /**
15631  * @class Roo.menu.CheckItem
15632  * @extends Roo.menu.Item
15633  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15634  * @constructor
15635  * Creates a new CheckItem
15636  * @param {Object} config Configuration options
15637  */
15638 Roo.menu.CheckItem = function(config){
15639     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15640     this.addEvents({
15641         /**
15642          * @event beforecheckchange
15643          * Fires before the checked value is set, providing an opportunity to cancel if needed
15644          * @param {Roo.menu.CheckItem} this
15645          * @param {Boolean} checked The new checked value that will be set
15646          */
15647         "beforecheckchange" : true,
15648         /**
15649          * @event checkchange
15650          * Fires after the checked value has been set
15651          * @param {Roo.menu.CheckItem} this
15652          * @param {Boolean} checked The checked value that was set
15653          */
15654         "checkchange" : true
15655     });
15656     if(this.checkHandler){
15657         this.on('checkchange', this.checkHandler, this.scope);
15658     }
15659 };
15660 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15661     /**
15662      * @cfg {String} group
15663      * All check items with the same group name will automatically be grouped into a single-select
15664      * radio button group (defaults to '')
15665      */
15666     /**
15667      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15668      */
15669     itemCls : "x-menu-item x-menu-check-item",
15670     /**
15671      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15672      */
15673     groupClass : "x-menu-group-item",
15674
15675     /**
15676      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15677      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15678      * initialized with checked = true will be rendered as checked.
15679      */
15680     checked: false,
15681
15682     // private
15683     ctype: "Roo.menu.CheckItem",
15684
15685     // private
15686     onRender : function(c){
15687         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15688         if(this.group){
15689             this.el.addClass(this.groupClass);
15690         }
15691         Roo.menu.MenuMgr.registerCheckable(this);
15692         if(this.checked){
15693             this.checked = false;
15694             this.setChecked(true, true);
15695         }
15696     },
15697
15698     // private
15699     destroy : function(){
15700         if(this.rendered){
15701             Roo.menu.MenuMgr.unregisterCheckable(this);
15702         }
15703         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15704     },
15705
15706     /**
15707      * Set the checked state of this item
15708      * @param {Boolean} checked The new checked value
15709      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15710      */
15711     setChecked : function(state, suppressEvent){
15712         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15713             if(this.container){
15714                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15715             }
15716             this.checked = state;
15717             if(suppressEvent !== true){
15718                 this.fireEvent("checkchange", this, state);
15719             }
15720         }
15721     },
15722
15723     // private
15724     handleClick : function(e){
15725        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15726            this.setChecked(!this.checked);
15727        }
15728        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15729     }
15730 });/*
15731  * Based on:
15732  * Ext JS Library 1.1.1
15733  * Copyright(c) 2006-2007, Ext JS, LLC.
15734  *
15735  * Originally Released Under LGPL - original licence link has changed is not relivant.
15736  *
15737  * Fork - LGPL
15738  * <script type="text/javascript">
15739  */
15740  
15741 /**
15742  * @class Roo.menu.DateItem
15743  * @extends Roo.menu.Adapter
15744  * A menu item that wraps the {@link Roo.DatPicker} component.
15745  * @constructor
15746  * Creates a new DateItem
15747  * @param {Object} config Configuration options
15748  */
15749 Roo.menu.DateItem = function(config){
15750     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15751     /** The Roo.DatePicker object @type Roo.DatePicker */
15752     this.picker = this.component;
15753     this.addEvents({select: true});
15754     
15755     this.picker.on("render", function(picker){
15756         picker.getEl().swallowEvent("click");
15757         picker.container.addClass("x-menu-date-item");
15758     });
15759
15760     this.picker.on("select", this.onSelect, this);
15761 };
15762
15763 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15764     // private
15765     onSelect : function(picker, date){
15766         this.fireEvent("select", this, date, picker);
15767         Roo.menu.DateItem.superclass.handleClick.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.ColorItem
15782  * @extends Roo.menu.Adapter
15783  * A menu item that wraps the {@link Roo.ColorPalette} component.
15784  * @constructor
15785  * Creates a new ColorItem
15786  * @param {Object} config Configuration options
15787  */
15788 Roo.menu.ColorItem = function(config){
15789     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15790     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15791     this.palette = this.component;
15792     this.relayEvents(this.palette, ["select"]);
15793     if(this.selectHandler){
15794         this.on('select', this.selectHandler, this.scope);
15795     }
15796 };
15797 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15798  * Based on:
15799  * Ext JS Library 1.1.1
15800  * Copyright(c) 2006-2007, Ext JS, LLC.
15801  *
15802  * Originally Released Under LGPL - original licence link has changed is not relivant.
15803  *
15804  * Fork - LGPL
15805  * <script type="text/javascript">
15806  */
15807  
15808
15809 /**
15810  * @class Roo.menu.DateMenu
15811  * @extends Roo.menu.Menu
15812  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15813  * @constructor
15814  * Creates a new DateMenu
15815  * @param {Object} config Configuration options
15816  */
15817 Roo.menu.DateMenu = function(config){
15818     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15819     this.plain = true;
15820     var di = new Roo.menu.DateItem(config);
15821     this.add(di);
15822     /**
15823      * The {@link Roo.DatePicker} instance for this DateMenu
15824      * @type DatePicker
15825      */
15826     this.picker = di.picker;
15827     /**
15828      * @event select
15829      * @param {DatePicker} picker
15830      * @param {Date} date
15831      */
15832     this.relayEvents(di, ["select"]);
15833     this.on('beforeshow', function(){
15834         if(this.picker){
15835             this.picker.hideMonthPicker(false);
15836         }
15837     }, this);
15838 };
15839 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15840     cls:'x-date-menu'
15841 });/*
15842  * Based on:
15843  * Ext JS Library 1.1.1
15844  * Copyright(c) 2006-2007, Ext JS, LLC.
15845  *
15846  * Originally Released Under LGPL - original licence link has changed is not relivant.
15847  *
15848  * Fork - LGPL
15849  * <script type="text/javascript">
15850  */
15851  
15852
15853 /**
15854  * @class Roo.menu.ColorMenu
15855  * @extends Roo.menu.Menu
15856  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15857  * @constructor
15858  * Creates a new ColorMenu
15859  * @param {Object} config Configuration options
15860  */
15861 Roo.menu.ColorMenu = function(config){
15862     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15863     this.plain = true;
15864     var ci = new Roo.menu.ColorItem(config);
15865     this.add(ci);
15866     /**
15867      * The {@link Roo.ColorPalette} instance for this ColorMenu
15868      * @type ColorPalette
15869      */
15870     this.palette = ci.palette;
15871     /**
15872      * @event select
15873      * @param {ColorPalette} palette
15874      * @param {String} color
15875      */
15876     this.relayEvents(ci, ["select"]);
15877 };
15878 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15879  * Based on:
15880  * Ext JS Library 1.1.1
15881  * Copyright(c) 2006-2007, Ext JS, LLC.
15882  *
15883  * Originally Released Under LGPL - original licence link has changed is not relivant.
15884  *
15885  * Fork - LGPL
15886  * <script type="text/javascript">
15887  */
15888  
15889 /**
15890  * @class Roo.form.TextItem
15891  * @extends Roo.BoxComponent
15892  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15893  * @constructor
15894  * Creates a new TextItem
15895  * @param {Object} config Configuration options
15896  */
15897 Roo.form.TextItem = function(config){
15898     Roo.form.TextItem.superclass.constructor.call(this, config);
15899 };
15900
15901 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15902     
15903     /**
15904      * @cfg {String} tag the tag for this item (default div)
15905      */
15906     tag : 'div',
15907     /**
15908      * @cfg {String} html the content for this item
15909      */
15910     html : '',
15911     
15912     getAutoCreate : function()
15913     {
15914         var cfg = {
15915             id: this.id,
15916             tag: this.tag,
15917             html: this.html,
15918             cls: 'x-form-item'
15919         };
15920         
15921         return cfg;
15922         
15923     },
15924     
15925     onRender : function(ct, position)
15926     {
15927         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15928         
15929         if(!this.el){
15930             var cfg = this.getAutoCreate();
15931             if(!cfg.name){
15932                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15933             }
15934             if (!cfg.name.length) {
15935                 delete cfg.name;
15936             }
15937             this.el = ct.createChild(cfg, position);
15938         }
15939     },
15940     /*
15941      * setHTML
15942      * @param {String} html update the Contents of the element.
15943      */
15944     setHTML : function(html)
15945     {
15946         this.fieldEl.dom.innerHTML = html;
15947     }
15948     
15949 });/*
15950  * Based on:
15951  * Ext JS Library 1.1.1
15952  * Copyright(c) 2006-2007, Ext JS, LLC.
15953  *
15954  * Originally Released Under LGPL - original licence link has changed is not relivant.
15955  *
15956  * Fork - LGPL
15957  * <script type="text/javascript">
15958  */
15959  
15960 /**
15961  * @class Roo.form.Field
15962  * @extends Roo.BoxComponent
15963  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15964  * @constructor
15965  * Creates a new Field
15966  * @param {Object} config Configuration options
15967  */
15968 Roo.form.Field = function(config){
15969     Roo.form.Field.superclass.constructor.call(this, config);
15970 };
15971
15972 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15973     /**
15974      * @cfg {String} fieldLabel Label to use when rendering a form.
15975      */
15976        /**
15977      * @cfg {String} qtip Mouse over tip
15978      */
15979      
15980     /**
15981      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15982      */
15983     invalidClass : "x-form-invalid",
15984     /**
15985      * @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")
15986      */
15987     invalidText : "The value in this field is invalid",
15988     /**
15989      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15990      */
15991     focusClass : "x-form-focus",
15992     /**
15993      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15994       automatic validation (defaults to "keyup").
15995      */
15996     validationEvent : "keyup",
15997     /**
15998      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
15999      */
16000     validateOnBlur : true,
16001     /**
16002      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16003      */
16004     validationDelay : 250,
16005     /**
16006      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16007      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16008      */
16009     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16010     /**
16011      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16012      */
16013     fieldClass : "x-form-field",
16014     /**
16015      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16016      *<pre>
16017 Value         Description
16018 -----------   ----------------------------------------------------------------------
16019 qtip          Display a quick tip when the user hovers over the field
16020 title         Display a default browser title attribute popup
16021 under         Add a block div beneath the field containing the error text
16022 side          Add an error icon to the right of the field with a popup on hover
16023 [element id]  Add the error text directly to the innerHTML of the specified element
16024 </pre>
16025      */
16026     msgTarget : 'qtip',
16027     /**
16028      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16029      */
16030     msgFx : 'normal',
16031
16032     /**
16033      * @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.
16034      */
16035     readOnly : false,
16036
16037     /**
16038      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16039      */
16040     disabled : false,
16041
16042     /**
16043      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16044      */
16045     inputType : undefined,
16046     
16047     /**
16048      * @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).
16049          */
16050         tabIndex : undefined,
16051         
16052     // private
16053     isFormField : true,
16054
16055     // private
16056     hasFocus : false,
16057     /**
16058      * @property {Roo.Element} fieldEl
16059      * Element Containing the rendered Field (with label etc.)
16060      */
16061     /**
16062      * @cfg {Mixed} value A value to initialize this field with.
16063      */
16064     value : undefined,
16065
16066     /**
16067      * @cfg {String} name The field's HTML name attribute.
16068      */
16069     /**
16070      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16071      */
16072     // private
16073     loadedValue : false,
16074      
16075      
16076         // private ??
16077         initComponent : function(){
16078         Roo.form.Field.superclass.initComponent.call(this);
16079         this.addEvents({
16080             /**
16081              * @event focus
16082              * Fires when this field receives input focus.
16083              * @param {Roo.form.Field} this
16084              */
16085             focus : true,
16086             /**
16087              * @event blur
16088              * Fires when this field loses input focus.
16089              * @param {Roo.form.Field} this
16090              */
16091             blur : true,
16092             /**
16093              * @event specialkey
16094              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16095              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16096              * @param {Roo.form.Field} this
16097              * @param {Roo.EventObject} e The event object
16098              */
16099             specialkey : true,
16100             /**
16101              * @event change
16102              * Fires just before the field blurs if the field value has changed.
16103              * @param {Roo.form.Field} this
16104              * @param {Mixed} newValue The new value
16105              * @param {Mixed} oldValue The original value
16106              */
16107             change : true,
16108             /**
16109              * @event invalid
16110              * Fires after the field has been marked as invalid.
16111              * @param {Roo.form.Field} this
16112              * @param {String} msg The validation message
16113              */
16114             invalid : true,
16115             /**
16116              * @event valid
16117              * Fires after the field has been validated with no errors.
16118              * @param {Roo.form.Field} this
16119              */
16120             valid : true,
16121              /**
16122              * @event keyup
16123              * Fires after the key up
16124              * @param {Roo.form.Field} this
16125              * @param {Roo.EventObject}  e The event Object
16126              */
16127             keyup : true
16128         });
16129     },
16130
16131     /**
16132      * Returns the name attribute of the field if available
16133      * @return {String} name The field name
16134      */
16135     getName: function(){
16136          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16137     },
16138
16139     // private
16140     onRender : function(ct, position){
16141         Roo.form.Field.superclass.onRender.call(this, ct, position);
16142         if(!this.el){
16143             var cfg = this.getAutoCreate();
16144             if(!cfg.name){
16145                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16146             }
16147             if (!cfg.name.length) {
16148                 delete cfg.name;
16149             }
16150             if(this.inputType){
16151                 cfg.type = this.inputType;
16152             }
16153             this.el = ct.createChild(cfg, position);
16154         }
16155         var type = this.el.dom.type;
16156         if(type){
16157             if(type == 'password'){
16158                 type = 'text';
16159             }
16160             this.el.addClass('x-form-'+type);
16161         }
16162         if(this.readOnly){
16163             this.el.dom.readOnly = true;
16164         }
16165         if(this.tabIndex !== undefined){
16166             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16167         }
16168
16169         this.el.addClass([this.fieldClass, this.cls]);
16170         this.initValue();
16171     },
16172
16173     /**
16174      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16175      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16176      * @return {Roo.form.Field} this
16177      */
16178     applyTo : function(target){
16179         this.allowDomMove = false;
16180         this.el = Roo.get(target);
16181         this.render(this.el.dom.parentNode);
16182         return this;
16183     },
16184
16185     // private
16186     initValue : function(){
16187         if(this.value !== undefined){
16188             this.setValue(this.value);
16189         }else if(this.el.dom.value.length > 0){
16190             this.setValue(this.el.dom.value);
16191         }
16192     },
16193
16194     /**
16195      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16196      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16197      */
16198     isDirty : function() {
16199         if(this.disabled) {
16200             return false;
16201         }
16202         return String(this.getValue()) !== String(this.originalValue);
16203     },
16204
16205     /**
16206      * stores the current value in loadedValue
16207      */
16208     resetHasChanged : function()
16209     {
16210         this.loadedValue = String(this.getValue());
16211     },
16212     /**
16213      * checks the current value against the 'loaded' value.
16214      * Note - will return false if 'resetHasChanged' has not been called first.
16215      */
16216     hasChanged : function()
16217     {
16218         if(this.disabled || this.readOnly) {
16219             return false;
16220         }
16221         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16222     },
16223     
16224     
16225     
16226     // private
16227     afterRender : function(){
16228         Roo.form.Field.superclass.afterRender.call(this);
16229         this.initEvents();
16230     },
16231
16232     // private
16233     fireKey : function(e){
16234         //Roo.log('field ' + e.getKey());
16235         if(e.isNavKeyPress()){
16236             this.fireEvent("specialkey", this, e);
16237         }
16238     },
16239
16240     /**
16241      * Resets the current field value to the originally loaded value and clears any validation messages
16242      */
16243     reset : function(){
16244         this.setValue(this.resetValue);
16245         this.originalValue = this.getValue();
16246         this.clearInvalid();
16247     },
16248
16249     // private
16250     initEvents : function(){
16251         // safari killled keypress - so keydown is now used..
16252         this.el.on("keydown" , this.fireKey,  this);
16253         this.el.on("focus", this.onFocus,  this);
16254         this.el.on("blur", this.onBlur,  this);
16255         this.el.relayEvent('keyup', this);
16256
16257         // reference to original value for reset
16258         this.originalValue = this.getValue();
16259         this.resetValue =  this.getValue();
16260     },
16261
16262     // private
16263     onFocus : function(){
16264         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16265             this.el.addClass(this.focusClass);
16266         }
16267         if(!this.hasFocus){
16268             this.hasFocus = true;
16269             this.startValue = this.getValue();
16270             this.fireEvent("focus", this);
16271         }
16272     },
16273
16274     beforeBlur : Roo.emptyFn,
16275
16276     // private
16277     onBlur : function(){
16278         this.beforeBlur();
16279         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16280             this.el.removeClass(this.focusClass);
16281         }
16282         this.hasFocus = false;
16283         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16284             this.validate();
16285         }
16286         var v = this.getValue();
16287         if(String(v) !== String(this.startValue)){
16288             this.fireEvent('change', this, v, this.startValue);
16289         }
16290         this.fireEvent("blur", this);
16291     },
16292
16293     /**
16294      * Returns whether or not the field value is currently valid
16295      * @param {Boolean} preventMark True to disable marking the field invalid
16296      * @return {Boolean} True if the value is valid, else false
16297      */
16298     isValid : function(preventMark){
16299         if(this.disabled){
16300             return true;
16301         }
16302         var restore = this.preventMark;
16303         this.preventMark = preventMark === true;
16304         var v = this.validateValue(this.processValue(this.getRawValue()));
16305         this.preventMark = restore;
16306         return v;
16307     },
16308
16309     /**
16310      * Validates the field value
16311      * @return {Boolean} True if the value is valid, else false
16312      */
16313     validate : function(){
16314         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16315             this.clearInvalid();
16316             return true;
16317         }
16318         return false;
16319     },
16320
16321     processValue : function(value){
16322         return value;
16323     },
16324
16325     // private
16326     // Subclasses should provide the validation implementation by overriding this
16327     validateValue : function(value){
16328         return true;
16329     },
16330
16331     /**
16332      * Mark this field as invalid
16333      * @param {String} msg The validation message
16334      */
16335     markInvalid : function(msg){
16336         if(!this.rendered || this.preventMark){ // not rendered
16337             return;
16338         }
16339         
16340         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16341         
16342         obj.el.addClass(this.invalidClass);
16343         msg = msg || this.invalidText;
16344         switch(this.msgTarget){
16345             case 'qtip':
16346                 obj.el.dom.qtip = msg;
16347                 obj.el.dom.qclass = 'x-form-invalid-tip';
16348                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16349                     Roo.QuickTips.enable();
16350                 }
16351                 break;
16352             case 'title':
16353                 this.el.dom.title = msg;
16354                 break;
16355             case 'under':
16356                 if(!this.errorEl){
16357                     var elp = this.el.findParent('.x-form-element', 5, true);
16358                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16359                     this.errorEl.setWidth(elp.getWidth(true)-20);
16360                 }
16361                 this.errorEl.update(msg);
16362                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16363                 break;
16364             case 'side':
16365                 if(!this.errorIcon){
16366                     var elp = this.el.findParent('.x-form-element', 5, true);
16367                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16368                 }
16369                 this.alignErrorIcon();
16370                 this.errorIcon.dom.qtip = msg;
16371                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16372                 this.errorIcon.show();
16373                 this.on('resize', this.alignErrorIcon, this);
16374                 break;
16375             default:
16376                 var t = Roo.getDom(this.msgTarget);
16377                 t.innerHTML = msg;
16378                 t.style.display = this.msgDisplay;
16379                 break;
16380         }
16381         this.fireEvent('invalid', this, msg);
16382     },
16383
16384     // private
16385     alignErrorIcon : function(){
16386         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16387     },
16388
16389     /**
16390      * Clear any invalid styles/messages for this field
16391      */
16392     clearInvalid : function(){
16393         if(!this.rendered || this.preventMark){ // not rendered
16394             return;
16395         }
16396         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16397         
16398         obj.el.removeClass(this.invalidClass);
16399         switch(this.msgTarget){
16400             case 'qtip':
16401                 obj.el.dom.qtip = '';
16402                 break;
16403             case 'title':
16404                 this.el.dom.title = '';
16405                 break;
16406             case 'under':
16407                 if(this.errorEl){
16408                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16409                 }
16410                 break;
16411             case 'side':
16412                 if(this.errorIcon){
16413                     this.errorIcon.dom.qtip = '';
16414                     this.errorIcon.hide();
16415                     this.un('resize', this.alignErrorIcon, this);
16416                 }
16417                 break;
16418             default:
16419                 var t = Roo.getDom(this.msgTarget);
16420                 t.innerHTML = '';
16421                 t.style.display = 'none';
16422                 break;
16423         }
16424         this.fireEvent('valid', this);
16425     },
16426
16427     /**
16428      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16429      * @return {Mixed} value The field value
16430      */
16431     getRawValue : function(){
16432         var v = this.el.getValue();
16433         
16434         return v;
16435     },
16436
16437     /**
16438      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16439      * @return {Mixed} value The field value
16440      */
16441     getValue : function(){
16442         var v = this.el.getValue();
16443          
16444         return v;
16445     },
16446
16447     /**
16448      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16449      * @param {Mixed} value The value to set
16450      */
16451     setRawValue : function(v){
16452         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16453     },
16454
16455     /**
16456      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16457      * @param {Mixed} value The value to set
16458      */
16459     setValue : function(v){
16460         this.value = v;
16461         if(this.rendered){
16462             this.el.dom.value = (v === null || v === undefined ? '' : v);
16463              this.validate();
16464         }
16465     },
16466
16467     adjustSize : function(w, h){
16468         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16469         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16470         return s;
16471     },
16472
16473     adjustWidth : function(tag, w){
16474         tag = tag.toLowerCase();
16475         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16476             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16477                 if(tag == 'input'){
16478                     return w + 2;
16479                 }
16480                 if(tag == 'textarea'){
16481                     return w-2;
16482                 }
16483             }else if(Roo.isOpera){
16484                 if(tag == 'input'){
16485                     return w + 2;
16486                 }
16487                 if(tag == 'textarea'){
16488                     return w-2;
16489                 }
16490             }
16491         }
16492         return w;
16493     }
16494 });
16495
16496
16497 // anything other than normal should be considered experimental
16498 Roo.form.Field.msgFx = {
16499     normal : {
16500         show: function(msgEl, f){
16501             msgEl.setDisplayed('block');
16502         },
16503
16504         hide : function(msgEl, f){
16505             msgEl.setDisplayed(false).update('');
16506         }
16507     },
16508
16509     slide : {
16510         show: function(msgEl, f){
16511             msgEl.slideIn('t', {stopFx:true});
16512         },
16513
16514         hide : function(msgEl, f){
16515             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16516         }
16517     },
16518
16519     slideRight : {
16520         show: function(msgEl, f){
16521             msgEl.fixDisplay();
16522             msgEl.alignTo(f.el, 'tl-tr');
16523             msgEl.slideIn('l', {stopFx:true});
16524         },
16525
16526         hide : function(msgEl, f){
16527             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16528         }
16529     }
16530 };/*
16531  * Based on:
16532  * Ext JS Library 1.1.1
16533  * Copyright(c) 2006-2007, Ext JS, LLC.
16534  *
16535  * Originally Released Under LGPL - original licence link has changed is not relivant.
16536  *
16537  * Fork - LGPL
16538  * <script type="text/javascript">
16539  */
16540  
16541
16542 /**
16543  * @class Roo.form.TextField
16544  * @extends Roo.form.Field
16545  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16546  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16547  * @constructor
16548  * Creates a new TextField
16549  * @param {Object} config Configuration options
16550  */
16551 Roo.form.TextField = function(config){
16552     Roo.form.TextField.superclass.constructor.call(this, config);
16553     this.addEvents({
16554         /**
16555          * @event autosize
16556          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16557          * according to the default logic, but this event provides a hook for the developer to apply additional
16558          * logic at runtime to resize the field if needed.
16559              * @param {Roo.form.Field} this This text field
16560              * @param {Number} width The new field width
16561              */
16562         autosize : true
16563     });
16564 };
16565
16566 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16567     /**
16568      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16569      */
16570     grow : false,
16571     /**
16572      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16573      */
16574     growMin : 30,
16575     /**
16576      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16577      */
16578     growMax : 800,
16579     /**
16580      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16581      */
16582     vtype : null,
16583     /**
16584      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16585      */
16586     maskRe : null,
16587     /**
16588      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16589      */
16590     disableKeyFilter : false,
16591     /**
16592      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16593      */
16594     allowBlank : true,
16595     /**
16596      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16597      */
16598     minLength : 0,
16599     /**
16600      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16601      */
16602     maxLength : Number.MAX_VALUE,
16603     /**
16604      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16605      */
16606     minLengthText : "The minimum length for this field is {0}",
16607     /**
16608      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16609      */
16610     maxLengthText : "The maximum length for this field is {0}",
16611     /**
16612      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16613      */
16614     selectOnFocus : false,
16615     /**
16616      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16617      */    
16618     allowLeadingSpace : false,
16619     /**
16620      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16621      */
16622     blankText : "This field is required",
16623     /**
16624      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16625      * If available, this function will be called only after the basic validators all return true, and will be passed the
16626      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16627      */
16628     validator : null,
16629     /**
16630      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16631      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16632      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16633      */
16634     regex : null,
16635     /**
16636      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16637      */
16638     regexText : "",
16639     /**
16640      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16641      */
16642     emptyText : null,
16643    
16644
16645     // private
16646     initEvents : function()
16647     {
16648         if (this.emptyText) {
16649             this.el.attr('placeholder', this.emptyText);
16650         }
16651         
16652         Roo.form.TextField.superclass.initEvents.call(this);
16653         if(this.validationEvent == 'keyup'){
16654             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16655             this.el.on('keyup', this.filterValidation, this);
16656         }
16657         else if(this.validationEvent !== false){
16658             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16659         }
16660         
16661         if(this.selectOnFocus){
16662             this.on("focus", this.preFocus, this);
16663         }
16664         if (!this.allowLeadingSpace) {
16665             this.on('blur', this.cleanLeadingSpace, this);
16666         }
16667         
16668         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16669             this.el.on("keypress", this.filterKeys, this);
16670         }
16671         if(this.grow){
16672             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16673             this.el.on("click", this.autoSize,  this);
16674         }
16675         if(this.el.is('input[type=password]') && Roo.isSafari){
16676             this.el.on('keydown', this.SafariOnKeyDown, this);
16677         }
16678     },
16679
16680     processValue : function(value){
16681         if(this.stripCharsRe){
16682             var newValue = value.replace(this.stripCharsRe, '');
16683             if(newValue !== value){
16684                 this.setRawValue(newValue);
16685                 return newValue;
16686             }
16687         }
16688         return value;
16689     },
16690
16691     filterValidation : function(e){
16692         if(!e.isNavKeyPress()){
16693             this.validationTask.delay(this.validationDelay);
16694         }
16695     },
16696
16697     // private
16698     onKeyUp : function(e){
16699         if(!e.isNavKeyPress()){
16700             this.autoSize();
16701         }
16702     },
16703     // private - clean the leading white space
16704     cleanLeadingSpace : function(e)
16705     {
16706         if ( this.inputType == 'file') {
16707             return;
16708         }
16709         
16710         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16711     },
16712     /**
16713      * Resets the current field value to the originally-loaded value and clears any validation messages.
16714      *  
16715      */
16716     reset : function(){
16717         Roo.form.TextField.superclass.reset.call(this);
16718        
16719     }, 
16720     // private
16721     preFocus : function(){
16722         
16723         if(this.selectOnFocus){
16724             this.el.dom.select();
16725         }
16726     },
16727
16728     
16729     // private
16730     filterKeys : function(e){
16731         var k = e.getKey();
16732         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16733             return;
16734         }
16735         var c = e.getCharCode(), cc = String.fromCharCode(c);
16736         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16737             return;
16738         }
16739         if(!this.maskRe.test(cc)){
16740             e.stopEvent();
16741         }
16742     },
16743
16744     setValue : function(v){
16745         
16746         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16747         
16748         this.autoSize();
16749     },
16750
16751     /**
16752      * Validates a value according to the field's validation rules and marks the field as invalid
16753      * if the validation fails
16754      * @param {Mixed} value The value to validate
16755      * @return {Boolean} True if the value is valid, else false
16756      */
16757     validateValue : function(value){
16758         if(value.length < 1)  { // if it's blank
16759              if(this.allowBlank){
16760                 this.clearInvalid();
16761                 return true;
16762              }else{
16763                 this.markInvalid(this.blankText);
16764                 return false;
16765              }
16766         }
16767         if(value.length < this.minLength){
16768             this.markInvalid(String.format(this.minLengthText, this.minLength));
16769             return false;
16770         }
16771         if(value.length > this.maxLength){
16772             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16773             return false;
16774         }
16775         if(this.vtype){
16776             var vt = Roo.form.VTypes;
16777             if(!vt[this.vtype](value, this)){
16778                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16779                 return false;
16780             }
16781         }
16782         if(typeof this.validator == "function"){
16783             var msg = this.validator(value);
16784             if(msg !== true){
16785                 this.markInvalid(msg);
16786                 return false;
16787             }
16788         }
16789         if(this.regex && !this.regex.test(value)){
16790             this.markInvalid(this.regexText);
16791             return false;
16792         }
16793         return true;
16794     },
16795
16796     /**
16797      * Selects text in this field
16798      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16799      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16800      */
16801     selectText : function(start, end){
16802         var v = this.getRawValue();
16803         if(v.length > 0){
16804             start = start === undefined ? 0 : start;
16805             end = end === undefined ? v.length : end;
16806             var d = this.el.dom;
16807             if(d.setSelectionRange){
16808                 d.setSelectionRange(start, end);
16809             }else if(d.createTextRange){
16810                 var range = d.createTextRange();
16811                 range.moveStart("character", start);
16812                 range.moveEnd("character", v.length-end);
16813                 range.select();
16814             }
16815         }
16816     },
16817
16818     /**
16819      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16820      * This only takes effect if grow = true, and fires the autosize event.
16821      */
16822     autoSize : function(){
16823         if(!this.grow || !this.rendered){
16824             return;
16825         }
16826         if(!this.metrics){
16827             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16828         }
16829         var el = this.el;
16830         var v = el.dom.value;
16831         var d = document.createElement('div');
16832         d.appendChild(document.createTextNode(v));
16833         v = d.innerHTML;
16834         d = null;
16835         v += "&#160;";
16836         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16837         this.el.setWidth(w);
16838         this.fireEvent("autosize", this, w);
16839     },
16840     
16841     // private
16842     SafariOnKeyDown : function(event)
16843     {
16844         // this is a workaround for a password hang bug on chrome/ webkit.
16845         
16846         var isSelectAll = false;
16847         
16848         if(this.el.dom.selectionEnd > 0){
16849             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16850         }
16851         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16852             event.preventDefault();
16853             this.setValue('');
16854             return;
16855         }
16856         
16857         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16858             
16859             event.preventDefault();
16860             // this is very hacky as keydown always get's upper case.
16861             
16862             var cc = String.fromCharCode(event.getCharCode());
16863             
16864             
16865             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16866             
16867         }
16868         
16869         
16870     }
16871 });/*
16872  * Based on:
16873  * Ext JS Library 1.1.1
16874  * Copyright(c) 2006-2007, Ext JS, LLC.
16875  *
16876  * Originally Released Under LGPL - original licence link has changed is not relivant.
16877  *
16878  * Fork - LGPL
16879  * <script type="text/javascript">
16880  */
16881  
16882 /**
16883  * @class Roo.form.Hidden
16884  * @extends Roo.form.TextField
16885  * Simple Hidden element used on forms 
16886  * 
16887  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16888  * 
16889  * @constructor
16890  * Creates a new Hidden form element.
16891  * @param {Object} config Configuration options
16892  */
16893
16894
16895
16896 // easy hidden field...
16897 Roo.form.Hidden = function(config){
16898     Roo.form.Hidden.superclass.constructor.call(this, config);
16899 };
16900   
16901 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16902     fieldLabel:      '',
16903     inputType:      'hidden',
16904     width:          50,
16905     allowBlank:     true,
16906     labelSeparator: '',
16907     hidden:         true,
16908     itemCls :       'x-form-item-display-none'
16909
16910
16911 });
16912
16913
16914 /*
16915  * Based on:
16916  * Ext JS Library 1.1.1
16917  * Copyright(c) 2006-2007, Ext JS, LLC.
16918  *
16919  * Originally Released Under LGPL - original licence link has changed is not relivant.
16920  *
16921  * Fork - LGPL
16922  * <script type="text/javascript">
16923  */
16924  
16925 /**
16926  * @class Roo.form.TriggerField
16927  * @extends Roo.form.TextField
16928  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16929  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16930  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16931  * for which you can provide a custom implementation.  For example:
16932  * <pre><code>
16933 var trigger = new Roo.form.TriggerField();
16934 trigger.onTriggerClick = myTriggerFn;
16935 trigger.applyTo('my-field');
16936 </code></pre>
16937  *
16938  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16939  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16940  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16941  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16942  * @constructor
16943  * Create a new TriggerField.
16944  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16945  * to the base TextField)
16946  */
16947 Roo.form.TriggerField = function(config){
16948     this.mimicing = false;
16949     Roo.form.TriggerField.superclass.constructor.call(this, config);
16950 };
16951
16952 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16953     /**
16954      * @cfg {String} triggerClass A CSS class to apply to the trigger
16955      */
16956     /**
16957      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16958      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16959      */
16960     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16961     /**
16962      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16963      */
16964     hideTrigger:false,
16965
16966     /** @cfg {Boolean} grow @hide */
16967     /** @cfg {Number} growMin @hide */
16968     /** @cfg {Number} growMax @hide */
16969
16970     /**
16971      * @hide 
16972      * @method
16973      */
16974     autoSize: Roo.emptyFn,
16975     // private
16976     monitorTab : true,
16977     // private
16978     deferHeight : true,
16979
16980     
16981     actionMode : 'wrap',
16982     // private
16983     onResize : function(w, h){
16984         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16985         if(typeof w == 'number'){
16986             var x = w - this.trigger.getWidth();
16987             this.el.setWidth(this.adjustWidth('input', x));
16988             this.trigger.setStyle('left', x+'px');
16989         }
16990     },
16991
16992     // private
16993     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16994
16995     // private
16996     getResizeEl : function(){
16997         return this.wrap;
16998     },
16999
17000     // private
17001     getPositionEl : function(){
17002         return this.wrap;
17003     },
17004
17005     // private
17006     alignErrorIcon : function(){
17007         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17008     },
17009
17010     // private
17011     onRender : function(ct, position){
17012         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17013         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17014         this.trigger = this.wrap.createChild(this.triggerConfig ||
17015                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17016         if(this.hideTrigger){
17017             this.trigger.setDisplayed(false);
17018         }
17019         this.initTrigger();
17020         if(!this.width){
17021             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17022         }
17023     },
17024
17025     // private
17026     initTrigger : function(){
17027         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17028         this.trigger.addClassOnOver('x-form-trigger-over');
17029         this.trigger.addClassOnClick('x-form-trigger-click');
17030     },
17031
17032     // private
17033     onDestroy : function(){
17034         if(this.trigger){
17035             this.trigger.removeAllListeners();
17036             this.trigger.remove();
17037         }
17038         if(this.wrap){
17039             this.wrap.remove();
17040         }
17041         Roo.form.TriggerField.superclass.onDestroy.call(this);
17042     },
17043
17044     // private
17045     onFocus : function(){
17046         Roo.form.TriggerField.superclass.onFocus.call(this);
17047         if(!this.mimicing){
17048             this.wrap.addClass('x-trigger-wrap-focus');
17049             this.mimicing = true;
17050             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17051             if(this.monitorTab){
17052                 this.el.on("keydown", this.checkTab, this);
17053             }
17054         }
17055     },
17056
17057     // private
17058     checkTab : function(e){
17059         if(e.getKey() == e.TAB){
17060             this.triggerBlur();
17061         }
17062     },
17063
17064     // private
17065     onBlur : function(){
17066         // do nothing
17067     },
17068
17069     // private
17070     mimicBlur : function(e, t){
17071         if(!this.wrap.contains(t) && this.validateBlur()){
17072             this.triggerBlur();
17073         }
17074     },
17075
17076     // private
17077     triggerBlur : function(){
17078         this.mimicing = false;
17079         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17080         if(this.monitorTab){
17081             this.el.un("keydown", this.checkTab, this);
17082         }
17083         this.wrap.removeClass('x-trigger-wrap-focus');
17084         Roo.form.TriggerField.superclass.onBlur.call(this);
17085     },
17086
17087     // private
17088     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17089     validateBlur : function(e, t){
17090         return true;
17091     },
17092
17093     // private
17094     onDisable : function(){
17095         Roo.form.TriggerField.superclass.onDisable.call(this);
17096         if(this.wrap){
17097             this.wrap.addClass('x-item-disabled');
17098         }
17099     },
17100
17101     // private
17102     onEnable : function(){
17103         Roo.form.TriggerField.superclass.onEnable.call(this);
17104         if(this.wrap){
17105             this.wrap.removeClass('x-item-disabled');
17106         }
17107     },
17108
17109     // private
17110     onShow : function(){
17111         var ae = this.getActionEl();
17112         
17113         if(ae){
17114             ae.dom.style.display = '';
17115             ae.dom.style.visibility = 'visible';
17116         }
17117     },
17118
17119     // private
17120     
17121     onHide : function(){
17122         var ae = this.getActionEl();
17123         ae.dom.style.display = 'none';
17124     },
17125
17126     /**
17127      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17128      * by an implementing function.
17129      * @method
17130      * @param {EventObject} e
17131      */
17132     onTriggerClick : Roo.emptyFn
17133 });
17134
17135 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17136 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17137 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17138 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17139     initComponent : function(){
17140         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17141
17142         this.triggerConfig = {
17143             tag:'span', cls:'x-form-twin-triggers', cn:[
17144             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17145             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17146         ]};
17147     },
17148
17149     getTrigger : function(index){
17150         return this.triggers[index];
17151     },
17152
17153     initTrigger : function(){
17154         var ts = this.trigger.select('.x-form-trigger', true);
17155         this.wrap.setStyle('overflow', 'hidden');
17156         var triggerField = this;
17157         ts.each(function(t, all, index){
17158             t.hide = function(){
17159                 var w = triggerField.wrap.getWidth();
17160                 this.dom.style.display = 'none';
17161                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17162             };
17163             t.show = function(){
17164                 var w = triggerField.wrap.getWidth();
17165                 this.dom.style.display = '';
17166                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17167             };
17168             var triggerIndex = 'Trigger'+(index+1);
17169
17170             if(this['hide'+triggerIndex]){
17171                 t.dom.style.display = 'none';
17172             }
17173             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17174             t.addClassOnOver('x-form-trigger-over');
17175             t.addClassOnClick('x-form-trigger-click');
17176         }, this);
17177         this.triggers = ts.elements;
17178     },
17179
17180     onTrigger1Click : Roo.emptyFn,
17181     onTrigger2Click : Roo.emptyFn
17182 });/*
17183  * Based on:
17184  * Ext JS Library 1.1.1
17185  * Copyright(c) 2006-2007, Ext JS, LLC.
17186  *
17187  * Originally Released Under LGPL - original licence link has changed is not relivant.
17188  *
17189  * Fork - LGPL
17190  * <script type="text/javascript">
17191  */
17192  
17193 /**
17194  * @class Roo.form.TextArea
17195  * @extends Roo.form.TextField
17196  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17197  * support for auto-sizing.
17198  * @constructor
17199  * Creates a new TextArea
17200  * @param {Object} config Configuration options
17201  */
17202 Roo.form.TextArea = function(config){
17203     Roo.form.TextArea.superclass.constructor.call(this, config);
17204     // these are provided exchanges for backwards compat
17205     // minHeight/maxHeight were replaced by growMin/growMax to be
17206     // compatible with TextField growing config values
17207     if(this.minHeight !== undefined){
17208         this.growMin = this.minHeight;
17209     }
17210     if(this.maxHeight !== undefined){
17211         this.growMax = this.maxHeight;
17212     }
17213 };
17214
17215 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17216     /**
17217      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17218      */
17219     growMin : 60,
17220     /**
17221      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17222      */
17223     growMax: 1000,
17224     /**
17225      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17226      * in the field (equivalent to setting overflow: hidden, defaults to false)
17227      */
17228     preventScrollbars: false,
17229     /**
17230      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17231      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17232      */
17233
17234     // private
17235     onRender : function(ct, position){
17236         if(!this.el){
17237             this.defaultAutoCreate = {
17238                 tag: "textarea",
17239                 style:"width:300px;height:60px;",
17240                 autocomplete: "new-password"
17241             };
17242         }
17243         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17244         if(this.grow){
17245             this.textSizeEl = Roo.DomHelper.append(document.body, {
17246                 tag: "pre", cls: "x-form-grow-sizer"
17247             });
17248             if(this.preventScrollbars){
17249                 this.el.setStyle("overflow", "hidden");
17250             }
17251             this.el.setHeight(this.growMin);
17252         }
17253     },
17254
17255     onDestroy : function(){
17256         if(this.textSizeEl){
17257             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17258         }
17259         Roo.form.TextArea.superclass.onDestroy.call(this);
17260     },
17261
17262     // private
17263     onKeyUp : function(e){
17264         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17265             this.autoSize();
17266         }
17267     },
17268
17269     /**
17270      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17271      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17272      */
17273     autoSize : function(){
17274         if(!this.grow || !this.textSizeEl){
17275             return;
17276         }
17277         var el = this.el;
17278         var v = el.dom.value;
17279         var ts = this.textSizeEl;
17280
17281         ts.innerHTML = '';
17282         ts.appendChild(document.createTextNode(v));
17283         v = ts.innerHTML;
17284
17285         Roo.fly(ts).setWidth(this.el.getWidth());
17286         if(v.length < 1){
17287             v = "&#160;&#160;";
17288         }else{
17289             if(Roo.isIE){
17290                 v = v.replace(/\n/g, '<p>&#160;</p>');
17291             }
17292             v += "&#160;\n&#160;";
17293         }
17294         ts.innerHTML = v;
17295         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17296         if(h != this.lastHeight){
17297             this.lastHeight = h;
17298             this.el.setHeight(h);
17299             this.fireEvent("autosize", this, h);
17300         }
17301     }
17302 });/*
17303  * Based on:
17304  * Ext JS Library 1.1.1
17305  * Copyright(c) 2006-2007, Ext JS, LLC.
17306  *
17307  * Originally Released Under LGPL - original licence link has changed is not relivant.
17308  *
17309  * Fork - LGPL
17310  * <script type="text/javascript">
17311  */
17312  
17313
17314 /**
17315  * @class Roo.form.NumberField
17316  * @extends Roo.form.TextField
17317  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17318  * @constructor
17319  * Creates a new NumberField
17320  * @param {Object} config Configuration options
17321  */
17322 Roo.form.NumberField = function(config){
17323     Roo.form.NumberField.superclass.constructor.call(this, config);
17324 };
17325
17326 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17327     /**
17328      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17329      */
17330     fieldClass: "x-form-field x-form-num-field",
17331     /**
17332      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17333      */
17334     allowDecimals : true,
17335     /**
17336      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17337      */
17338     decimalSeparator : ".",
17339     /**
17340      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17341      */
17342     decimalPrecision : 2,
17343     /**
17344      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17345      */
17346     allowNegative : true,
17347     /**
17348      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17349      */
17350     minValue : Number.NEGATIVE_INFINITY,
17351     /**
17352      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17353      */
17354     maxValue : Number.MAX_VALUE,
17355     /**
17356      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17357      */
17358     minText : "The minimum value for this field is {0}",
17359     /**
17360      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17361      */
17362     maxText : "The maximum value for this field is {0}",
17363     /**
17364      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17365      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17366      */
17367     nanText : "{0} is not a valid number",
17368
17369     // private
17370     initEvents : function(){
17371         Roo.form.NumberField.superclass.initEvents.call(this);
17372         var allowed = "0123456789";
17373         if(this.allowDecimals){
17374             allowed += this.decimalSeparator;
17375         }
17376         if(this.allowNegative){
17377             allowed += "-";
17378         }
17379         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17380         var keyPress = function(e){
17381             var k = e.getKey();
17382             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17383                 return;
17384             }
17385             var c = e.getCharCode();
17386             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17387                 e.stopEvent();
17388             }
17389         };
17390         this.el.on("keypress", keyPress, this);
17391     },
17392
17393     // private
17394     validateValue : function(value){
17395         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17396             return false;
17397         }
17398         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17399              return true;
17400         }
17401         var num = this.parseValue(value);
17402         if(isNaN(num)){
17403             this.markInvalid(String.format(this.nanText, value));
17404             return false;
17405         }
17406         if(num < this.minValue){
17407             this.markInvalid(String.format(this.minText, this.minValue));
17408             return false;
17409         }
17410         if(num > this.maxValue){
17411             this.markInvalid(String.format(this.maxText, this.maxValue));
17412             return false;
17413         }
17414         return true;
17415     },
17416
17417     getValue : function(){
17418         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17419     },
17420
17421     // private
17422     parseValue : function(value){
17423         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17424         return isNaN(value) ? '' : value;
17425     },
17426
17427     // private
17428     fixPrecision : function(value){
17429         var nan = isNaN(value);
17430         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17431             return nan ? '' : value;
17432         }
17433         return parseFloat(value).toFixed(this.decimalPrecision);
17434     },
17435
17436     setValue : function(v){
17437         v = this.fixPrecision(v);
17438         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17439     },
17440
17441     // private
17442     decimalPrecisionFcn : function(v){
17443         return Math.floor(v);
17444     },
17445
17446     beforeBlur : function(){
17447         var v = this.parseValue(this.getRawValue());
17448         if(v){
17449             this.setValue(v);
17450         }
17451     }
17452 });/*
17453  * Based on:
17454  * Ext JS Library 1.1.1
17455  * Copyright(c) 2006-2007, Ext JS, LLC.
17456  *
17457  * Originally Released Under LGPL - original licence link has changed is not relivant.
17458  *
17459  * Fork - LGPL
17460  * <script type="text/javascript">
17461  */
17462  
17463 /**
17464  * @class Roo.form.DateField
17465  * @extends Roo.form.TriggerField
17466  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17467 * @constructor
17468 * Create a new DateField
17469 * @param {Object} config
17470  */
17471 Roo.form.DateField = function(config)
17472 {
17473     Roo.form.DateField.superclass.constructor.call(this, config);
17474     
17475       this.addEvents({
17476          
17477         /**
17478          * @event select
17479          * Fires when a date is selected
17480              * @param {Roo.form.DateField} combo This combo box
17481              * @param {Date} date The date selected
17482              */
17483         'select' : true
17484          
17485     });
17486     
17487     
17488     if(typeof this.minValue == "string") {
17489         this.minValue = this.parseDate(this.minValue);
17490     }
17491     if(typeof this.maxValue == "string") {
17492         this.maxValue = this.parseDate(this.maxValue);
17493     }
17494     this.ddMatch = null;
17495     if(this.disabledDates){
17496         var dd = this.disabledDates;
17497         var re = "(?:";
17498         for(var i = 0; i < dd.length; i++){
17499             re += dd[i];
17500             if(i != dd.length-1) {
17501                 re += "|";
17502             }
17503         }
17504         this.ddMatch = new RegExp(re + ")");
17505     }
17506 };
17507
17508 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17509     /**
17510      * @cfg {String} format
17511      * The default date format string which can be overriden for localization support.  The format must be
17512      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17513      */
17514     format : "m/d/y",
17515     /**
17516      * @cfg {String} altFormats
17517      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17518      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17519      */
17520     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17521     /**
17522      * @cfg {Array} disabledDays
17523      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17524      */
17525     disabledDays : null,
17526     /**
17527      * @cfg {String} disabledDaysText
17528      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17529      */
17530     disabledDaysText : "Disabled",
17531     /**
17532      * @cfg {Array} disabledDates
17533      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17534      * expression so they are very powerful. Some examples:
17535      * <ul>
17536      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17537      * <li>["03/08", "09/16"] would disable those days for every year</li>
17538      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17539      * <li>["03/../2006"] would disable every day in March 2006</li>
17540      * <li>["^03"] would disable every day in every March</li>
17541      * </ul>
17542      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17543      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17544      */
17545     disabledDates : null,
17546     /**
17547      * @cfg {String} disabledDatesText
17548      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17549      */
17550     disabledDatesText : "Disabled",
17551     /**
17552      * @cfg {Date/String} minValue
17553      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17554      * valid format (defaults to null).
17555      */
17556     minValue : null,
17557     /**
17558      * @cfg {Date/String} maxValue
17559      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17560      * valid format (defaults to null).
17561      */
17562     maxValue : null,
17563     /**
17564      * @cfg {String} minText
17565      * The error text to display when the date in the cell is before minValue (defaults to
17566      * 'The date in this field must be after {minValue}').
17567      */
17568     minText : "The date in this field must be equal to or after {0}",
17569     /**
17570      * @cfg {String} maxText
17571      * The error text to display when the date in the cell is after maxValue (defaults to
17572      * 'The date in this field must be before {maxValue}').
17573      */
17574     maxText : "The date in this field must be equal to or before {0}",
17575     /**
17576      * @cfg {String} invalidText
17577      * The error text to display when the date in the field is invalid (defaults to
17578      * '{value} is not a valid date - it must be in the format {format}').
17579      */
17580     invalidText : "{0} is not a valid date - it must be in the format {1}",
17581     /**
17582      * @cfg {String} triggerClass
17583      * An additional CSS class used to style the trigger button.  The trigger will always get the
17584      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17585      * which displays a calendar icon).
17586      */
17587     triggerClass : 'x-form-date-trigger',
17588     
17589
17590     /**
17591      * @cfg {Boolean} useIso
17592      * if enabled, then the date field will use a hidden field to store the 
17593      * real value as iso formated date. default (false)
17594      */ 
17595     useIso : false,
17596     /**
17597      * @cfg {String/Object} autoCreate
17598      * A DomHelper element spec, or true for a default element spec (defaults to
17599      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17600      */ 
17601     // private
17602     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17603     
17604     // private
17605     hiddenField: false,
17606     
17607     onRender : function(ct, position)
17608     {
17609         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17610         if (this.useIso) {
17611             //this.el.dom.removeAttribute('name'); 
17612             Roo.log("Changing name?");
17613             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17614             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17615                     'before', true);
17616             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17617             // prevent input submission
17618             this.hiddenName = this.name;
17619         }
17620             
17621             
17622     },
17623     
17624     // private
17625     validateValue : function(value)
17626     {
17627         value = this.formatDate(value);
17628         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17629             Roo.log('super failed');
17630             return false;
17631         }
17632         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17633              return true;
17634         }
17635         var svalue = value;
17636         value = this.parseDate(value);
17637         if(!value){
17638             Roo.log('parse date failed' + svalue);
17639             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17640             return false;
17641         }
17642         var time = value.getTime();
17643         if(this.minValue && time < this.minValue.getTime()){
17644             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17645             return false;
17646         }
17647         if(this.maxValue && time > this.maxValue.getTime()){
17648             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17649             return false;
17650         }
17651         if(this.disabledDays){
17652             var day = value.getDay();
17653             for(var i = 0; i < this.disabledDays.length; i++) {
17654                 if(day === this.disabledDays[i]){
17655                     this.markInvalid(this.disabledDaysText);
17656                     return false;
17657                 }
17658             }
17659         }
17660         var fvalue = this.formatDate(value);
17661         if(this.ddMatch && this.ddMatch.test(fvalue)){
17662             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17663             return false;
17664         }
17665         return true;
17666     },
17667
17668     // private
17669     // Provides logic to override the default TriggerField.validateBlur which just returns true
17670     validateBlur : function(){
17671         return !this.menu || !this.menu.isVisible();
17672     },
17673     
17674     getName: function()
17675     {
17676         // returns hidden if it's set..
17677         if (!this.rendered) {return ''};
17678         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17679         
17680     },
17681
17682     /**
17683      * Returns the current date value of the date field.
17684      * @return {Date} The date value
17685      */
17686     getValue : function(){
17687         
17688         return  this.hiddenField ?
17689                 this.hiddenField.value :
17690                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17691     },
17692
17693     /**
17694      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17695      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17696      * (the default format used is "m/d/y").
17697      * <br />Usage:
17698      * <pre><code>
17699 //All of these calls set the same date value (May 4, 2006)
17700
17701 //Pass a date object:
17702 var dt = new Date('5/4/06');
17703 dateField.setValue(dt);
17704
17705 //Pass a date string (default format):
17706 dateField.setValue('5/4/06');
17707
17708 //Pass a date string (custom format):
17709 dateField.format = 'Y-m-d';
17710 dateField.setValue('2006-5-4');
17711 </code></pre>
17712      * @param {String/Date} date The date or valid date string
17713      */
17714     setValue : function(date){
17715         if (this.hiddenField) {
17716             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17717         }
17718         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17719         // make sure the value field is always stored as a date..
17720         this.value = this.parseDate(date);
17721         
17722         
17723     },
17724
17725     // private
17726     parseDate : function(value){
17727         if(!value || value instanceof Date){
17728             return value;
17729         }
17730         var v = Date.parseDate(value, this.format);
17731          if (!v && this.useIso) {
17732             v = Date.parseDate(value, 'Y-m-d');
17733         }
17734         if(!v && this.altFormats){
17735             if(!this.altFormatsArray){
17736                 this.altFormatsArray = this.altFormats.split("|");
17737             }
17738             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17739                 v = Date.parseDate(value, this.altFormatsArray[i]);
17740             }
17741         }
17742         return v;
17743     },
17744
17745     // private
17746     formatDate : function(date, fmt){
17747         return (!date || !(date instanceof Date)) ?
17748                date : date.dateFormat(fmt || this.format);
17749     },
17750
17751     // private
17752     menuListeners : {
17753         select: function(m, d){
17754             
17755             this.setValue(d);
17756             this.fireEvent('select', this, d);
17757         },
17758         show : function(){ // retain focus styling
17759             this.onFocus();
17760         },
17761         hide : function(){
17762             this.focus.defer(10, this);
17763             var ml = this.menuListeners;
17764             this.menu.un("select", ml.select,  this);
17765             this.menu.un("show", ml.show,  this);
17766             this.menu.un("hide", ml.hide,  this);
17767         }
17768     },
17769
17770     // private
17771     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17772     onTriggerClick : function(){
17773         if(this.disabled){
17774             return;
17775         }
17776         if(this.menu == null){
17777             this.menu = new Roo.menu.DateMenu();
17778         }
17779         Roo.apply(this.menu.picker,  {
17780             showClear: this.allowBlank,
17781             minDate : this.minValue,
17782             maxDate : this.maxValue,
17783             disabledDatesRE : this.ddMatch,
17784             disabledDatesText : this.disabledDatesText,
17785             disabledDays : this.disabledDays,
17786             disabledDaysText : this.disabledDaysText,
17787             format : this.useIso ? 'Y-m-d' : this.format,
17788             minText : String.format(this.minText, this.formatDate(this.minValue)),
17789             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17790         });
17791         this.menu.on(Roo.apply({}, this.menuListeners, {
17792             scope:this
17793         }));
17794         this.menu.picker.setValue(this.getValue() || new Date());
17795         this.menu.show(this.el, "tl-bl?");
17796     },
17797
17798     beforeBlur : function(){
17799         var v = this.parseDate(this.getRawValue());
17800         if(v){
17801             this.setValue(v);
17802         }
17803     },
17804
17805     /*@
17806      * overide
17807      * 
17808      */
17809     isDirty : function() {
17810         if(this.disabled) {
17811             return false;
17812         }
17813         
17814         if(typeof(this.startValue) === 'undefined'){
17815             return false;
17816         }
17817         
17818         return String(this.getValue()) !== String(this.startValue);
17819         
17820     },
17821     // @overide
17822     cleanLeadingSpace : function(e)
17823     {
17824        return;
17825     }
17826     
17827 });/*
17828  * Based on:
17829  * Ext JS Library 1.1.1
17830  * Copyright(c) 2006-2007, Ext JS, LLC.
17831  *
17832  * Originally Released Under LGPL - original licence link has changed is not relivant.
17833  *
17834  * Fork - LGPL
17835  * <script type="text/javascript">
17836  */
17837  
17838 /**
17839  * @class Roo.form.MonthField
17840  * @extends Roo.form.TriggerField
17841  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17842 * @constructor
17843 * Create a new MonthField
17844 * @param {Object} config
17845  */
17846 Roo.form.MonthField = function(config){
17847     
17848     Roo.form.MonthField.superclass.constructor.call(this, config);
17849     
17850       this.addEvents({
17851          
17852         /**
17853          * @event select
17854          * Fires when a date is selected
17855              * @param {Roo.form.MonthFieeld} combo This combo box
17856              * @param {Date} date The date selected
17857              */
17858         'select' : true
17859          
17860     });
17861     
17862     
17863     if(typeof this.minValue == "string") {
17864         this.minValue = this.parseDate(this.minValue);
17865     }
17866     if(typeof this.maxValue == "string") {
17867         this.maxValue = this.parseDate(this.maxValue);
17868     }
17869     this.ddMatch = null;
17870     if(this.disabledDates){
17871         var dd = this.disabledDates;
17872         var re = "(?:";
17873         for(var i = 0; i < dd.length; i++){
17874             re += dd[i];
17875             if(i != dd.length-1) {
17876                 re += "|";
17877             }
17878         }
17879         this.ddMatch = new RegExp(re + ")");
17880     }
17881 };
17882
17883 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17884     /**
17885      * @cfg {String} format
17886      * The default date format string which can be overriden for localization support.  The format must be
17887      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17888      */
17889     format : "M Y",
17890     /**
17891      * @cfg {String} altFormats
17892      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17893      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17894      */
17895     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17896     /**
17897      * @cfg {Array} disabledDays
17898      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17899      */
17900     disabledDays : [0,1,2,3,4,5,6],
17901     /**
17902      * @cfg {String} disabledDaysText
17903      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17904      */
17905     disabledDaysText : "Disabled",
17906     /**
17907      * @cfg {Array} disabledDates
17908      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17909      * expression so they are very powerful. Some examples:
17910      * <ul>
17911      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17912      * <li>["03/08", "09/16"] would disable those days for every year</li>
17913      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17914      * <li>["03/../2006"] would disable every day in March 2006</li>
17915      * <li>["^03"] would disable every day in every March</li>
17916      * </ul>
17917      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17918      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17919      */
17920     disabledDates : null,
17921     /**
17922      * @cfg {String} disabledDatesText
17923      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17924      */
17925     disabledDatesText : "Disabled",
17926     /**
17927      * @cfg {Date/String} minValue
17928      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17929      * valid format (defaults to null).
17930      */
17931     minValue : null,
17932     /**
17933      * @cfg {Date/String} maxValue
17934      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17935      * valid format (defaults to null).
17936      */
17937     maxValue : null,
17938     /**
17939      * @cfg {String} minText
17940      * The error text to display when the date in the cell is before minValue (defaults to
17941      * 'The date in this field must be after {minValue}').
17942      */
17943     minText : "The date in this field must be equal to or after {0}",
17944     /**
17945      * @cfg {String} maxTextf
17946      * The error text to display when the date in the cell is after maxValue (defaults to
17947      * 'The date in this field must be before {maxValue}').
17948      */
17949     maxText : "The date in this field must be equal to or before {0}",
17950     /**
17951      * @cfg {String} invalidText
17952      * The error text to display when the date in the field is invalid (defaults to
17953      * '{value} is not a valid date - it must be in the format {format}').
17954      */
17955     invalidText : "{0} is not a valid date - it must be in the format {1}",
17956     /**
17957      * @cfg {String} triggerClass
17958      * An additional CSS class used to style the trigger button.  The trigger will always get the
17959      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17960      * which displays a calendar icon).
17961      */
17962     triggerClass : 'x-form-date-trigger',
17963     
17964
17965     /**
17966      * @cfg {Boolean} useIso
17967      * if enabled, then the date field will use a hidden field to store the 
17968      * real value as iso formated date. default (true)
17969      */ 
17970     useIso : true,
17971     /**
17972      * @cfg {String/Object} autoCreate
17973      * A DomHelper element spec, or true for a default element spec (defaults to
17974      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17975      */ 
17976     // private
17977     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17978     
17979     // private
17980     hiddenField: false,
17981     
17982     hideMonthPicker : false,
17983     
17984     onRender : function(ct, position)
17985     {
17986         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17987         if (this.useIso) {
17988             this.el.dom.removeAttribute('name'); 
17989             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17990                     'before', true);
17991             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17992             // prevent input submission
17993             this.hiddenName = this.name;
17994         }
17995             
17996             
17997     },
17998     
17999     // private
18000     validateValue : function(value)
18001     {
18002         value = this.formatDate(value);
18003         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18004             return false;
18005         }
18006         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18007              return true;
18008         }
18009         var svalue = value;
18010         value = this.parseDate(value);
18011         if(!value){
18012             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18013             return false;
18014         }
18015         var time = value.getTime();
18016         if(this.minValue && time < this.minValue.getTime()){
18017             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18018             return false;
18019         }
18020         if(this.maxValue && time > this.maxValue.getTime()){
18021             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18022             return false;
18023         }
18024         /*if(this.disabledDays){
18025             var day = value.getDay();
18026             for(var i = 0; i < this.disabledDays.length; i++) {
18027                 if(day === this.disabledDays[i]){
18028                     this.markInvalid(this.disabledDaysText);
18029                     return false;
18030                 }
18031             }
18032         }
18033         */
18034         var fvalue = this.formatDate(value);
18035         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18036             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18037             return false;
18038         }
18039         */
18040         return true;
18041     },
18042
18043     // private
18044     // Provides logic to override the default TriggerField.validateBlur which just returns true
18045     validateBlur : function(){
18046         return !this.menu || !this.menu.isVisible();
18047     },
18048
18049     /**
18050      * Returns the current date value of the date field.
18051      * @return {Date} The date value
18052      */
18053     getValue : function(){
18054         
18055         
18056         
18057         return  this.hiddenField ?
18058                 this.hiddenField.value :
18059                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18060     },
18061
18062     /**
18063      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18064      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18065      * (the default format used is "m/d/y").
18066      * <br />Usage:
18067      * <pre><code>
18068 //All of these calls set the same date value (May 4, 2006)
18069
18070 //Pass a date object:
18071 var dt = new Date('5/4/06');
18072 monthField.setValue(dt);
18073
18074 //Pass a date string (default format):
18075 monthField.setValue('5/4/06');
18076
18077 //Pass a date string (custom format):
18078 monthField.format = 'Y-m-d';
18079 monthField.setValue('2006-5-4');
18080 </code></pre>
18081      * @param {String/Date} date The date or valid date string
18082      */
18083     setValue : function(date){
18084         Roo.log('month setValue' + date);
18085         // can only be first of month..
18086         
18087         var val = this.parseDate(date);
18088         
18089         if (this.hiddenField) {
18090             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18091         }
18092         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18093         this.value = this.parseDate(date);
18094     },
18095
18096     // private
18097     parseDate : function(value){
18098         if(!value || value instanceof Date){
18099             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18100             return value;
18101         }
18102         var v = Date.parseDate(value, this.format);
18103         if (!v && this.useIso) {
18104             v = Date.parseDate(value, 'Y-m-d');
18105         }
18106         if (v) {
18107             // 
18108             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18109         }
18110         
18111         
18112         if(!v && this.altFormats){
18113             if(!this.altFormatsArray){
18114                 this.altFormatsArray = this.altFormats.split("|");
18115             }
18116             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18117                 v = Date.parseDate(value, this.altFormatsArray[i]);
18118             }
18119         }
18120         return v;
18121     },
18122
18123     // private
18124     formatDate : function(date, fmt){
18125         return (!date || !(date instanceof Date)) ?
18126                date : date.dateFormat(fmt || this.format);
18127     },
18128
18129     // private
18130     menuListeners : {
18131         select: function(m, d){
18132             this.setValue(d);
18133             this.fireEvent('select', this, d);
18134         },
18135         show : function(){ // retain focus styling
18136             this.onFocus();
18137         },
18138         hide : function(){
18139             this.focus.defer(10, this);
18140             var ml = this.menuListeners;
18141             this.menu.un("select", ml.select,  this);
18142             this.menu.un("show", ml.show,  this);
18143             this.menu.un("hide", ml.hide,  this);
18144         }
18145     },
18146     // private
18147     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18148     onTriggerClick : function(){
18149         if(this.disabled){
18150             return;
18151         }
18152         if(this.menu == null){
18153             this.menu = new Roo.menu.DateMenu();
18154            
18155         }
18156         
18157         Roo.apply(this.menu.picker,  {
18158             
18159             showClear: this.allowBlank,
18160             minDate : this.minValue,
18161             maxDate : this.maxValue,
18162             disabledDatesRE : this.ddMatch,
18163             disabledDatesText : this.disabledDatesText,
18164             
18165             format : this.useIso ? 'Y-m-d' : this.format,
18166             minText : String.format(this.minText, this.formatDate(this.minValue)),
18167             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18168             
18169         });
18170          this.menu.on(Roo.apply({}, this.menuListeners, {
18171             scope:this
18172         }));
18173        
18174         
18175         var m = this.menu;
18176         var p = m.picker;
18177         
18178         // hide month picker get's called when we called by 'before hide';
18179         
18180         var ignorehide = true;
18181         p.hideMonthPicker  = function(disableAnim){
18182             if (ignorehide) {
18183                 return;
18184             }
18185              if(this.monthPicker){
18186                 Roo.log("hideMonthPicker called");
18187                 if(disableAnim === true){
18188                     this.monthPicker.hide();
18189                 }else{
18190                     this.monthPicker.slideOut('t', {duration:.2});
18191                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18192                     p.fireEvent("select", this, this.value);
18193                     m.hide();
18194                 }
18195             }
18196         }
18197         
18198         Roo.log('picker set value');
18199         Roo.log(this.getValue());
18200         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18201         m.show(this.el, 'tl-bl?');
18202         ignorehide  = false;
18203         // this will trigger hideMonthPicker..
18204         
18205         
18206         // hidden the day picker
18207         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18208         
18209         
18210         
18211       
18212         
18213         p.showMonthPicker.defer(100, p);
18214     
18215         
18216        
18217     },
18218
18219     beforeBlur : function(){
18220         var v = this.parseDate(this.getRawValue());
18221         if(v){
18222             this.setValue(v);
18223         }
18224     }
18225
18226     /** @cfg {Boolean} grow @hide */
18227     /** @cfg {Number} growMin @hide */
18228     /** @cfg {Number} growMax @hide */
18229     /**
18230      * @hide
18231      * @method autoSize
18232      */
18233 });/*
18234  * Based on:
18235  * Ext JS Library 1.1.1
18236  * Copyright(c) 2006-2007, Ext JS, LLC.
18237  *
18238  * Originally Released Under LGPL - original licence link has changed is not relivant.
18239  *
18240  * Fork - LGPL
18241  * <script type="text/javascript">
18242  */
18243  
18244
18245 /**
18246  * @class Roo.form.ComboBox
18247  * @extends Roo.form.TriggerField
18248  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18249  * @constructor
18250  * Create a new ComboBox.
18251  * @param {Object} config Configuration options
18252  */
18253 Roo.form.ComboBox = function(config){
18254     Roo.form.ComboBox.superclass.constructor.call(this, config);
18255     this.addEvents({
18256         /**
18257          * @event expand
18258          * Fires when the dropdown list is expanded
18259              * @param {Roo.form.ComboBox} combo This combo box
18260              */
18261         'expand' : true,
18262         /**
18263          * @event collapse
18264          * Fires when the dropdown list is collapsed
18265              * @param {Roo.form.ComboBox} combo This combo box
18266              */
18267         'collapse' : true,
18268         /**
18269          * @event beforeselect
18270          * Fires before a list item is selected. Return false to cancel the selection.
18271              * @param {Roo.form.ComboBox} combo This combo box
18272              * @param {Roo.data.Record} record The data record returned from the underlying store
18273              * @param {Number} index The index of the selected item in the dropdown list
18274              */
18275         'beforeselect' : true,
18276         /**
18277          * @event select
18278          * Fires when a list item is selected
18279              * @param {Roo.form.ComboBox} combo This combo box
18280              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18281              * @param {Number} index The index of the selected item in the dropdown list
18282              */
18283         'select' : true,
18284         /**
18285          * @event beforequery
18286          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18287          * The event object passed has these properties:
18288              * @param {Roo.form.ComboBox} combo This combo box
18289              * @param {String} query The query
18290              * @param {Boolean} forceAll true to force "all" query
18291              * @param {Boolean} cancel true to cancel the query
18292              * @param {Object} e The query event object
18293              */
18294         'beforequery': true,
18295          /**
18296          * @event add
18297          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18298              * @param {Roo.form.ComboBox} combo This combo box
18299              */
18300         'add' : true,
18301         /**
18302          * @event edit
18303          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18304              * @param {Roo.form.ComboBox} combo This combo box
18305              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18306              */
18307         'edit' : true
18308         
18309         
18310     });
18311     if(this.transform){
18312         this.allowDomMove = false;
18313         var s = Roo.getDom(this.transform);
18314         if(!this.hiddenName){
18315             this.hiddenName = s.name;
18316         }
18317         if(!this.store){
18318             this.mode = 'local';
18319             var d = [], opts = s.options;
18320             for(var i = 0, len = opts.length;i < len; i++){
18321                 var o = opts[i];
18322                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18323                 if(o.selected) {
18324                     this.value = value;
18325                 }
18326                 d.push([value, o.text]);
18327             }
18328             this.store = new Roo.data.SimpleStore({
18329                 'id': 0,
18330                 fields: ['value', 'text'],
18331                 data : d
18332             });
18333             this.valueField = 'value';
18334             this.displayField = 'text';
18335         }
18336         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18337         if(!this.lazyRender){
18338             this.target = true;
18339             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18340             s.parentNode.removeChild(s); // remove it
18341             this.render(this.el.parentNode);
18342         }else{
18343             s.parentNode.removeChild(s); // remove it
18344         }
18345
18346     }
18347     if (this.store) {
18348         this.store = Roo.factory(this.store, Roo.data);
18349     }
18350     
18351     this.selectedIndex = -1;
18352     if(this.mode == 'local'){
18353         if(config.queryDelay === undefined){
18354             this.queryDelay = 10;
18355         }
18356         if(config.minChars === undefined){
18357             this.minChars = 0;
18358         }
18359     }
18360 };
18361
18362 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18363     /**
18364      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18365      */
18366     /**
18367      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18368      * rendering into an Roo.Editor, defaults to false)
18369      */
18370     /**
18371      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18372      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18373      */
18374     /**
18375      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18376      */
18377     /**
18378      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18379      * the dropdown list (defaults to undefined, with no header element)
18380      */
18381
18382      /**
18383      * @cfg {String/Roo.Template} tpl The template to use to render the output
18384      */
18385      
18386     // private
18387     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18388     /**
18389      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18390      */
18391     listWidth: undefined,
18392     /**
18393      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18394      * mode = 'remote' or 'text' if mode = 'local')
18395      */
18396     displayField: undefined,
18397     /**
18398      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18399      * mode = 'remote' or 'value' if mode = 'local'). 
18400      * Note: use of a valueField requires the user make a selection
18401      * in order for a value to be mapped.
18402      */
18403     valueField: undefined,
18404     
18405     
18406     /**
18407      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18408      * field's data value (defaults to the underlying DOM element's name)
18409      */
18410     hiddenName: undefined,
18411     /**
18412      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18413      */
18414     listClass: '',
18415     /**
18416      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18417      */
18418     selectedClass: 'x-combo-selected',
18419     /**
18420      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18421      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18422      * which displays a downward arrow icon).
18423      */
18424     triggerClass : 'x-form-arrow-trigger',
18425     /**
18426      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18427      */
18428     shadow:'sides',
18429     /**
18430      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18431      * anchor positions (defaults to 'tl-bl')
18432      */
18433     listAlign: 'tl-bl?',
18434     /**
18435      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18436      */
18437     maxHeight: 300,
18438     /**
18439      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18440      * query specified by the allQuery config option (defaults to 'query')
18441      */
18442     triggerAction: 'query',
18443     /**
18444      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18445      * (defaults to 4, does not apply if editable = false)
18446      */
18447     minChars : 4,
18448     /**
18449      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18450      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18451      */
18452     typeAhead: false,
18453     /**
18454      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18455      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18456      */
18457     queryDelay: 500,
18458     /**
18459      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18460      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18461      */
18462     pageSize: 0,
18463     /**
18464      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18465      * when editable = true (defaults to false)
18466      */
18467     selectOnFocus:false,
18468     /**
18469      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18470      */
18471     queryParam: 'query',
18472     /**
18473      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18474      * when mode = 'remote' (defaults to 'Loading...')
18475      */
18476     loadingText: 'Loading...',
18477     /**
18478      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18479      */
18480     resizable: false,
18481     /**
18482      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18483      */
18484     handleHeight : 8,
18485     /**
18486      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18487      * traditional select (defaults to true)
18488      */
18489     editable: true,
18490     /**
18491      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18492      */
18493     allQuery: '',
18494     /**
18495      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18496      */
18497     mode: 'remote',
18498     /**
18499      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18500      * listWidth has a higher value)
18501      */
18502     minListWidth : 70,
18503     /**
18504      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18505      * allow the user to set arbitrary text into the field (defaults to false)
18506      */
18507     forceSelection:false,
18508     /**
18509      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18510      * if typeAhead = true (defaults to 250)
18511      */
18512     typeAheadDelay : 250,
18513     /**
18514      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18515      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18516      */
18517     valueNotFoundText : undefined,
18518     /**
18519      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18520      */
18521     blockFocus : false,
18522     
18523     /**
18524      * @cfg {Boolean} disableClear Disable showing of clear button.
18525      */
18526     disableClear : false,
18527     /**
18528      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18529      */
18530     alwaysQuery : false,
18531     
18532     //private
18533     addicon : false,
18534     editicon: false,
18535     
18536     // element that contains real text value.. (when hidden is used..)
18537      
18538     // private
18539     onRender : function(ct, position)
18540     {
18541         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18542         
18543         if(this.hiddenName){
18544             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18545                     'before', true);
18546             this.hiddenField.value =
18547                 this.hiddenValue !== undefined ? this.hiddenValue :
18548                 this.value !== undefined ? this.value : '';
18549
18550             // prevent input submission
18551             this.el.dom.removeAttribute('name');
18552              
18553              
18554         }
18555         
18556         if(Roo.isGecko){
18557             this.el.dom.setAttribute('autocomplete', 'off');
18558         }
18559
18560         var cls = 'x-combo-list';
18561
18562         this.list = new Roo.Layer({
18563             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18564         });
18565
18566         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18567         this.list.setWidth(lw);
18568         this.list.swallowEvent('mousewheel');
18569         this.assetHeight = 0;
18570
18571         if(this.title){
18572             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18573             this.assetHeight += this.header.getHeight();
18574         }
18575
18576         this.innerList = this.list.createChild({cls:cls+'-inner'});
18577         this.innerList.on('mouseover', this.onViewOver, this);
18578         this.innerList.on('mousemove', this.onViewMove, this);
18579         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18580         
18581         if(this.allowBlank && !this.pageSize && !this.disableClear){
18582             this.footer = this.list.createChild({cls:cls+'-ft'});
18583             this.pageTb = new Roo.Toolbar(this.footer);
18584            
18585         }
18586         if(this.pageSize){
18587             this.footer = this.list.createChild({cls:cls+'-ft'});
18588             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18589                     {pageSize: this.pageSize});
18590             
18591         }
18592         
18593         if (this.pageTb && this.allowBlank && !this.disableClear) {
18594             var _this = this;
18595             this.pageTb.add(new Roo.Toolbar.Fill(), {
18596                 cls: 'x-btn-icon x-btn-clear',
18597                 text: '&#160;',
18598                 handler: function()
18599                 {
18600                     _this.collapse();
18601                     _this.clearValue();
18602                     _this.onSelect(false, -1);
18603                 }
18604             });
18605         }
18606         if (this.footer) {
18607             this.assetHeight += this.footer.getHeight();
18608         }
18609         
18610
18611         if(!this.tpl){
18612             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18613         }
18614
18615         this.view = new Roo.View(this.innerList, this.tpl, {
18616             singleSelect:true,
18617             store: this.store,
18618             selectedClass: this.selectedClass
18619         });
18620
18621         this.view.on('click', this.onViewClick, this);
18622
18623         this.store.on('beforeload', this.onBeforeLoad, this);
18624         this.store.on('load', this.onLoad, this);
18625         this.store.on('loadexception', this.onLoadException, this);
18626
18627         if(this.resizable){
18628             this.resizer = new Roo.Resizable(this.list,  {
18629                pinned:true, handles:'se'
18630             });
18631             this.resizer.on('resize', function(r, w, h){
18632                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18633                 this.listWidth = w;
18634                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18635                 this.restrictHeight();
18636             }, this);
18637             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18638         }
18639         if(!this.editable){
18640             this.editable = true;
18641             this.setEditable(false);
18642         }  
18643         
18644         
18645         if (typeof(this.events.add.listeners) != 'undefined') {
18646             
18647             this.addicon = this.wrap.createChild(
18648                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18649        
18650             this.addicon.on('click', function(e) {
18651                 this.fireEvent('add', this);
18652             }, this);
18653         }
18654         if (typeof(this.events.edit.listeners) != 'undefined') {
18655             
18656             this.editicon = this.wrap.createChild(
18657                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18658             if (this.addicon) {
18659                 this.editicon.setStyle('margin-left', '40px');
18660             }
18661             this.editicon.on('click', function(e) {
18662                 
18663                 // we fire even  if inothing is selected..
18664                 this.fireEvent('edit', this, this.lastData );
18665                 
18666             }, this);
18667         }
18668         
18669         
18670         
18671     },
18672
18673     // private
18674     initEvents : function(){
18675         Roo.form.ComboBox.superclass.initEvents.call(this);
18676
18677         this.keyNav = new Roo.KeyNav(this.el, {
18678             "up" : function(e){
18679                 this.inKeyMode = true;
18680                 this.selectPrev();
18681             },
18682
18683             "down" : function(e){
18684                 if(!this.isExpanded()){
18685                     this.onTriggerClick();
18686                 }else{
18687                     this.inKeyMode = true;
18688                     this.selectNext();
18689                 }
18690             },
18691
18692             "enter" : function(e){
18693                 this.onViewClick();
18694                 //return true;
18695             },
18696
18697             "esc" : function(e){
18698                 this.collapse();
18699             },
18700
18701             "tab" : function(e){
18702                 this.onViewClick(false);
18703                 this.fireEvent("specialkey", this, e);
18704                 return true;
18705             },
18706
18707             scope : this,
18708
18709             doRelay : function(foo, bar, hname){
18710                 if(hname == 'down' || this.scope.isExpanded()){
18711                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18712                 }
18713                 return true;
18714             },
18715
18716             forceKeyDown: true
18717         });
18718         this.queryDelay = Math.max(this.queryDelay || 10,
18719                 this.mode == 'local' ? 10 : 250);
18720         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18721         if(this.typeAhead){
18722             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18723         }
18724         if(this.editable !== false){
18725             this.el.on("keyup", this.onKeyUp, this);
18726         }
18727         if(this.forceSelection){
18728             this.on('blur', this.doForce, this);
18729         }
18730     },
18731
18732     onDestroy : function(){
18733         if(this.view){
18734             this.view.setStore(null);
18735             this.view.el.removeAllListeners();
18736             this.view.el.remove();
18737             this.view.purgeListeners();
18738         }
18739         if(this.list){
18740             this.list.destroy();
18741         }
18742         if(this.store){
18743             this.store.un('beforeload', this.onBeforeLoad, this);
18744             this.store.un('load', this.onLoad, this);
18745             this.store.un('loadexception', this.onLoadException, this);
18746         }
18747         Roo.form.ComboBox.superclass.onDestroy.call(this);
18748     },
18749
18750     // private
18751     fireKey : function(e){
18752         if(e.isNavKeyPress() && !this.list.isVisible()){
18753             this.fireEvent("specialkey", this, e);
18754         }
18755     },
18756
18757     // private
18758     onResize: function(w, h){
18759         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18760         
18761         if(typeof w != 'number'){
18762             // we do not handle it!?!?
18763             return;
18764         }
18765         var tw = this.trigger.getWidth();
18766         tw += this.addicon ? this.addicon.getWidth() : 0;
18767         tw += this.editicon ? this.editicon.getWidth() : 0;
18768         var x = w - tw;
18769         this.el.setWidth( this.adjustWidth('input', x));
18770             
18771         this.trigger.setStyle('left', x+'px');
18772         
18773         if(this.list && this.listWidth === undefined){
18774             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18775             this.list.setWidth(lw);
18776             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18777         }
18778         
18779     
18780         
18781     },
18782
18783     /**
18784      * Allow or prevent the user from directly editing the field text.  If false is passed,
18785      * the user will only be able to select from the items defined in the dropdown list.  This method
18786      * is the runtime equivalent of setting the 'editable' config option at config time.
18787      * @param {Boolean} value True to allow the user to directly edit the field text
18788      */
18789     setEditable : function(value){
18790         if(value == this.editable){
18791             return;
18792         }
18793         this.editable = value;
18794         if(!value){
18795             this.el.dom.setAttribute('readOnly', true);
18796             this.el.on('mousedown', this.onTriggerClick,  this);
18797             this.el.addClass('x-combo-noedit');
18798         }else{
18799             this.el.dom.setAttribute('readOnly', false);
18800             this.el.un('mousedown', this.onTriggerClick,  this);
18801             this.el.removeClass('x-combo-noedit');
18802         }
18803     },
18804
18805     // private
18806     onBeforeLoad : function(){
18807         if(!this.hasFocus){
18808             return;
18809         }
18810         this.innerList.update(this.loadingText ?
18811                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18812         this.restrictHeight();
18813         this.selectedIndex = -1;
18814     },
18815
18816     // private
18817     onLoad : function(){
18818         if(!this.hasFocus){
18819             return;
18820         }
18821         if(this.store.getCount() > 0){
18822             this.expand();
18823             this.restrictHeight();
18824             if(this.lastQuery == this.allQuery){
18825                 if(this.editable){
18826                     this.el.dom.select();
18827                 }
18828                 if(!this.selectByValue(this.value, true)){
18829                     this.select(0, true);
18830                 }
18831             }else{
18832                 this.selectNext();
18833                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18834                     this.taTask.delay(this.typeAheadDelay);
18835                 }
18836             }
18837         }else{
18838             this.onEmptyResults();
18839         }
18840         //this.el.focus();
18841     },
18842     // private
18843     onLoadException : function()
18844     {
18845         this.collapse();
18846         Roo.log(this.store.reader.jsonData);
18847         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18848             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18849         }
18850         
18851         
18852     },
18853     // private
18854     onTypeAhead : function(){
18855         if(this.store.getCount() > 0){
18856             var r = this.store.getAt(0);
18857             var newValue = r.data[this.displayField];
18858             var len = newValue.length;
18859             var selStart = this.getRawValue().length;
18860             if(selStart != len){
18861                 this.setRawValue(newValue);
18862                 this.selectText(selStart, newValue.length);
18863             }
18864         }
18865     },
18866
18867     // private
18868     onSelect : function(record, index){
18869         if(this.fireEvent('beforeselect', this, record, index) !== false){
18870             this.setFromData(index > -1 ? record.data : false);
18871             this.collapse();
18872             this.fireEvent('select', this, record, index);
18873         }
18874     },
18875
18876     /**
18877      * Returns the currently selected field value or empty string if no value is set.
18878      * @return {String} value The selected value
18879      */
18880     getValue : function(){
18881         if(this.valueField){
18882             return typeof this.value != 'undefined' ? this.value : '';
18883         }
18884         return Roo.form.ComboBox.superclass.getValue.call(this);
18885     },
18886
18887     /**
18888      * Clears any text/value currently set in the field
18889      */
18890     clearValue : function(){
18891         if(this.hiddenField){
18892             this.hiddenField.value = '';
18893         }
18894         this.value = '';
18895         this.setRawValue('');
18896         this.lastSelectionText = '';
18897         
18898     },
18899
18900     /**
18901      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18902      * will be displayed in the field.  If the value does not match the data value of an existing item,
18903      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18904      * Otherwise the field will be blank (although the value will still be set).
18905      * @param {String} value The value to match
18906      */
18907     setValue : function(v){
18908         var text = v;
18909         if(this.valueField){
18910             var r = this.findRecord(this.valueField, v);
18911             if(r){
18912                 text = r.data[this.displayField];
18913             }else if(this.valueNotFoundText !== undefined){
18914                 text = this.valueNotFoundText;
18915             }
18916         }
18917         this.lastSelectionText = text;
18918         if(this.hiddenField){
18919             this.hiddenField.value = v;
18920         }
18921         Roo.form.ComboBox.superclass.setValue.call(this, text);
18922         this.value = v;
18923     },
18924     /**
18925      * @property {Object} the last set data for the element
18926      */
18927     
18928     lastData : false,
18929     /**
18930      * Sets the value of the field based on a object which is related to the record format for the store.
18931      * @param {Object} value the value to set as. or false on reset?
18932      */
18933     setFromData : function(o){
18934         var dv = ''; // display value
18935         var vv = ''; // value value..
18936         this.lastData = o;
18937         if (this.displayField) {
18938             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18939         } else {
18940             // this is an error condition!!!
18941             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18942         }
18943         
18944         if(this.valueField){
18945             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18946         }
18947         if(this.hiddenField){
18948             this.hiddenField.value = vv;
18949             
18950             this.lastSelectionText = dv;
18951             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18952             this.value = vv;
18953             return;
18954         }
18955         // no hidden field.. - we store the value in 'value', but still display
18956         // display field!!!!
18957         this.lastSelectionText = dv;
18958         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18959         this.value = vv;
18960         
18961         
18962     },
18963     // private
18964     reset : function(){
18965         // overridden so that last data is reset..
18966         this.setValue(this.resetValue);
18967         this.originalValue = this.getValue();
18968         this.clearInvalid();
18969         this.lastData = false;
18970         if (this.view) {
18971             this.view.clearSelections();
18972         }
18973     },
18974     // private
18975     findRecord : function(prop, value){
18976         var record;
18977         if(this.store.getCount() > 0){
18978             this.store.each(function(r){
18979                 if(r.data[prop] == value){
18980                     record = r;
18981                     return false;
18982                 }
18983                 return true;
18984             });
18985         }
18986         return record;
18987     },
18988     
18989     getName: function()
18990     {
18991         // returns hidden if it's set..
18992         if (!this.rendered) {return ''};
18993         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18994         
18995     },
18996     // private
18997     onViewMove : function(e, t){
18998         this.inKeyMode = false;
18999     },
19000
19001     // private
19002     onViewOver : function(e, t){
19003         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19004             return;
19005         }
19006         var item = this.view.findItemFromChild(t);
19007         if(item){
19008             var index = this.view.indexOf(item);
19009             this.select(index, false);
19010         }
19011     },
19012
19013     // private
19014     onViewClick : function(doFocus)
19015     {
19016         var index = this.view.getSelectedIndexes()[0];
19017         var r = this.store.getAt(index);
19018         if(r){
19019             this.onSelect(r, index);
19020         }
19021         if(doFocus !== false && !this.blockFocus){
19022             this.el.focus();
19023         }
19024     },
19025
19026     // private
19027     restrictHeight : function(){
19028         this.innerList.dom.style.height = '';
19029         var inner = this.innerList.dom;
19030         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19031         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19032         this.list.beginUpdate();
19033         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19034         this.list.alignTo(this.el, this.listAlign);
19035         this.list.endUpdate();
19036     },
19037
19038     // private
19039     onEmptyResults : function(){
19040         this.collapse();
19041     },
19042
19043     /**
19044      * Returns true if the dropdown list is expanded, else false.
19045      */
19046     isExpanded : function(){
19047         return this.list.isVisible();
19048     },
19049
19050     /**
19051      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19052      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19053      * @param {String} value The data value of the item to select
19054      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19055      * selected item if it is not currently in view (defaults to true)
19056      * @return {Boolean} True if the value matched an item in the list, else false
19057      */
19058     selectByValue : function(v, scrollIntoView){
19059         if(v !== undefined && v !== null){
19060             var r = this.findRecord(this.valueField || this.displayField, v);
19061             if(r){
19062                 this.select(this.store.indexOf(r), scrollIntoView);
19063                 return true;
19064             }
19065         }
19066         return false;
19067     },
19068
19069     /**
19070      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19071      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19072      * @param {Number} index The zero-based index of the list item to select
19073      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19074      * selected item if it is not currently in view (defaults to true)
19075      */
19076     select : function(index, scrollIntoView){
19077         this.selectedIndex = index;
19078         this.view.select(index);
19079         if(scrollIntoView !== false){
19080             var el = this.view.getNode(index);
19081             if(el){
19082                 this.innerList.scrollChildIntoView(el, false);
19083             }
19084         }
19085     },
19086
19087     // private
19088     selectNext : function(){
19089         var ct = this.store.getCount();
19090         if(ct > 0){
19091             if(this.selectedIndex == -1){
19092                 this.select(0);
19093             }else if(this.selectedIndex < ct-1){
19094                 this.select(this.selectedIndex+1);
19095             }
19096         }
19097     },
19098
19099     // private
19100     selectPrev : function(){
19101         var ct = this.store.getCount();
19102         if(ct > 0){
19103             if(this.selectedIndex == -1){
19104                 this.select(0);
19105             }else if(this.selectedIndex != 0){
19106                 this.select(this.selectedIndex-1);
19107             }
19108         }
19109     },
19110
19111     // private
19112     onKeyUp : function(e){
19113         if(this.editable !== false && !e.isSpecialKey()){
19114             this.lastKey = e.getKey();
19115             this.dqTask.delay(this.queryDelay);
19116         }
19117     },
19118
19119     // private
19120     validateBlur : function(){
19121         return !this.list || !this.list.isVisible();   
19122     },
19123
19124     // private
19125     initQuery : function(){
19126         this.doQuery(this.getRawValue());
19127     },
19128
19129     // private
19130     doForce : function(){
19131         if(this.el.dom.value.length > 0){
19132             this.el.dom.value =
19133                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19134              
19135         }
19136     },
19137
19138     /**
19139      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19140      * query allowing the query action to be canceled if needed.
19141      * @param {String} query The SQL query to execute
19142      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19143      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19144      * saved in the current store (defaults to false)
19145      */
19146     doQuery : function(q, forceAll){
19147         if(q === undefined || q === null){
19148             q = '';
19149         }
19150         var qe = {
19151             query: q,
19152             forceAll: forceAll,
19153             combo: this,
19154             cancel:false
19155         };
19156         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19157             return false;
19158         }
19159         q = qe.query;
19160         forceAll = qe.forceAll;
19161         if(forceAll === true || (q.length >= this.minChars)){
19162             if(this.lastQuery != q || this.alwaysQuery){
19163                 this.lastQuery = q;
19164                 if(this.mode == 'local'){
19165                     this.selectedIndex = -1;
19166                     if(forceAll){
19167                         this.store.clearFilter();
19168                     }else{
19169                         this.store.filter(this.displayField, q);
19170                     }
19171                     this.onLoad();
19172                 }else{
19173                     this.store.baseParams[this.queryParam] = q;
19174                     this.store.load({
19175                         params: this.getParams(q)
19176                     });
19177                     this.expand();
19178                 }
19179             }else{
19180                 this.selectedIndex = -1;
19181                 this.onLoad();   
19182             }
19183         }
19184     },
19185
19186     // private
19187     getParams : function(q){
19188         var p = {};
19189         //p[this.queryParam] = q;
19190         if(this.pageSize){
19191             p.start = 0;
19192             p.limit = this.pageSize;
19193         }
19194         return p;
19195     },
19196
19197     /**
19198      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19199      */
19200     collapse : function(){
19201         if(!this.isExpanded()){
19202             return;
19203         }
19204         this.list.hide();
19205         Roo.get(document).un('mousedown', this.collapseIf, this);
19206         Roo.get(document).un('mousewheel', this.collapseIf, this);
19207         if (!this.editable) {
19208             Roo.get(document).un('keydown', this.listKeyPress, this);
19209         }
19210         this.fireEvent('collapse', this);
19211     },
19212
19213     // private
19214     collapseIf : function(e){
19215         if(!e.within(this.wrap) && !e.within(this.list)){
19216             this.collapse();
19217         }
19218     },
19219
19220     /**
19221      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19222      */
19223     expand : function(){
19224         if(this.isExpanded() || !this.hasFocus){
19225             return;
19226         }
19227         this.list.alignTo(this.el, this.listAlign);
19228         this.list.show();
19229         Roo.get(document).on('mousedown', this.collapseIf, this);
19230         Roo.get(document).on('mousewheel', this.collapseIf, this);
19231         if (!this.editable) {
19232             Roo.get(document).on('keydown', this.listKeyPress, this);
19233         }
19234         
19235         this.fireEvent('expand', this);
19236     },
19237
19238     // private
19239     // Implements the default empty TriggerField.onTriggerClick function
19240     onTriggerClick : function(){
19241         if(this.disabled){
19242             return;
19243         }
19244         if(this.isExpanded()){
19245             this.collapse();
19246             if (!this.blockFocus) {
19247                 this.el.focus();
19248             }
19249             
19250         }else {
19251             this.hasFocus = true;
19252             if(this.triggerAction == 'all') {
19253                 this.doQuery(this.allQuery, true);
19254             } else {
19255                 this.doQuery(this.getRawValue());
19256             }
19257             if (!this.blockFocus) {
19258                 this.el.focus();
19259             }
19260         }
19261     },
19262     listKeyPress : function(e)
19263     {
19264         //Roo.log('listkeypress');
19265         // scroll to first matching element based on key pres..
19266         if (e.isSpecialKey()) {
19267             return false;
19268         }
19269         var k = String.fromCharCode(e.getKey()).toUpperCase();
19270         //Roo.log(k);
19271         var match  = false;
19272         var csel = this.view.getSelectedNodes();
19273         var cselitem = false;
19274         if (csel.length) {
19275             var ix = this.view.indexOf(csel[0]);
19276             cselitem  = this.store.getAt(ix);
19277             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19278                 cselitem = false;
19279             }
19280             
19281         }
19282         
19283         this.store.each(function(v) { 
19284             if (cselitem) {
19285                 // start at existing selection.
19286                 if (cselitem.id == v.id) {
19287                     cselitem = false;
19288                 }
19289                 return;
19290             }
19291                 
19292             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19293                 match = this.store.indexOf(v);
19294                 return false;
19295             }
19296         }, this);
19297         
19298         if (match === false) {
19299             return true; // no more action?
19300         }
19301         // scroll to?
19302         this.view.select(match);
19303         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19304         sn.scrollIntoView(sn.dom.parentNode, false);
19305     } 
19306
19307     /** 
19308     * @cfg {Boolean} grow 
19309     * @hide 
19310     */
19311     /** 
19312     * @cfg {Number} growMin 
19313     * @hide 
19314     */
19315     /** 
19316     * @cfg {Number} growMax 
19317     * @hide 
19318     */
19319     /**
19320      * @hide
19321      * @method autoSize
19322      */
19323 });/*
19324  * Copyright(c) 2010-2012, Roo J Solutions Limited
19325  *
19326  * Licence LGPL
19327  *
19328  */
19329
19330 /**
19331  * @class Roo.form.ComboBoxArray
19332  * @extends Roo.form.TextField
19333  * A facebook style adder... for lists of email / people / countries  etc...
19334  * pick multiple items from a combo box, and shows each one.
19335  *
19336  *  Fred [x]  Brian [x]  [Pick another |v]
19337  *
19338  *
19339  *  For this to work: it needs various extra information
19340  *    - normal combo problay has
19341  *      name, hiddenName
19342  *    + displayField, valueField
19343  *
19344  *    For our purpose...
19345  *
19346  *
19347  *   If we change from 'extends' to wrapping...
19348  *   
19349  *  
19350  *
19351  
19352  
19353  * @constructor
19354  * Create a new ComboBoxArray.
19355  * @param {Object} config Configuration options
19356  */
19357  
19358
19359 Roo.form.ComboBoxArray = function(config)
19360 {
19361     this.addEvents({
19362         /**
19363          * @event beforeremove
19364          * Fires before remove the value from the list
19365              * @param {Roo.form.ComboBoxArray} _self This combo box array
19366              * @param {Roo.form.ComboBoxArray.Item} item removed item
19367              */
19368         'beforeremove' : true,
19369         /**
19370          * @event remove
19371          * Fires when remove the value from the list
19372              * @param {Roo.form.ComboBoxArray} _self This combo box array
19373              * @param {Roo.form.ComboBoxArray.Item} item removed item
19374              */
19375         'remove' : true
19376         
19377         
19378     });
19379     
19380     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19381     
19382     this.items = new Roo.util.MixedCollection(false);
19383     
19384     // construct the child combo...
19385     
19386     
19387     
19388     
19389    
19390     
19391 }
19392
19393  
19394 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19395
19396     /**
19397      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19398      */
19399     
19400     lastData : false,
19401     
19402     // behavies liek a hiddne field
19403     inputType:      'hidden',
19404     /**
19405      * @cfg {Number} width The width of the box that displays the selected element
19406      */ 
19407     width:          300,
19408
19409     
19410     
19411     /**
19412      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19413      */
19414     name : false,
19415     /**
19416      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19417      */
19418     hiddenName : false,
19419       /**
19420      * @cfg {String} seperator    The value seperator normally ',' 
19421      */
19422     seperator : ',',
19423     
19424     // private the array of items that are displayed..
19425     items  : false,
19426     // private - the hidden field el.
19427     hiddenEl : false,
19428     // private - the filed el..
19429     el : false,
19430     
19431     //validateValue : function() { return true; }, // all values are ok!
19432     //onAddClick: function() { },
19433     
19434     onRender : function(ct, position) 
19435     {
19436         
19437         // create the standard hidden element
19438         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19439         
19440         
19441         // give fake names to child combo;
19442         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19443         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19444         
19445         this.combo = Roo.factory(this.combo, Roo.form);
19446         this.combo.onRender(ct, position);
19447         if (typeof(this.combo.width) != 'undefined') {
19448             this.combo.onResize(this.combo.width,0);
19449         }
19450         
19451         this.combo.initEvents();
19452         
19453         // assigned so form know we need to do this..
19454         this.store          = this.combo.store;
19455         this.valueField     = this.combo.valueField;
19456         this.displayField   = this.combo.displayField ;
19457         
19458         
19459         this.combo.wrap.addClass('x-cbarray-grp');
19460         
19461         var cbwrap = this.combo.wrap.createChild(
19462             {tag: 'div', cls: 'x-cbarray-cb'},
19463             this.combo.el.dom
19464         );
19465         
19466              
19467         this.hiddenEl = this.combo.wrap.createChild({
19468             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19469         });
19470         this.el = this.combo.wrap.createChild({
19471             tag: 'input',  type:'hidden' , name: this.name, value : ''
19472         });
19473          //   this.el.dom.removeAttribute("name");
19474         
19475         
19476         this.outerWrap = this.combo.wrap;
19477         this.wrap = cbwrap;
19478         
19479         this.outerWrap.setWidth(this.width);
19480         this.outerWrap.dom.removeChild(this.el.dom);
19481         
19482         this.wrap.dom.appendChild(this.el.dom);
19483         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19484         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19485         
19486         this.combo.trigger.setStyle('position','relative');
19487         this.combo.trigger.setStyle('left', '0px');
19488         this.combo.trigger.setStyle('top', '2px');
19489         
19490         this.combo.el.setStyle('vertical-align', 'text-bottom');
19491         
19492         //this.trigger.setStyle('vertical-align', 'top');
19493         
19494         // this should use the code from combo really... on('add' ....)
19495         if (this.adder) {
19496             
19497         
19498             this.adder = this.outerWrap.createChild(
19499                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19500             var _t = this;
19501             this.adder.on('click', function(e) {
19502                 _t.fireEvent('adderclick', this, e);
19503             }, _t);
19504         }
19505         //var _t = this;
19506         //this.adder.on('click', this.onAddClick, _t);
19507         
19508         
19509         this.combo.on('select', function(cb, rec, ix) {
19510             this.addItem(rec.data);
19511             
19512             cb.setValue('');
19513             cb.el.dom.value = '';
19514             //cb.lastData = rec.data;
19515             // add to list
19516             
19517         }, this);
19518         
19519         
19520     },
19521     
19522     
19523     getName: function()
19524     {
19525         // returns hidden if it's set..
19526         if (!this.rendered) {return ''};
19527         return  this.hiddenName ? this.hiddenName : this.name;
19528         
19529     },
19530     
19531     
19532     onResize: function(w, h){
19533         
19534         return;
19535         // not sure if this is needed..
19536         //this.combo.onResize(w,h);
19537         
19538         if(typeof w != 'number'){
19539             // we do not handle it!?!?
19540             return;
19541         }
19542         var tw = this.combo.trigger.getWidth();
19543         tw += this.addicon ? this.addicon.getWidth() : 0;
19544         tw += this.editicon ? this.editicon.getWidth() : 0;
19545         var x = w - tw;
19546         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19547             
19548         this.combo.trigger.setStyle('left', '0px');
19549         
19550         if(this.list && this.listWidth === undefined){
19551             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19552             this.list.setWidth(lw);
19553             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19554         }
19555         
19556     
19557         
19558     },
19559     
19560     addItem: function(rec)
19561     {
19562         var valueField = this.combo.valueField;
19563         var displayField = this.combo.displayField;
19564         
19565         if (this.items.indexOfKey(rec[valueField]) > -1) {
19566             //console.log("GOT " + rec.data.id);
19567             return;
19568         }
19569         
19570         var x = new Roo.form.ComboBoxArray.Item({
19571             //id : rec[this.idField],
19572             data : rec,
19573             displayField : displayField ,
19574             tipField : displayField ,
19575             cb : this
19576         });
19577         // use the 
19578         this.items.add(rec[valueField],x);
19579         // add it before the element..
19580         this.updateHiddenEl();
19581         x.render(this.outerWrap, this.wrap.dom);
19582         // add the image handler..
19583     },
19584     
19585     updateHiddenEl : function()
19586     {
19587         this.validate();
19588         if (!this.hiddenEl) {
19589             return;
19590         }
19591         var ar = [];
19592         var idField = this.combo.valueField;
19593         
19594         this.items.each(function(f) {
19595             ar.push(f.data[idField]);
19596         });
19597         this.hiddenEl.dom.value = ar.join(this.seperator);
19598         this.validate();
19599     },
19600     
19601     reset : function()
19602     {
19603         this.items.clear();
19604         
19605         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19606            el.remove();
19607         });
19608         
19609         this.el.dom.value = '';
19610         if (this.hiddenEl) {
19611             this.hiddenEl.dom.value = '';
19612         }
19613         
19614     },
19615     getValue: function()
19616     {
19617         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19618     },
19619     setValue: function(v) // not a valid action - must use addItems..
19620     {
19621         
19622         this.reset();
19623          
19624         if (this.store.isLocal && (typeof(v) == 'string')) {
19625             // then we can use the store to find the values..
19626             // comma seperated at present.. this needs to allow JSON based encoding..
19627             this.hiddenEl.value  = v;
19628             var v_ar = [];
19629             Roo.each(v.split(this.seperator), function(k) {
19630                 Roo.log("CHECK " + this.valueField + ',' + k);
19631                 var li = this.store.query(this.valueField, k);
19632                 if (!li.length) {
19633                     return;
19634                 }
19635                 var add = {};
19636                 add[this.valueField] = k;
19637                 add[this.displayField] = li.item(0).data[this.displayField];
19638                 
19639                 this.addItem(add);
19640             }, this) 
19641              
19642         }
19643         if (typeof(v) == 'object' ) {
19644             // then let's assume it's an array of objects..
19645             Roo.each(v, function(l) {
19646                 var add = l;
19647                 if (typeof(l) == 'string') {
19648                     add = {};
19649                     add[this.valueField] = l;
19650                     add[this.displayField] = l
19651                 }
19652                 this.addItem(add);
19653             }, this);
19654              
19655         }
19656         
19657         
19658     },
19659     setFromData: function(v)
19660     {
19661         // this recieves an object, if setValues is called.
19662         this.reset();
19663         this.el.dom.value = v[this.displayField];
19664         this.hiddenEl.dom.value = v[this.valueField];
19665         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19666             return;
19667         }
19668         var kv = v[this.valueField];
19669         var dv = v[this.displayField];
19670         kv = typeof(kv) != 'string' ? '' : kv;
19671         dv = typeof(dv) != 'string' ? '' : dv;
19672         
19673         
19674         var keys = kv.split(this.seperator);
19675         var display = dv.split(this.seperator);
19676         for (var i = 0 ; i < keys.length; i++) {
19677             add = {};
19678             add[this.valueField] = keys[i];
19679             add[this.displayField] = display[i];
19680             this.addItem(add);
19681         }
19682       
19683         
19684     },
19685     
19686     /**
19687      * Validates the combox array value
19688      * @return {Boolean} True if the value is valid, else false
19689      */
19690     validate : function(){
19691         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19692             this.clearInvalid();
19693             return true;
19694         }
19695         return false;
19696     },
19697     
19698     validateValue : function(value){
19699         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19700         
19701     },
19702     
19703     /*@
19704      * overide
19705      * 
19706      */
19707     isDirty : function() {
19708         if(this.disabled) {
19709             return false;
19710         }
19711         
19712         try {
19713             var d = Roo.decode(String(this.originalValue));
19714         } catch (e) {
19715             return String(this.getValue()) !== String(this.originalValue);
19716         }
19717         
19718         var originalValue = [];
19719         
19720         for (var i = 0; i < d.length; i++){
19721             originalValue.push(d[i][this.valueField]);
19722         }
19723         
19724         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19725         
19726     }
19727     
19728 });
19729
19730
19731
19732 /**
19733  * @class Roo.form.ComboBoxArray.Item
19734  * @extends Roo.BoxComponent
19735  * A selected item in the list
19736  *  Fred [x]  Brian [x]  [Pick another |v]
19737  * 
19738  * @constructor
19739  * Create a new item.
19740  * @param {Object} config Configuration options
19741  */
19742  
19743 Roo.form.ComboBoxArray.Item = function(config) {
19744     config.id = Roo.id();
19745     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19746 }
19747
19748 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19749     data : {},
19750     cb: false,
19751     displayField : false,
19752     tipField : false,
19753     
19754     
19755     defaultAutoCreate : {
19756         tag: 'div',
19757         cls: 'x-cbarray-item',
19758         cn : [ 
19759             { tag: 'div' },
19760             {
19761                 tag: 'img',
19762                 width:16,
19763                 height : 16,
19764                 src : Roo.BLANK_IMAGE_URL ,
19765                 align: 'center'
19766             }
19767         ]
19768         
19769     },
19770     
19771  
19772     onRender : function(ct, position)
19773     {
19774         Roo.form.Field.superclass.onRender.call(this, ct, position);
19775         
19776         if(!this.el){
19777             var cfg = this.getAutoCreate();
19778             this.el = ct.createChild(cfg, position);
19779         }
19780         
19781         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19782         
19783         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19784             this.cb.renderer(this.data) :
19785             String.format('{0}',this.data[this.displayField]);
19786         
19787             
19788         this.el.child('div').dom.setAttribute('qtip',
19789                         String.format('{0}',this.data[this.tipField])
19790         );
19791         
19792         this.el.child('img').on('click', this.remove, this);
19793         
19794     },
19795    
19796     remove : function()
19797     {
19798         if(this.cb.disabled){
19799             return;
19800         }
19801         
19802         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19803             this.cb.items.remove(this);
19804             this.el.child('img').un('click', this.remove, this);
19805             this.el.remove();
19806             this.cb.updateHiddenEl();
19807
19808             this.cb.fireEvent('remove', this.cb, this);
19809         }
19810         
19811     }
19812 });/*
19813  * RooJS Library 1.1.1
19814  * Copyright(c) 2008-2011  Alan Knowles
19815  *
19816  * License - LGPL
19817  */
19818  
19819
19820 /**
19821  * @class Roo.form.ComboNested
19822  * @extends Roo.form.ComboBox
19823  * A combobox for that allows selection of nested items in a list,
19824  * eg.
19825  *
19826  *  Book
19827  *    -> red
19828  *    -> green
19829  *  Table
19830  *    -> square
19831  *      ->red
19832  *      ->green
19833  *    -> rectangle
19834  *      ->green
19835  *      
19836  * 
19837  * @constructor
19838  * Create a new ComboNested
19839  * @param {Object} config Configuration options
19840  */
19841 Roo.form.ComboNested = function(config){
19842     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19843     // should verify some data...
19844     // like
19845     // hiddenName = required..
19846     // displayField = required
19847     // valudField == required
19848     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19849     var _t = this;
19850     Roo.each(req, function(e) {
19851         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19852             throw "Roo.form.ComboNested : missing value for: " + e;
19853         }
19854     });
19855      
19856     
19857 };
19858
19859 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19860    
19861     /*
19862      * @config {Number} max Number of columns to show
19863      */
19864     
19865     maxColumns : 3,
19866    
19867     list : null, // the outermost div..
19868     innerLists : null, // the
19869     views : null,
19870     stores : null,
19871     // private
19872     loadingChildren : false,
19873     
19874     onRender : function(ct, position)
19875     {
19876         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19877         
19878         if(this.hiddenName){
19879             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19880                     'before', true);
19881             this.hiddenField.value =
19882                 this.hiddenValue !== undefined ? this.hiddenValue :
19883                 this.value !== undefined ? this.value : '';
19884
19885             // prevent input submission
19886             this.el.dom.removeAttribute('name');
19887              
19888              
19889         }
19890         
19891         if(Roo.isGecko){
19892             this.el.dom.setAttribute('autocomplete', 'off');
19893         }
19894
19895         var cls = 'x-combo-list';
19896
19897         this.list = new Roo.Layer({
19898             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19899         });
19900
19901         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19902         this.list.setWidth(lw);
19903         this.list.swallowEvent('mousewheel');
19904         this.assetHeight = 0;
19905
19906         if(this.title){
19907             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19908             this.assetHeight += this.header.getHeight();
19909         }
19910         this.innerLists = [];
19911         this.views = [];
19912         this.stores = [];
19913         for (var i =0 ; i < this.maxColumns; i++) {
19914             this.onRenderList( cls, i);
19915         }
19916         
19917         // always needs footer, as we are going to have an 'OK' button.
19918         this.footer = this.list.createChild({cls:cls+'-ft'});
19919         this.pageTb = new Roo.Toolbar(this.footer);  
19920         var _this = this;
19921         this.pageTb.add(  {
19922             
19923             text: 'Done',
19924             handler: function()
19925             {
19926                 _this.collapse();
19927             }
19928         });
19929         
19930         if ( this.allowBlank && !this.disableClear) {
19931             
19932             this.pageTb.add(new Roo.Toolbar.Fill(), {
19933                 cls: 'x-btn-icon x-btn-clear',
19934                 text: '&#160;',
19935                 handler: function()
19936                 {
19937                     _this.collapse();
19938                     _this.clearValue();
19939                     _this.onSelect(false, -1);
19940                 }
19941             });
19942         }
19943         if (this.footer) {
19944             this.assetHeight += this.footer.getHeight();
19945         }
19946         
19947     },
19948     onRenderList : function (  cls, i)
19949     {
19950         
19951         var lw = Math.floor(
19952                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19953         );
19954         
19955         this.list.setWidth(lw); // default to '1'
19956
19957         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19958         //il.on('mouseover', this.onViewOver, this, { list:  i });
19959         //il.on('mousemove', this.onViewMove, this, { list:  i });
19960         il.setWidth(lw);
19961         il.setStyle({ 'overflow-x' : 'hidden'});
19962
19963         if(!this.tpl){
19964             this.tpl = new Roo.Template({
19965                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19966                 isEmpty: function (value, allValues) {
19967                     //Roo.log(value);
19968                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19969                     return dl ? 'has-children' : 'no-children'
19970                 }
19971             });
19972         }
19973         
19974         var store  = this.store;
19975         if (i > 0) {
19976             store  = new Roo.data.SimpleStore({
19977                 //fields : this.store.reader.meta.fields,
19978                 reader : this.store.reader,
19979                 data : [ ]
19980             });
19981         }
19982         this.stores[i]  = store;
19983                   
19984         var view = this.views[i] = new Roo.View(
19985             il,
19986             this.tpl,
19987             {
19988                 singleSelect:true,
19989                 store: store,
19990                 selectedClass: this.selectedClass
19991             }
19992         );
19993         view.getEl().setWidth(lw);
19994         view.getEl().setStyle({
19995             position: i < 1 ? 'relative' : 'absolute',
19996             top: 0,
19997             left: (i * lw ) + 'px',
19998             display : i > 0 ? 'none' : 'block'
19999         });
20000         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20001         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20002         //view.on('click', this.onViewClick, this, { list : i });
20003
20004         store.on('beforeload', this.onBeforeLoad, this);
20005         store.on('load',  this.onLoad, this, { list  : i});
20006         store.on('loadexception', this.onLoadException, this);
20007
20008         // hide the other vies..
20009         
20010         
20011         
20012     },
20013       
20014     restrictHeight : function()
20015     {
20016         var mh = 0;
20017         Roo.each(this.innerLists, function(il,i) {
20018             var el = this.views[i].getEl();
20019             el.dom.style.height = '';
20020             var inner = el.dom;
20021             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20022             // only adjust heights on other ones..
20023             mh = Math.max(h, mh);
20024             if (i < 1) {
20025                 
20026                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20027                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20028                
20029             }
20030             
20031             
20032         }, this);
20033         
20034         this.list.beginUpdate();
20035         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20036         this.list.alignTo(this.el, this.listAlign);
20037         this.list.endUpdate();
20038         
20039     },
20040      
20041     
20042     // -- store handlers..
20043     // private
20044     onBeforeLoad : function()
20045     {
20046         if(!this.hasFocus){
20047             return;
20048         }
20049         this.innerLists[0].update(this.loadingText ?
20050                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20051         this.restrictHeight();
20052         this.selectedIndex = -1;
20053     },
20054     // private
20055     onLoad : function(a,b,c,d)
20056     {
20057         if (!this.loadingChildren) {
20058             // then we are loading the top level. - hide the children
20059             for (var i = 1;i < this.views.length; i++) {
20060                 this.views[i].getEl().setStyle({ display : 'none' });
20061             }
20062             var lw = Math.floor(
20063                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20064             );
20065         
20066              this.list.setWidth(lw); // default to '1'
20067
20068             
20069         }
20070         if(!this.hasFocus){
20071             return;
20072         }
20073         
20074         if(this.store.getCount() > 0) {
20075             this.expand();
20076             this.restrictHeight();   
20077         } else {
20078             this.onEmptyResults();
20079         }
20080         
20081         if (!this.loadingChildren) {
20082             this.selectActive();
20083         }
20084         /*
20085         this.stores[1].loadData([]);
20086         this.stores[2].loadData([]);
20087         this.views
20088         */    
20089     
20090         //this.el.focus();
20091     },
20092     
20093     
20094     // private
20095     onLoadException : function()
20096     {
20097         this.collapse();
20098         Roo.log(this.store.reader.jsonData);
20099         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20100             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20101         }
20102         
20103         
20104     },
20105     // no cleaning of leading spaces on blur here.
20106     cleanLeadingSpace : function(e) { },
20107     
20108
20109     onSelectChange : function (view, sels, opts )
20110     {
20111         var ix = view.getSelectedIndexes();
20112          
20113         if (opts.list > this.maxColumns - 2) {
20114             if (view.store.getCount()<  1) {
20115                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20116
20117             } else  {
20118                 if (ix.length) {
20119                     // used to clear ?? but if we are loading unselected 
20120                     this.setFromData(view.store.getAt(ix[0]).data);
20121                 }
20122                 
20123             }
20124             
20125             return;
20126         }
20127         
20128         if (!ix.length) {
20129             // this get's fired when trigger opens..
20130            // this.setFromData({});
20131             var str = this.stores[opts.list+1];
20132             str.data.clear(); // removeall wihtout the fire events..
20133             return;
20134         }
20135         
20136         var rec = view.store.getAt(ix[0]);
20137          
20138         this.setFromData(rec.data);
20139         this.fireEvent('select', this, rec, ix[0]);
20140         
20141         var lw = Math.floor(
20142              (
20143                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20144              ) / this.maxColumns
20145         );
20146         this.loadingChildren = true;
20147         this.stores[opts.list+1].loadDataFromChildren( rec );
20148         this.loadingChildren = false;
20149         var dl = this.stores[opts.list+1]. getTotalCount();
20150         
20151         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20152         
20153         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20154         for (var i = opts.list+2; i < this.views.length;i++) {
20155             this.views[i].getEl().setStyle({ display : 'none' });
20156         }
20157         
20158         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20159         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20160         
20161         if (this.isLoading) {
20162            // this.selectActive(opts.list);
20163         }
20164          
20165     },
20166     
20167     
20168     
20169     
20170     onDoubleClick : function()
20171     {
20172         this.collapse(); //??
20173     },
20174     
20175      
20176     
20177     
20178     
20179     // private
20180     recordToStack : function(store, prop, value, stack)
20181     {
20182         var cstore = new Roo.data.SimpleStore({
20183             //fields : this.store.reader.meta.fields, // we need array reader.. for
20184             reader : this.store.reader,
20185             data : [ ]
20186         });
20187         var _this = this;
20188         var record  = false;
20189         var srec = false;
20190         if(store.getCount() < 1){
20191             return false;
20192         }
20193         store.each(function(r){
20194             if(r.data[prop] == value){
20195                 record = r;
20196             srec = r;
20197                 return false;
20198             }
20199             if (r.data.cn && r.data.cn.length) {
20200                 cstore.loadDataFromChildren( r);
20201                 var cret = _this.recordToStack(cstore, prop, value, stack);
20202                 if (cret !== false) {
20203                     record = cret;
20204                     srec = r;
20205                     return false;
20206                 }
20207             }
20208              
20209             return true;
20210         });
20211         if (record == false) {
20212             return false
20213         }
20214         stack.unshift(srec);
20215         return record;
20216     },
20217     
20218     /*
20219      * find the stack of stores that match our value.
20220      *
20221      * 
20222      */
20223     
20224     selectActive : function ()
20225     {
20226         // if store is not loaded, then we will need to wait for that to happen first.
20227         var stack = [];
20228         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20229         for (var i = 0; i < stack.length; i++ ) {
20230             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20231         }
20232         
20233     }
20234         
20235          
20236     
20237     
20238     
20239     
20240 });/*
20241  * Based on:
20242  * Ext JS Library 1.1.1
20243  * Copyright(c) 2006-2007, Ext JS, LLC.
20244  *
20245  * Originally Released Under LGPL - original licence link has changed is not relivant.
20246  *
20247  * Fork - LGPL
20248  * <script type="text/javascript">
20249  */
20250 /**
20251  * @class Roo.form.Checkbox
20252  * @extends Roo.form.Field
20253  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20254  * @constructor
20255  * Creates a new Checkbox
20256  * @param {Object} config Configuration options
20257  */
20258 Roo.form.Checkbox = function(config){
20259     Roo.form.Checkbox.superclass.constructor.call(this, config);
20260     this.addEvents({
20261         /**
20262          * @event check
20263          * Fires when the checkbox is checked or unchecked.
20264              * @param {Roo.form.Checkbox} this This checkbox
20265              * @param {Boolean} checked The new checked value
20266              */
20267         check : true
20268     });
20269 };
20270
20271 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20272     /**
20273      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20274      */
20275     focusClass : undefined,
20276     /**
20277      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20278      */
20279     fieldClass: "x-form-field",
20280     /**
20281      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20282      */
20283     checked: false,
20284     /**
20285      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20286      * {tag: "input", type: "checkbox", autocomplete: "off"})
20287      */
20288     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20289     /**
20290      * @cfg {String} boxLabel The text that appears beside the checkbox
20291      */
20292     boxLabel : "",
20293     /**
20294      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20295      */  
20296     inputValue : '1',
20297     /**
20298      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20299      */
20300      valueOff: '0', // value when not checked..
20301
20302     actionMode : 'viewEl', 
20303     //
20304     // private
20305     itemCls : 'x-menu-check-item x-form-item',
20306     groupClass : 'x-menu-group-item',
20307     inputType : 'hidden',
20308     
20309     
20310     inSetChecked: false, // check that we are not calling self...
20311     
20312     inputElement: false, // real input element?
20313     basedOn: false, // ????
20314     
20315     isFormField: true, // not sure where this is needed!!!!
20316
20317     onResize : function(){
20318         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20319         if(!this.boxLabel){
20320             this.el.alignTo(this.wrap, 'c-c');
20321         }
20322     },
20323
20324     initEvents : function(){
20325         Roo.form.Checkbox.superclass.initEvents.call(this);
20326         this.el.on("click", this.onClick,  this);
20327         this.el.on("change", this.onClick,  this);
20328     },
20329
20330
20331     getResizeEl : function(){
20332         return this.wrap;
20333     },
20334
20335     getPositionEl : function(){
20336         return this.wrap;
20337     },
20338
20339     // private
20340     onRender : function(ct, position){
20341         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20342         /*
20343         if(this.inputValue !== undefined){
20344             this.el.dom.value = this.inputValue;
20345         }
20346         */
20347         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20348         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20349         var viewEl = this.wrap.createChild({ 
20350             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20351         this.viewEl = viewEl;   
20352         this.wrap.on('click', this.onClick,  this); 
20353         
20354         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20355         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20356         
20357         
20358         
20359         if(this.boxLabel){
20360             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20361         //    viewEl.on('click', this.onClick,  this); 
20362         }
20363         //if(this.checked){
20364             this.setChecked(this.checked);
20365         //}else{
20366             //this.checked = this.el.dom;
20367         //}
20368
20369     },
20370
20371     // private
20372     initValue : Roo.emptyFn,
20373
20374     /**
20375      * Returns the checked state of the checkbox.
20376      * @return {Boolean} True if checked, else false
20377      */
20378     getValue : function(){
20379         if(this.el){
20380             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20381         }
20382         return this.valueOff;
20383         
20384     },
20385
20386         // private
20387     onClick : function(){ 
20388         if (this.disabled) {
20389             return;
20390         }
20391         this.setChecked(!this.checked);
20392
20393         //if(this.el.dom.checked != this.checked){
20394         //    this.setValue(this.el.dom.checked);
20395        // }
20396     },
20397
20398     /**
20399      * Sets the checked state of the checkbox.
20400      * On is always based on a string comparison between inputValue and the param.
20401      * @param {Boolean/String} value - the value to set 
20402      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20403      */
20404     setValue : function(v,suppressEvent){
20405         
20406         
20407         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20408         //if(this.el && this.el.dom){
20409         //    this.el.dom.checked = this.checked;
20410         //    this.el.dom.defaultChecked = this.checked;
20411         //}
20412         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20413         //this.fireEvent("check", this, this.checked);
20414     },
20415     // private..
20416     setChecked : function(state,suppressEvent)
20417     {
20418         if (this.inSetChecked) {
20419             this.checked = state;
20420             return;
20421         }
20422         
20423     
20424         if(this.wrap){
20425             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20426         }
20427         this.checked = state;
20428         if(suppressEvent !== true){
20429             this.fireEvent('check', this, state);
20430         }
20431         this.inSetChecked = true;
20432         this.el.dom.value = state ? this.inputValue : this.valueOff;
20433         this.inSetChecked = false;
20434         
20435     },
20436     // handle setting of hidden value by some other method!!?!?
20437     setFromHidden: function()
20438     {
20439         if(!this.el){
20440             return;
20441         }
20442         //console.log("SET FROM HIDDEN");
20443         //alert('setFrom hidden');
20444         this.setValue(this.el.dom.value);
20445     },
20446     
20447     onDestroy : function()
20448     {
20449         if(this.viewEl){
20450             Roo.get(this.viewEl).remove();
20451         }
20452          
20453         Roo.form.Checkbox.superclass.onDestroy.call(this);
20454     },
20455     
20456     setBoxLabel : function(str)
20457     {
20458         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20459     }
20460
20461 });/*
20462  * Based on:
20463  * Ext JS Library 1.1.1
20464  * Copyright(c) 2006-2007, Ext JS, LLC.
20465  *
20466  * Originally Released Under LGPL - original licence link has changed is not relivant.
20467  *
20468  * Fork - LGPL
20469  * <script type="text/javascript">
20470  */
20471  
20472 /**
20473  * @class Roo.form.Radio
20474  * @extends Roo.form.Checkbox
20475  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20476  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20477  * @constructor
20478  * Creates a new Radio
20479  * @param {Object} config Configuration options
20480  */
20481 Roo.form.Radio = function(){
20482     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20483 };
20484 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20485     inputType: 'radio',
20486
20487     /**
20488      * If this radio is part of a group, it will return the selected value
20489      * @return {String}
20490      */
20491     getGroupValue : function(){
20492         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20493     },
20494     
20495     
20496     onRender : function(ct, position){
20497         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20498         
20499         if(this.inputValue !== undefined){
20500             this.el.dom.value = this.inputValue;
20501         }
20502          
20503         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20504         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20505         //var viewEl = this.wrap.createChild({ 
20506         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20507         //this.viewEl = viewEl;   
20508         //this.wrap.on('click', this.onClick,  this); 
20509         
20510         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20511         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20512         
20513         
20514         
20515         if(this.boxLabel){
20516             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20517         //    viewEl.on('click', this.onClick,  this); 
20518         }
20519          if(this.checked){
20520             this.el.dom.checked =   'checked' ;
20521         }
20522          
20523     } 
20524     
20525     
20526 });//<script type="text/javascript">
20527
20528 /*
20529  * Based  Ext JS Library 1.1.1
20530  * Copyright(c) 2006-2007, Ext JS, LLC.
20531  * LGPL
20532  *
20533  */
20534  
20535 /**
20536  * @class Roo.HtmlEditorCore
20537  * @extends Roo.Component
20538  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20539  *
20540  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20541  */
20542
20543 Roo.HtmlEditorCore = function(config){
20544     
20545     
20546     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20547     
20548     
20549     this.addEvents({
20550         /**
20551          * @event initialize
20552          * Fires when the editor is fully initialized (including the iframe)
20553          * @param {Roo.HtmlEditorCore} this
20554          */
20555         initialize: true,
20556         /**
20557          * @event activate
20558          * Fires when the editor is first receives the focus. Any insertion must wait
20559          * until after this event.
20560          * @param {Roo.HtmlEditorCore} this
20561          */
20562         activate: true,
20563          /**
20564          * @event beforesync
20565          * Fires before the textarea is updated with content from the editor iframe. Return false
20566          * to cancel the sync.
20567          * @param {Roo.HtmlEditorCore} this
20568          * @param {String} html
20569          */
20570         beforesync: true,
20571          /**
20572          * @event beforepush
20573          * Fires before the iframe editor is updated with content from the textarea. Return false
20574          * to cancel the push.
20575          * @param {Roo.HtmlEditorCore} this
20576          * @param {String} html
20577          */
20578         beforepush: true,
20579          /**
20580          * @event sync
20581          * Fires when the textarea is updated with content from the editor iframe.
20582          * @param {Roo.HtmlEditorCore} this
20583          * @param {String} html
20584          */
20585         sync: true,
20586          /**
20587          * @event push
20588          * Fires when the iframe editor is updated with content from the textarea.
20589          * @param {Roo.HtmlEditorCore} this
20590          * @param {String} html
20591          */
20592         push: true,
20593         
20594         /**
20595          * @event editorevent
20596          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20597          * @param {Roo.HtmlEditorCore} this
20598          */
20599         editorevent: true
20600         
20601     });
20602     
20603     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20604     
20605     // defaults : white / black...
20606     this.applyBlacklists();
20607     
20608     
20609     
20610 };
20611
20612
20613 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20614
20615
20616      /**
20617      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20618      */
20619     
20620     owner : false,
20621     
20622      /**
20623      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20624      *                        Roo.resizable.
20625      */
20626     resizable : false,
20627      /**
20628      * @cfg {Number} height (in pixels)
20629      */   
20630     height: 300,
20631    /**
20632      * @cfg {Number} width (in pixels)
20633      */   
20634     width: 500,
20635     
20636     /**
20637      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20638      * 
20639      */
20640     stylesheets: false,
20641     
20642     /**
20643      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20644      */
20645     allowComments: false,
20646     // id of frame..
20647     frameId: false,
20648     
20649     // private properties
20650     validationEvent : false,
20651     deferHeight: true,
20652     initialized : false,
20653     activated : false,
20654     sourceEditMode : false,
20655     onFocus : Roo.emptyFn,
20656     iframePad:3,
20657     hideMode:'offsets',
20658     
20659     clearUp: true,
20660     
20661     // blacklist + whitelisted elements..
20662     black: false,
20663     white: false,
20664      
20665     bodyCls : '',
20666
20667     /**
20668      * Protected method that will not generally be called directly. It
20669      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20670      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20671      */
20672     getDocMarkup : function(){
20673         // body styles..
20674         var st = '';
20675         
20676         // inherit styels from page...?? 
20677         if (this.stylesheets === false) {
20678             
20679             Roo.get(document.head).select('style').each(function(node) {
20680                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20681             });
20682             
20683             Roo.get(document.head).select('link').each(function(node) { 
20684                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20685             });
20686             
20687         } else if (!this.stylesheets.length) {
20688                 // simple..
20689                 st = '<style type="text/css">' +
20690                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20691                    '</style>';
20692         } else {
20693             for (var i in this.stylesheets) { 
20694                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20695             }
20696             
20697         }
20698         
20699         st +=  '<style type="text/css">' +
20700             'IMG { cursor: pointer } ' +
20701         '</style>';
20702
20703         var cls = 'roo-htmleditor-body';
20704         
20705         if(this.bodyCls.length){
20706             cls += ' ' + this.bodyCls;
20707         }
20708         
20709         return '<html><head>' + st  +
20710             //<style type="text/css">' +
20711             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20712             //'</style>' +
20713             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20714     },
20715
20716     // private
20717     onRender : function(ct, position)
20718     {
20719         var _t = this;
20720         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20721         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20722         
20723         
20724         this.el.dom.style.border = '0 none';
20725         this.el.dom.setAttribute('tabIndex', -1);
20726         this.el.addClass('x-hidden hide');
20727         
20728         
20729         
20730         if(Roo.isIE){ // fix IE 1px bogus margin
20731             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20732         }
20733        
20734         
20735         this.frameId = Roo.id();
20736         
20737          
20738         
20739         var iframe = this.owner.wrap.createChild({
20740             tag: 'iframe',
20741             cls: 'form-control', // bootstrap..
20742             id: this.frameId,
20743             name: this.frameId,
20744             frameBorder : 'no',
20745             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20746         }, this.el
20747         );
20748         
20749         
20750         this.iframe = iframe.dom;
20751
20752          this.assignDocWin();
20753         
20754         this.doc.designMode = 'on';
20755        
20756         this.doc.open();
20757         this.doc.write(this.getDocMarkup());
20758         this.doc.close();
20759
20760         
20761         var task = { // must defer to wait for browser to be ready
20762             run : function(){
20763                 //console.log("run task?" + this.doc.readyState);
20764                 this.assignDocWin();
20765                 if(this.doc.body || this.doc.readyState == 'complete'){
20766                     try {
20767                         this.doc.designMode="on";
20768                     } catch (e) {
20769                         return;
20770                     }
20771                     Roo.TaskMgr.stop(task);
20772                     this.initEditor.defer(10, this);
20773                 }
20774             },
20775             interval : 10,
20776             duration: 10000,
20777             scope: this
20778         };
20779         Roo.TaskMgr.start(task);
20780
20781     },
20782
20783     // private
20784     onResize : function(w, h)
20785     {
20786          Roo.log('resize: ' +w + ',' + h );
20787         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20788         if(!this.iframe){
20789             return;
20790         }
20791         if(typeof w == 'number'){
20792             
20793             this.iframe.style.width = w + 'px';
20794         }
20795         if(typeof h == 'number'){
20796             
20797             this.iframe.style.height = h + 'px';
20798             if(this.doc){
20799                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20800             }
20801         }
20802         
20803     },
20804
20805     /**
20806      * Toggles the editor between standard and source edit mode.
20807      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20808      */
20809     toggleSourceEdit : function(sourceEditMode){
20810         
20811         this.sourceEditMode = sourceEditMode === true;
20812         
20813         if(this.sourceEditMode){
20814  
20815             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20816             
20817         }else{
20818             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20819             //this.iframe.className = '';
20820             this.deferFocus();
20821         }
20822         //this.setSize(this.owner.wrap.getSize());
20823         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20824     },
20825
20826     
20827   
20828
20829     /**
20830      * Protected method that will not generally be called directly. If you need/want
20831      * custom HTML cleanup, this is the method you should override.
20832      * @param {String} html The HTML to be cleaned
20833      * return {String} The cleaned HTML
20834      */
20835     cleanHtml : function(html){
20836         html = String(html);
20837         if(html.length > 5){
20838             if(Roo.isSafari){ // strip safari nonsense
20839                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20840             }
20841         }
20842         if(html == '&nbsp;'){
20843             html = '';
20844         }
20845         return html;
20846     },
20847
20848     /**
20849      * HTML Editor -> Textarea
20850      * Protected method that will not generally be called directly. Syncs the contents
20851      * of the editor iframe with the textarea.
20852      */
20853     syncValue : function(){
20854         if(this.initialized){
20855             var bd = (this.doc.body || this.doc.documentElement);
20856             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20857             var html = bd.innerHTML;
20858             if(Roo.isSafari){
20859                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20860                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20861                 if(m && m[1]){
20862                     html = '<div style="'+m[0]+'">' + html + '</div>';
20863                 }
20864             }
20865             html = this.cleanHtml(html);
20866             // fix up the special chars.. normaly like back quotes in word...
20867             // however we do not want to do this with chinese..
20868             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20869                 
20870                 var cc = match.charCodeAt();
20871
20872                 // Get the character value, handling surrogate pairs
20873                 if (match.length == 2) {
20874                     // It's a surrogate pair, calculate the Unicode code point
20875                     var high = match.charCodeAt(0) - 0xD800;
20876                     var low  = match.charCodeAt(1) - 0xDC00;
20877                     cc = (high * 0x400) + low + 0x10000;
20878                 }  else if (
20879                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20880                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20881                     (cc >= 0xf900 && cc < 0xfb00 )
20882                 ) {
20883                         return match;
20884                 }  
20885          
20886                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20887                 return "&#" + cc + ";";
20888                 
20889                 
20890             });
20891             
20892             
20893              
20894             if(this.owner.fireEvent('beforesync', this, html) !== false){
20895                 this.el.dom.value = html;
20896                 this.owner.fireEvent('sync', this, html);
20897             }
20898         }
20899     },
20900
20901     /**
20902      * Protected method that will not generally be called directly. Pushes the value of the textarea
20903      * into the iframe editor.
20904      */
20905     pushValue : function(){
20906         if(this.initialized){
20907             var v = this.el.dom.value.trim();
20908             
20909 //            if(v.length < 1){
20910 //                v = '&#160;';
20911 //            }
20912             
20913             if(this.owner.fireEvent('beforepush', this, v) !== false){
20914                 var d = (this.doc.body || this.doc.documentElement);
20915                 d.innerHTML = v;
20916                 this.cleanUpPaste();
20917                 this.el.dom.value = d.innerHTML;
20918                 this.owner.fireEvent('push', this, v);
20919             }
20920         }
20921     },
20922
20923     // private
20924     deferFocus : function(){
20925         this.focus.defer(10, this);
20926     },
20927
20928     // doc'ed in Field
20929     focus : function(){
20930         if(this.win && !this.sourceEditMode){
20931             this.win.focus();
20932         }else{
20933             this.el.focus();
20934         }
20935     },
20936     
20937     assignDocWin: function()
20938     {
20939         var iframe = this.iframe;
20940         
20941          if(Roo.isIE){
20942             this.doc = iframe.contentWindow.document;
20943             this.win = iframe.contentWindow;
20944         } else {
20945 //            if (!Roo.get(this.frameId)) {
20946 //                return;
20947 //            }
20948 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20949 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20950             
20951             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20952                 return;
20953             }
20954             
20955             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20956             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20957         }
20958     },
20959     
20960     // private
20961     initEditor : function(){
20962         //console.log("INIT EDITOR");
20963         this.assignDocWin();
20964         
20965         
20966         
20967         this.doc.designMode="on";
20968         this.doc.open();
20969         this.doc.write(this.getDocMarkup());
20970         this.doc.close();
20971         
20972         var dbody = (this.doc.body || this.doc.documentElement);
20973         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20974         // this copies styles from the containing element into thsi one..
20975         // not sure why we need all of this..
20976         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20977         
20978         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20979         //ss['background-attachment'] = 'fixed'; // w3c
20980         dbody.bgProperties = 'fixed'; // ie
20981         //Roo.DomHelper.applyStyles(dbody, ss);
20982         Roo.EventManager.on(this.doc, {
20983             //'mousedown': this.onEditorEvent,
20984             'mouseup': this.onEditorEvent,
20985             'dblclick': this.onEditorEvent,
20986             'click': this.onEditorEvent,
20987             'keyup': this.onEditorEvent,
20988             buffer:100,
20989             scope: this
20990         });
20991         if(Roo.isGecko){
20992             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20993         }
20994         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20995             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20996         }
20997         this.initialized = true;
20998
20999         this.owner.fireEvent('initialize', this);
21000         this.pushValue();
21001     },
21002
21003     // private
21004     onDestroy : function(){
21005         
21006         
21007         
21008         if(this.rendered){
21009             
21010             //for (var i =0; i < this.toolbars.length;i++) {
21011             //    // fixme - ask toolbars for heights?
21012             //    this.toolbars[i].onDestroy();
21013            // }
21014             
21015             //this.wrap.dom.innerHTML = '';
21016             //this.wrap.remove();
21017         }
21018     },
21019
21020     // private
21021     onFirstFocus : function(){
21022         
21023         this.assignDocWin();
21024         
21025         
21026         this.activated = true;
21027          
21028     
21029         if(Roo.isGecko){ // prevent silly gecko errors
21030             this.win.focus();
21031             var s = this.win.getSelection();
21032             if(!s.focusNode || s.focusNode.nodeType != 3){
21033                 var r = s.getRangeAt(0);
21034                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21035                 r.collapse(true);
21036                 this.deferFocus();
21037             }
21038             try{
21039                 this.execCmd('useCSS', true);
21040                 this.execCmd('styleWithCSS', false);
21041             }catch(e){}
21042         }
21043         this.owner.fireEvent('activate', this);
21044     },
21045
21046     // private
21047     adjustFont: function(btn){
21048         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21049         //if(Roo.isSafari){ // safari
21050         //    adjust *= 2;
21051        // }
21052         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21053         if(Roo.isSafari){ // safari
21054             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21055             v =  (v < 10) ? 10 : v;
21056             v =  (v > 48) ? 48 : v;
21057             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21058             
21059         }
21060         
21061         
21062         v = Math.max(1, v+adjust);
21063         
21064         this.execCmd('FontSize', v  );
21065     },
21066
21067     onEditorEvent : function(e)
21068     {
21069         this.owner.fireEvent('editorevent', this, e);
21070       //  this.updateToolbar();
21071         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21072     },
21073
21074     insertTag : function(tg)
21075     {
21076         // could be a bit smarter... -> wrap the current selected tRoo..
21077         if (tg.toLowerCase() == 'span' ||
21078             tg.toLowerCase() == 'code' ||
21079             tg.toLowerCase() == 'sup' ||
21080             tg.toLowerCase() == 'sub' 
21081             ) {
21082             
21083             range = this.createRange(this.getSelection());
21084             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21085             wrappingNode.appendChild(range.extractContents());
21086             range.insertNode(wrappingNode);
21087
21088             return;
21089             
21090             
21091             
21092         }
21093         this.execCmd("formatblock",   tg);
21094         
21095     },
21096     
21097     insertText : function(txt)
21098     {
21099         
21100         
21101         var range = this.createRange();
21102         range.deleteContents();
21103                //alert(Sender.getAttribute('label'));
21104                
21105         range.insertNode(this.doc.createTextNode(txt));
21106     } ,
21107     
21108      
21109
21110     /**
21111      * Executes a Midas editor command on the editor document and performs necessary focus and
21112      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21113      * @param {String} cmd The Midas command
21114      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21115      */
21116     relayCmd : function(cmd, value){
21117         this.win.focus();
21118         this.execCmd(cmd, value);
21119         this.owner.fireEvent('editorevent', this);
21120         //this.updateToolbar();
21121         this.owner.deferFocus();
21122     },
21123
21124     /**
21125      * Executes a Midas editor command directly on the editor document.
21126      * For visual commands, you should use {@link #relayCmd} instead.
21127      * <b>This should only be called after the editor is initialized.</b>
21128      * @param {String} cmd The Midas command
21129      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21130      */
21131     execCmd : function(cmd, value){
21132         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21133         this.syncValue();
21134     },
21135  
21136  
21137    
21138     /**
21139      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21140      * to insert tRoo.
21141      * @param {String} text | dom node.. 
21142      */
21143     insertAtCursor : function(text)
21144     {
21145         
21146         if(!this.activated){
21147             return;
21148         }
21149         /*
21150         if(Roo.isIE){
21151             this.win.focus();
21152             var r = this.doc.selection.createRange();
21153             if(r){
21154                 r.collapse(true);
21155                 r.pasteHTML(text);
21156                 this.syncValue();
21157                 this.deferFocus();
21158             
21159             }
21160             return;
21161         }
21162         */
21163         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21164             this.win.focus();
21165             
21166             
21167             // from jquery ui (MIT licenced)
21168             var range, node;
21169             var win = this.win;
21170             
21171             if (win.getSelection && win.getSelection().getRangeAt) {
21172                 range = win.getSelection().getRangeAt(0);
21173                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21174                 range.insertNode(node);
21175             } else if (win.document.selection && win.document.selection.createRange) {
21176                 // no firefox support
21177                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21178                 win.document.selection.createRange().pasteHTML(txt);
21179             } else {
21180                 // no firefox support
21181                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21182                 this.execCmd('InsertHTML', txt);
21183             } 
21184             
21185             this.syncValue();
21186             
21187             this.deferFocus();
21188         }
21189     },
21190  // private
21191     mozKeyPress : function(e){
21192         if(e.ctrlKey){
21193             var c = e.getCharCode(), cmd;
21194           
21195             if(c > 0){
21196                 c = String.fromCharCode(c).toLowerCase();
21197                 switch(c){
21198                     case 'b':
21199                         cmd = 'bold';
21200                         break;
21201                     case 'i':
21202                         cmd = 'italic';
21203                         break;
21204                     
21205                     case 'u':
21206                         cmd = 'underline';
21207                         break;
21208                     
21209                     case 'v':
21210                         this.cleanUpPaste.defer(100, this);
21211                         return;
21212                         
21213                 }
21214                 if(cmd){
21215                     this.win.focus();
21216                     this.execCmd(cmd);
21217                     this.deferFocus();
21218                     e.preventDefault();
21219                 }
21220                 
21221             }
21222         }
21223     },
21224
21225     // private
21226     fixKeys : function(){ // load time branching for fastest keydown performance
21227         if(Roo.isIE){
21228             return function(e){
21229                 var k = e.getKey(), r;
21230                 if(k == e.TAB){
21231                     e.stopEvent();
21232                     r = this.doc.selection.createRange();
21233                     if(r){
21234                         r.collapse(true);
21235                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21236                         this.deferFocus();
21237                     }
21238                     return;
21239                 }
21240                 
21241                 if(k == e.ENTER){
21242                     r = this.doc.selection.createRange();
21243                     if(r){
21244                         var target = r.parentElement();
21245                         if(!target || target.tagName.toLowerCase() != 'li'){
21246                             e.stopEvent();
21247                             r.pasteHTML('<br />');
21248                             r.collapse(false);
21249                             r.select();
21250                         }
21251                     }
21252                 }
21253                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21254                     this.cleanUpPaste.defer(100, this);
21255                     return;
21256                 }
21257                 
21258                 
21259             };
21260         }else if(Roo.isOpera){
21261             return function(e){
21262                 var k = e.getKey();
21263                 if(k == e.TAB){
21264                     e.stopEvent();
21265                     this.win.focus();
21266                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21267                     this.deferFocus();
21268                 }
21269                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21270                     this.cleanUpPaste.defer(100, this);
21271                     return;
21272                 }
21273                 
21274             };
21275         }else if(Roo.isSafari){
21276             return function(e){
21277                 var k = e.getKey();
21278                 
21279                 if(k == e.TAB){
21280                     e.stopEvent();
21281                     this.execCmd('InsertText','\t');
21282                     this.deferFocus();
21283                     return;
21284                 }
21285                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21286                     this.cleanUpPaste.defer(100, this);
21287                     return;
21288                 }
21289                 
21290              };
21291         }
21292     }(),
21293     
21294     getAllAncestors: function()
21295     {
21296         var p = this.getSelectedNode();
21297         var a = [];
21298         if (!p) {
21299             a.push(p); // push blank onto stack..
21300             p = this.getParentElement();
21301         }
21302         
21303         
21304         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21305             a.push(p);
21306             p = p.parentNode;
21307         }
21308         a.push(this.doc.body);
21309         return a;
21310     },
21311     lastSel : false,
21312     lastSelNode : false,
21313     
21314     
21315     getSelection : function() 
21316     {
21317         this.assignDocWin();
21318         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21319     },
21320     
21321     getSelectedNode: function() 
21322     {
21323         // this may only work on Gecko!!!
21324         
21325         // should we cache this!!!!
21326         
21327         
21328         
21329          
21330         var range = this.createRange(this.getSelection()).cloneRange();
21331         
21332         if (Roo.isIE) {
21333             var parent = range.parentElement();
21334             while (true) {
21335                 var testRange = range.duplicate();
21336                 testRange.moveToElementText(parent);
21337                 if (testRange.inRange(range)) {
21338                     break;
21339                 }
21340                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21341                     break;
21342                 }
21343                 parent = parent.parentElement;
21344             }
21345             return parent;
21346         }
21347         
21348         // is ancestor a text element.
21349         var ac =  range.commonAncestorContainer;
21350         if (ac.nodeType == 3) {
21351             ac = ac.parentNode;
21352         }
21353         
21354         var ar = ac.childNodes;
21355          
21356         var nodes = [];
21357         var other_nodes = [];
21358         var has_other_nodes = false;
21359         for (var i=0;i<ar.length;i++) {
21360             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21361                 continue;
21362             }
21363             // fullly contained node.
21364             
21365             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21366                 nodes.push(ar[i]);
21367                 continue;
21368             }
21369             
21370             // probably selected..
21371             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21372                 other_nodes.push(ar[i]);
21373                 continue;
21374             }
21375             // outer..
21376             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21377                 continue;
21378             }
21379             
21380             
21381             has_other_nodes = true;
21382         }
21383         if (!nodes.length && other_nodes.length) {
21384             nodes= other_nodes;
21385         }
21386         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21387             return false;
21388         }
21389         
21390         return nodes[0];
21391     },
21392     createRange: function(sel)
21393     {
21394         // this has strange effects when using with 
21395         // top toolbar - not sure if it's a great idea.
21396         //this.editor.contentWindow.focus();
21397         if (typeof sel != "undefined") {
21398             try {
21399                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21400             } catch(e) {
21401                 return this.doc.createRange();
21402             }
21403         } else {
21404             return this.doc.createRange();
21405         }
21406     },
21407     getParentElement: function()
21408     {
21409         
21410         this.assignDocWin();
21411         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21412         
21413         var range = this.createRange(sel);
21414          
21415         try {
21416             var p = range.commonAncestorContainer;
21417             while (p.nodeType == 3) { // text node
21418                 p = p.parentNode;
21419             }
21420             return p;
21421         } catch (e) {
21422             return null;
21423         }
21424     
21425     },
21426     /***
21427      *
21428      * Range intersection.. the hard stuff...
21429      *  '-1' = before
21430      *  '0' = hits..
21431      *  '1' = after.
21432      *         [ -- selected range --- ]
21433      *   [fail]                        [fail]
21434      *
21435      *    basically..
21436      *      if end is before start or  hits it. fail.
21437      *      if start is after end or hits it fail.
21438      *
21439      *   if either hits (but other is outside. - then it's not 
21440      *   
21441      *    
21442      **/
21443     
21444     
21445     // @see http://www.thismuchiknow.co.uk/?p=64.
21446     rangeIntersectsNode : function(range, node)
21447     {
21448         var nodeRange = node.ownerDocument.createRange();
21449         try {
21450             nodeRange.selectNode(node);
21451         } catch (e) {
21452             nodeRange.selectNodeContents(node);
21453         }
21454     
21455         var rangeStartRange = range.cloneRange();
21456         rangeStartRange.collapse(true);
21457     
21458         var rangeEndRange = range.cloneRange();
21459         rangeEndRange.collapse(false);
21460     
21461         var nodeStartRange = nodeRange.cloneRange();
21462         nodeStartRange.collapse(true);
21463     
21464         var nodeEndRange = nodeRange.cloneRange();
21465         nodeEndRange.collapse(false);
21466     
21467         return rangeStartRange.compareBoundaryPoints(
21468                  Range.START_TO_START, nodeEndRange) == -1 &&
21469                rangeEndRange.compareBoundaryPoints(
21470                  Range.START_TO_START, nodeStartRange) == 1;
21471         
21472          
21473     },
21474     rangeCompareNode : function(range, node)
21475     {
21476         var nodeRange = node.ownerDocument.createRange();
21477         try {
21478             nodeRange.selectNode(node);
21479         } catch (e) {
21480             nodeRange.selectNodeContents(node);
21481         }
21482         
21483         
21484         range.collapse(true);
21485     
21486         nodeRange.collapse(true);
21487      
21488         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21489         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21490          
21491         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21492         
21493         var nodeIsBefore   =  ss == 1;
21494         var nodeIsAfter    = ee == -1;
21495         
21496         if (nodeIsBefore && nodeIsAfter) {
21497             return 0; // outer
21498         }
21499         if (!nodeIsBefore && nodeIsAfter) {
21500             return 1; //right trailed.
21501         }
21502         
21503         if (nodeIsBefore && !nodeIsAfter) {
21504             return 2;  // left trailed.
21505         }
21506         // fully contined.
21507         return 3;
21508     },
21509
21510     // private? - in a new class?
21511     cleanUpPaste :  function()
21512     {
21513         // cleans up the whole document..
21514         Roo.log('cleanuppaste');
21515         
21516         this.cleanUpChildren(this.doc.body);
21517         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21518         if (clean != this.doc.body.innerHTML) {
21519             this.doc.body.innerHTML = clean;
21520         }
21521         
21522     },
21523     
21524     cleanWordChars : function(input) {// change the chars to hex code
21525         var he = Roo.HtmlEditorCore;
21526         
21527         var output = input;
21528         Roo.each(he.swapCodes, function(sw) { 
21529             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21530             
21531             output = output.replace(swapper, sw[1]);
21532         });
21533         
21534         return output;
21535     },
21536     
21537     
21538     cleanUpChildren : function (n)
21539     {
21540         if (!n.childNodes.length) {
21541             return;
21542         }
21543         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21544            this.cleanUpChild(n.childNodes[i]);
21545         }
21546     },
21547     
21548     
21549         
21550     
21551     cleanUpChild : function (node)
21552     {
21553         var ed = this;
21554         //console.log(node);
21555         if (node.nodeName == "#text") {
21556             // clean up silly Windows -- stuff?
21557             return; 
21558         }
21559         if (node.nodeName == "#comment") {
21560             if (!this.allowComments) {
21561                 node.parentNode.removeChild(node);
21562             }
21563             // clean up silly Windows -- stuff?
21564             return; 
21565         }
21566         var lcname = node.tagName.toLowerCase();
21567         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21568         // whitelist of tags..
21569         
21570         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21571             // remove node.
21572             node.parentNode.removeChild(node);
21573             return;
21574             
21575         }
21576         
21577         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21578         
21579         // spans with no attributes - just remove them..
21580         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21581             remove_keep_children = true;
21582         }
21583         
21584         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21585         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21586         
21587         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21588         //    remove_keep_children = true;
21589         //}
21590         
21591         if (remove_keep_children) {
21592             this.cleanUpChildren(node);
21593             // inserts everything just before this node...
21594             while (node.childNodes.length) {
21595                 var cn = node.childNodes[0];
21596                 node.removeChild(cn);
21597                 node.parentNode.insertBefore(cn, node);
21598             }
21599             node.parentNode.removeChild(node);
21600             return;
21601         }
21602         
21603         if (!node.attributes || !node.attributes.length) {
21604             
21605           
21606             
21607             
21608             this.cleanUpChildren(node);
21609             return;
21610         }
21611         
21612         function cleanAttr(n,v)
21613         {
21614             
21615             if (v.match(/^\./) || v.match(/^\//)) {
21616                 return;
21617             }
21618             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21619                 return;
21620             }
21621             if (v.match(/^#/)) {
21622                 return;
21623             }
21624             if (v.match(/^\{/)) { // allow template editing.
21625                 return;
21626             }
21627 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21628             node.removeAttribute(n);
21629             
21630         }
21631         
21632         var cwhite = this.cwhite;
21633         var cblack = this.cblack;
21634             
21635         function cleanStyle(n,v)
21636         {
21637             if (v.match(/expression/)) { //XSS?? should we even bother..
21638                 node.removeAttribute(n);
21639                 return;
21640             }
21641             
21642             var parts = v.split(/;/);
21643             var clean = [];
21644             
21645             Roo.each(parts, function(p) {
21646                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21647                 if (!p.length) {
21648                     return true;
21649                 }
21650                 var l = p.split(':').shift().replace(/\s+/g,'');
21651                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21652                 
21653                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21654 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21655                     //node.removeAttribute(n);
21656                     return true;
21657                 }
21658                 //Roo.log()
21659                 // only allow 'c whitelisted system attributes'
21660                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21661 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21662                     //node.removeAttribute(n);
21663                     return true;
21664                 }
21665                 
21666                 
21667                  
21668                 
21669                 clean.push(p);
21670                 return true;
21671             });
21672             if (clean.length) { 
21673                 node.setAttribute(n, clean.join(';'));
21674             } else {
21675                 node.removeAttribute(n);
21676             }
21677             
21678         }
21679         
21680         
21681         for (var i = node.attributes.length-1; i > -1 ; i--) {
21682             var a = node.attributes[i];
21683             //console.log(a);
21684             
21685             if (a.name.toLowerCase().substr(0,2)=='on')  {
21686                 node.removeAttribute(a.name);
21687                 continue;
21688             }
21689             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21690                 node.removeAttribute(a.name);
21691                 continue;
21692             }
21693             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21694                 cleanAttr(a.name,a.value); // fixme..
21695                 continue;
21696             }
21697             if (a.name == 'style') {
21698                 cleanStyle(a.name,a.value);
21699                 continue;
21700             }
21701             /// clean up MS crap..
21702             // tecnically this should be a list of valid class'es..
21703             
21704             
21705             if (a.name == 'class') {
21706                 if (a.value.match(/^Mso/)) {
21707                     node.removeAttribute('class');
21708                 }
21709                 
21710                 if (a.value.match(/^body$/)) {
21711                     node.removeAttribute('class');
21712                 }
21713                 continue;
21714             }
21715             
21716             // style cleanup!?
21717             // class cleanup?
21718             
21719         }
21720         
21721         
21722         this.cleanUpChildren(node);
21723         
21724         
21725     },
21726     
21727     /**
21728      * Clean up MS wordisms...
21729      */
21730     cleanWord : function(node)
21731     {
21732         if (!node) {
21733             this.cleanWord(this.doc.body);
21734             return;
21735         }
21736         
21737         if(
21738                 node.nodeName == 'SPAN' &&
21739                 !node.hasAttributes() &&
21740                 node.childNodes.length == 1 &&
21741                 node.firstChild.nodeName == "#text"  
21742         ) {
21743             var textNode = node.firstChild;
21744             node.removeChild(textNode);
21745             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21746                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21747             }
21748             node.parentNode.insertBefore(textNode, node);
21749             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21750                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21751             }
21752             node.parentNode.removeChild(node);
21753         }
21754         
21755         if (node.nodeName == "#text") {
21756             // clean up silly Windows -- stuff?
21757             return; 
21758         }
21759         if (node.nodeName == "#comment") {
21760             node.parentNode.removeChild(node);
21761             // clean up silly Windows -- stuff?
21762             return; 
21763         }
21764         
21765         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21766             node.parentNode.removeChild(node);
21767             return;
21768         }
21769         //Roo.log(node.tagName);
21770         // remove - but keep children..
21771         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21772             //Roo.log('-- removed');
21773             while (node.childNodes.length) {
21774                 var cn = node.childNodes[0];
21775                 node.removeChild(cn);
21776                 node.parentNode.insertBefore(cn, node);
21777                 // move node to parent - and clean it..
21778                 this.cleanWord(cn);
21779             }
21780             node.parentNode.removeChild(node);
21781             /// no need to iterate chidlren = it's got none..
21782             //this.iterateChildren(node, this.cleanWord);
21783             return;
21784         }
21785         // clean styles
21786         if (node.className.length) {
21787             
21788             var cn = node.className.split(/\W+/);
21789             var cna = [];
21790             Roo.each(cn, function(cls) {
21791                 if (cls.match(/Mso[a-zA-Z]+/)) {
21792                     return;
21793                 }
21794                 cna.push(cls);
21795             });
21796             node.className = cna.length ? cna.join(' ') : '';
21797             if (!cna.length) {
21798                 node.removeAttribute("class");
21799             }
21800         }
21801         
21802         if (node.hasAttribute("lang")) {
21803             node.removeAttribute("lang");
21804         }
21805         
21806         if (node.hasAttribute("style")) {
21807             
21808             var styles = node.getAttribute("style").split(";");
21809             var nstyle = [];
21810             Roo.each(styles, function(s) {
21811                 if (!s.match(/:/)) {
21812                     return;
21813                 }
21814                 var kv = s.split(":");
21815                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21816                     return;
21817                 }
21818                 // what ever is left... we allow.
21819                 nstyle.push(s);
21820             });
21821             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21822             if (!nstyle.length) {
21823                 node.removeAttribute('style');
21824             }
21825         }
21826         this.iterateChildren(node, this.cleanWord);
21827         
21828         
21829         
21830     },
21831     /**
21832      * iterateChildren of a Node, calling fn each time, using this as the scole..
21833      * @param {DomNode} node node to iterate children of.
21834      * @param {Function} fn method of this class to call on each item.
21835      */
21836     iterateChildren : function(node, fn)
21837     {
21838         if (!node.childNodes.length) {
21839                 return;
21840         }
21841         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21842            fn.call(this, node.childNodes[i])
21843         }
21844     },
21845     
21846     
21847     /**
21848      * cleanTableWidths.
21849      *
21850      * Quite often pasting from word etc.. results in tables with column and widths.
21851      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21852      *
21853      */
21854     cleanTableWidths : function(node)
21855     {
21856          
21857          
21858         if (!node) {
21859             this.cleanTableWidths(this.doc.body);
21860             return;
21861         }
21862         
21863         // ignore list...
21864         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21865             return; 
21866         }
21867         Roo.log(node.tagName);
21868         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21869             this.iterateChildren(node, this.cleanTableWidths);
21870             return;
21871         }
21872         if (node.hasAttribute('width')) {
21873             node.removeAttribute('width');
21874         }
21875         
21876          
21877         if (node.hasAttribute("style")) {
21878             // pretty basic...
21879             
21880             var styles = node.getAttribute("style").split(";");
21881             var nstyle = [];
21882             Roo.each(styles, function(s) {
21883                 if (!s.match(/:/)) {
21884                     return;
21885                 }
21886                 var kv = s.split(":");
21887                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21888                     return;
21889                 }
21890                 // what ever is left... we allow.
21891                 nstyle.push(s);
21892             });
21893             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21894             if (!nstyle.length) {
21895                 node.removeAttribute('style');
21896             }
21897         }
21898         
21899         this.iterateChildren(node, this.cleanTableWidths);
21900         
21901         
21902     },
21903     
21904     
21905     
21906     
21907     domToHTML : function(currentElement, depth, nopadtext) {
21908         
21909         depth = depth || 0;
21910         nopadtext = nopadtext || false;
21911     
21912         if (!currentElement) {
21913             return this.domToHTML(this.doc.body);
21914         }
21915         
21916         //Roo.log(currentElement);
21917         var j;
21918         var allText = false;
21919         var nodeName = currentElement.nodeName;
21920         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21921         
21922         if  (nodeName == '#text') {
21923             
21924             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21925         }
21926         
21927         
21928         var ret = '';
21929         if (nodeName != 'BODY') {
21930              
21931             var i = 0;
21932             // Prints the node tagName, such as <A>, <IMG>, etc
21933             if (tagName) {
21934                 var attr = [];
21935                 for(i = 0; i < currentElement.attributes.length;i++) {
21936                     // quoting?
21937                     var aname = currentElement.attributes.item(i).name;
21938                     if (!currentElement.attributes.item(i).value.length) {
21939                         continue;
21940                     }
21941                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21942                 }
21943                 
21944                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21945             } 
21946             else {
21947                 
21948                 // eack
21949             }
21950         } else {
21951             tagName = false;
21952         }
21953         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21954             return ret;
21955         }
21956         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21957             nopadtext = true;
21958         }
21959         
21960         
21961         // Traverse the tree
21962         i = 0;
21963         var currentElementChild = currentElement.childNodes.item(i);
21964         var allText = true;
21965         var innerHTML  = '';
21966         lastnode = '';
21967         while (currentElementChild) {
21968             // Formatting code (indent the tree so it looks nice on the screen)
21969             var nopad = nopadtext;
21970             if (lastnode == 'SPAN') {
21971                 nopad  = true;
21972             }
21973             // text
21974             if  (currentElementChild.nodeName == '#text') {
21975                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21976                 toadd = nopadtext ? toadd : toadd.trim();
21977                 if (!nopad && toadd.length > 80) {
21978                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21979                 }
21980                 innerHTML  += toadd;
21981                 
21982                 i++;
21983                 currentElementChild = currentElement.childNodes.item(i);
21984                 lastNode = '';
21985                 continue;
21986             }
21987             allText = false;
21988             
21989             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21990                 
21991             // Recursively traverse the tree structure of the child node
21992             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21993             lastnode = currentElementChild.nodeName;
21994             i++;
21995             currentElementChild=currentElement.childNodes.item(i);
21996         }
21997         
21998         ret += innerHTML;
21999         
22000         if (!allText) {
22001                 // The remaining code is mostly for formatting the tree
22002             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22003         }
22004         
22005         
22006         if (tagName) {
22007             ret+= "</"+tagName+">";
22008         }
22009         return ret;
22010         
22011     },
22012         
22013     applyBlacklists : function()
22014     {
22015         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22016         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22017         
22018         this.white = [];
22019         this.black = [];
22020         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22021             if (b.indexOf(tag) > -1) {
22022                 return;
22023             }
22024             this.white.push(tag);
22025             
22026         }, this);
22027         
22028         Roo.each(w, function(tag) {
22029             if (b.indexOf(tag) > -1) {
22030                 return;
22031             }
22032             if (this.white.indexOf(tag) > -1) {
22033                 return;
22034             }
22035             this.white.push(tag);
22036             
22037         }, this);
22038         
22039         
22040         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22041             if (w.indexOf(tag) > -1) {
22042                 return;
22043             }
22044             this.black.push(tag);
22045             
22046         }, this);
22047         
22048         Roo.each(b, function(tag) {
22049             if (w.indexOf(tag) > -1) {
22050                 return;
22051             }
22052             if (this.black.indexOf(tag) > -1) {
22053                 return;
22054             }
22055             this.black.push(tag);
22056             
22057         }, this);
22058         
22059         
22060         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22061         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22062         
22063         this.cwhite = [];
22064         this.cblack = [];
22065         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22066             if (b.indexOf(tag) > -1) {
22067                 return;
22068             }
22069             this.cwhite.push(tag);
22070             
22071         }, this);
22072         
22073         Roo.each(w, function(tag) {
22074             if (b.indexOf(tag) > -1) {
22075                 return;
22076             }
22077             if (this.cwhite.indexOf(tag) > -1) {
22078                 return;
22079             }
22080             this.cwhite.push(tag);
22081             
22082         }, this);
22083         
22084         
22085         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22086             if (w.indexOf(tag) > -1) {
22087                 return;
22088             }
22089             this.cblack.push(tag);
22090             
22091         }, this);
22092         
22093         Roo.each(b, function(tag) {
22094             if (w.indexOf(tag) > -1) {
22095                 return;
22096             }
22097             if (this.cblack.indexOf(tag) > -1) {
22098                 return;
22099             }
22100             this.cblack.push(tag);
22101             
22102         }, this);
22103     },
22104     
22105     setStylesheets : function(stylesheets)
22106     {
22107         if(typeof(stylesheets) == 'string'){
22108             Roo.get(this.iframe.contentDocument.head).createChild({
22109                 tag : 'link',
22110                 rel : 'stylesheet',
22111                 type : 'text/css',
22112                 href : stylesheets
22113             });
22114             
22115             return;
22116         }
22117         var _this = this;
22118      
22119         Roo.each(stylesheets, function(s) {
22120             if(!s.length){
22121                 return;
22122             }
22123             
22124             Roo.get(_this.iframe.contentDocument.head).createChild({
22125                 tag : 'link',
22126                 rel : 'stylesheet',
22127                 type : 'text/css',
22128                 href : s
22129             });
22130         });
22131
22132         
22133     },
22134     
22135     removeStylesheets : function()
22136     {
22137         var _this = this;
22138         
22139         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22140             s.remove();
22141         });
22142     },
22143     
22144     setStyle : function(style)
22145     {
22146         Roo.get(this.iframe.contentDocument.head).createChild({
22147             tag : 'style',
22148             type : 'text/css',
22149             html : style
22150         });
22151
22152         return;
22153     }
22154     
22155     // hide stuff that is not compatible
22156     /**
22157      * @event blur
22158      * @hide
22159      */
22160     /**
22161      * @event change
22162      * @hide
22163      */
22164     /**
22165      * @event focus
22166      * @hide
22167      */
22168     /**
22169      * @event specialkey
22170      * @hide
22171      */
22172     /**
22173      * @cfg {String} fieldClass @hide
22174      */
22175     /**
22176      * @cfg {String} focusClass @hide
22177      */
22178     /**
22179      * @cfg {String} autoCreate @hide
22180      */
22181     /**
22182      * @cfg {String} inputType @hide
22183      */
22184     /**
22185      * @cfg {String} invalidClass @hide
22186      */
22187     /**
22188      * @cfg {String} invalidText @hide
22189      */
22190     /**
22191      * @cfg {String} msgFx @hide
22192      */
22193     /**
22194      * @cfg {String} validateOnBlur @hide
22195      */
22196 });
22197
22198 Roo.HtmlEditorCore.white = [
22199         'area', 'br', 'img', 'input', 'hr', 'wbr',
22200         
22201        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22202        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22203        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22204        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22205        'table',   'ul',         'xmp', 
22206        
22207        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22208       'thead',   'tr', 
22209      
22210       'dir', 'menu', 'ol', 'ul', 'dl',
22211        
22212       'embed',  'object'
22213 ];
22214
22215
22216 Roo.HtmlEditorCore.black = [
22217     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22218         'applet', // 
22219         'base',   'basefont', 'bgsound', 'blink',  'body', 
22220         'frame',  'frameset', 'head',    'html',   'ilayer', 
22221         'iframe', 'layer',  'link',     'meta',    'object',   
22222         'script', 'style' ,'title',  'xml' // clean later..
22223 ];
22224 Roo.HtmlEditorCore.clean = [
22225     'script', 'style', 'title', 'xml'
22226 ];
22227 Roo.HtmlEditorCore.remove = [
22228     'font'
22229 ];
22230 // attributes..
22231
22232 Roo.HtmlEditorCore.ablack = [
22233     'on'
22234 ];
22235     
22236 Roo.HtmlEditorCore.aclean = [ 
22237     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22238 ];
22239
22240 // protocols..
22241 Roo.HtmlEditorCore.pwhite= [
22242         'http',  'https',  'mailto'
22243 ];
22244
22245 // white listed style attributes.
22246 Roo.HtmlEditorCore.cwhite= [
22247       //  'text-align', /// default is to allow most things..
22248       
22249          
22250 //        'font-size'//??
22251 ];
22252
22253 // black listed style attributes.
22254 Roo.HtmlEditorCore.cblack= [
22255       //  'font-size' -- this can be set by the project 
22256 ];
22257
22258
22259 Roo.HtmlEditorCore.swapCodes   =[ 
22260     [    8211, "&#8211;" ], 
22261     [    8212, "&#8212;" ], 
22262     [    8216,  "'" ],  
22263     [    8217, "'" ],  
22264     [    8220, '"' ],  
22265     [    8221, '"' ],  
22266     [    8226, "*" ],  
22267     [    8230, "..." ]
22268 ]; 
22269
22270     //<script type="text/javascript">
22271
22272 /*
22273  * Ext JS Library 1.1.1
22274  * Copyright(c) 2006-2007, Ext JS, LLC.
22275  * Licence LGPL
22276  * 
22277  */
22278  
22279  
22280 Roo.form.HtmlEditor = function(config){
22281     
22282     
22283     
22284     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22285     
22286     if (!this.toolbars) {
22287         this.toolbars = [];
22288     }
22289     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22290     
22291     
22292 };
22293
22294 /**
22295  * @class Roo.form.HtmlEditor
22296  * @extends Roo.form.Field
22297  * Provides a lightweight HTML Editor component.
22298  *
22299  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22300  * 
22301  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22302  * supported by this editor.</b><br/><br/>
22303  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22304  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22305  */
22306 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22307     /**
22308      * @cfg {Boolean} clearUp
22309      */
22310     clearUp : true,
22311       /**
22312      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22313      */
22314     toolbars : false,
22315    
22316      /**
22317      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22318      *                        Roo.resizable.
22319      */
22320     resizable : false,
22321      /**
22322      * @cfg {Number} height (in pixels)
22323      */   
22324     height: 300,
22325    /**
22326      * @cfg {Number} width (in pixels)
22327      */   
22328     width: 500,
22329     
22330     /**
22331      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22332      * 
22333      */
22334     stylesheets: false,
22335     
22336     
22337      /**
22338      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22339      * 
22340      */
22341     cblack: false,
22342     /**
22343      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22344      * 
22345      */
22346     cwhite: false,
22347     
22348      /**
22349      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22350      * 
22351      */
22352     black: false,
22353     /**
22354      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22355      * 
22356      */
22357     white: false,
22358     /**
22359      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22360      */
22361     allowComments: false,
22362     
22363     // id of frame..
22364     frameId: false,
22365     
22366     // private properties
22367     validationEvent : false,
22368     deferHeight: true,
22369     initialized : false,
22370     activated : false,
22371     
22372     onFocus : Roo.emptyFn,
22373     iframePad:3,
22374     hideMode:'offsets',
22375     
22376     actionMode : 'container', // defaults to hiding it...
22377     
22378     defaultAutoCreate : { // modified by initCompnoent..
22379         tag: "textarea",
22380         style:"width:500px;height:300px;",
22381         autocomplete: "new-password"
22382     },
22383
22384     // private
22385     initComponent : function(){
22386         this.addEvents({
22387             /**
22388              * @event initialize
22389              * Fires when the editor is fully initialized (including the iframe)
22390              * @param {HtmlEditor} this
22391              */
22392             initialize: true,
22393             /**
22394              * @event activate
22395              * Fires when the editor is first receives the focus. Any insertion must wait
22396              * until after this event.
22397              * @param {HtmlEditor} this
22398              */
22399             activate: true,
22400              /**
22401              * @event beforesync
22402              * Fires before the textarea is updated with content from the editor iframe. Return false
22403              * to cancel the sync.
22404              * @param {HtmlEditor} this
22405              * @param {String} html
22406              */
22407             beforesync: true,
22408              /**
22409              * @event beforepush
22410              * Fires before the iframe editor is updated with content from the textarea. Return false
22411              * to cancel the push.
22412              * @param {HtmlEditor} this
22413              * @param {String} html
22414              */
22415             beforepush: true,
22416              /**
22417              * @event sync
22418              * Fires when the textarea is updated with content from the editor iframe.
22419              * @param {HtmlEditor} this
22420              * @param {String} html
22421              */
22422             sync: true,
22423              /**
22424              * @event push
22425              * Fires when the iframe editor is updated with content from the textarea.
22426              * @param {HtmlEditor} this
22427              * @param {String} html
22428              */
22429             push: true,
22430              /**
22431              * @event editmodechange
22432              * Fires when the editor switches edit modes
22433              * @param {HtmlEditor} this
22434              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22435              */
22436             editmodechange: true,
22437             /**
22438              * @event editorevent
22439              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22440              * @param {HtmlEditor} this
22441              */
22442             editorevent: true,
22443             /**
22444              * @event firstfocus
22445              * Fires when on first focus - needed by toolbars..
22446              * @param {HtmlEditor} this
22447              */
22448             firstfocus: true,
22449             /**
22450              * @event autosave
22451              * Auto save the htmlEditor value as a file into Events
22452              * @param {HtmlEditor} this
22453              */
22454             autosave: true,
22455             /**
22456              * @event savedpreview
22457              * preview the saved version of htmlEditor
22458              * @param {HtmlEditor} this
22459              */
22460             savedpreview: true,
22461             
22462             /**
22463             * @event stylesheetsclick
22464             * Fires when press the Sytlesheets button
22465             * @param {Roo.HtmlEditorCore} this
22466             */
22467             stylesheetsclick: true
22468         });
22469         this.defaultAutoCreate =  {
22470             tag: "textarea",
22471             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22472             autocomplete: "new-password"
22473         };
22474     },
22475
22476     /**
22477      * Protected method that will not generally be called directly. It
22478      * is called when the editor creates its toolbar. Override this method if you need to
22479      * add custom toolbar buttons.
22480      * @param {HtmlEditor} editor
22481      */
22482     createToolbar : function(editor){
22483         Roo.log("create toolbars");
22484         if (!editor.toolbars || !editor.toolbars.length) {
22485             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22486         }
22487         
22488         for (var i =0 ; i < editor.toolbars.length;i++) {
22489             editor.toolbars[i] = Roo.factory(
22490                     typeof(editor.toolbars[i]) == 'string' ?
22491                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22492                 Roo.form.HtmlEditor);
22493             editor.toolbars[i].init(editor);
22494         }
22495          
22496         
22497     },
22498
22499      
22500     // private
22501     onRender : function(ct, position)
22502     {
22503         var _t = this;
22504         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22505         
22506         this.wrap = this.el.wrap({
22507             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22508         });
22509         
22510         this.editorcore.onRender(ct, position);
22511          
22512         if (this.resizable) {
22513             this.resizeEl = new Roo.Resizable(this.wrap, {
22514                 pinned : true,
22515                 wrap: true,
22516                 dynamic : true,
22517                 minHeight : this.height,
22518                 height: this.height,
22519                 handles : this.resizable,
22520                 width: this.width,
22521                 listeners : {
22522                     resize : function(r, w, h) {
22523                         _t.onResize(w,h); // -something
22524                     }
22525                 }
22526             });
22527             
22528         }
22529         this.createToolbar(this);
22530        
22531         
22532         if(!this.width){
22533             this.setSize(this.wrap.getSize());
22534         }
22535         if (this.resizeEl) {
22536             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22537             // should trigger onReize..
22538         }
22539         
22540         this.keyNav = new Roo.KeyNav(this.el, {
22541             
22542             "tab" : function(e){
22543                 e.preventDefault();
22544                 
22545                 var value = this.getValue();
22546                 
22547                 var start = this.el.dom.selectionStart;
22548                 var end = this.el.dom.selectionEnd;
22549                 
22550                 if(!e.shiftKey){
22551                     
22552                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22553                     this.el.dom.setSelectionRange(end + 1, end + 1);
22554                     return;
22555                 }
22556                 
22557                 var f = value.substring(0, start).split("\t");
22558                 
22559                 if(f.pop().length != 0){
22560                     return;
22561                 }
22562                 
22563                 this.setValue(f.join("\t") + value.substring(end));
22564                 this.el.dom.setSelectionRange(start - 1, start - 1);
22565                 
22566             },
22567             
22568             "home" : function(e){
22569                 e.preventDefault();
22570                 
22571                 var curr = this.el.dom.selectionStart;
22572                 var lines = this.getValue().split("\n");
22573                 
22574                 if(!lines.length){
22575                     return;
22576                 }
22577                 
22578                 if(e.ctrlKey){
22579                     this.el.dom.setSelectionRange(0, 0);
22580                     return;
22581                 }
22582                 
22583                 var pos = 0;
22584                 
22585                 for (var i = 0; i < lines.length;i++) {
22586                     pos += lines[i].length;
22587                     
22588                     if(i != 0){
22589                         pos += 1;
22590                     }
22591                     
22592                     if(pos < curr){
22593                         continue;
22594                     }
22595                     
22596                     pos -= lines[i].length;
22597                     
22598                     break;
22599                 }
22600                 
22601                 if(!e.shiftKey){
22602                     this.el.dom.setSelectionRange(pos, pos);
22603                     return;
22604                 }
22605                 
22606                 this.el.dom.selectionStart = pos;
22607                 this.el.dom.selectionEnd = curr;
22608             },
22609             
22610             "end" : function(e){
22611                 e.preventDefault();
22612                 
22613                 var curr = this.el.dom.selectionStart;
22614                 var lines = this.getValue().split("\n");
22615                 
22616                 if(!lines.length){
22617                     return;
22618                 }
22619                 
22620                 if(e.ctrlKey){
22621                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22622                     return;
22623                 }
22624                 
22625                 var pos = 0;
22626                 
22627                 for (var i = 0; i < lines.length;i++) {
22628                     
22629                     pos += lines[i].length;
22630                     
22631                     if(i != 0){
22632                         pos += 1;
22633                     }
22634                     
22635                     if(pos < curr){
22636                         continue;
22637                     }
22638                     
22639                     break;
22640                 }
22641                 
22642                 if(!e.shiftKey){
22643                     this.el.dom.setSelectionRange(pos, pos);
22644                     return;
22645                 }
22646                 
22647                 this.el.dom.selectionStart = curr;
22648                 this.el.dom.selectionEnd = pos;
22649             },
22650
22651             scope : this,
22652
22653             doRelay : function(foo, bar, hname){
22654                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22655             },
22656
22657             forceKeyDown: true
22658         });
22659         
22660 //        if(this.autosave && this.w){
22661 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22662 //        }
22663     },
22664
22665     // private
22666     onResize : function(w, h)
22667     {
22668         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22669         var ew = false;
22670         var eh = false;
22671         
22672         if(this.el ){
22673             if(typeof w == 'number'){
22674                 var aw = w - this.wrap.getFrameWidth('lr');
22675                 this.el.setWidth(this.adjustWidth('textarea', aw));
22676                 ew = aw;
22677             }
22678             if(typeof h == 'number'){
22679                 var tbh = 0;
22680                 for (var i =0; i < this.toolbars.length;i++) {
22681                     // fixme - ask toolbars for heights?
22682                     tbh += this.toolbars[i].tb.el.getHeight();
22683                     if (this.toolbars[i].footer) {
22684                         tbh += this.toolbars[i].footer.el.getHeight();
22685                     }
22686                 }
22687                 
22688                 
22689                 
22690                 
22691                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22692                 ah -= 5; // knock a few pixes off for look..
22693 //                Roo.log(ah);
22694                 this.el.setHeight(this.adjustWidth('textarea', ah));
22695                 var eh = ah;
22696             }
22697         }
22698         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22699         this.editorcore.onResize(ew,eh);
22700         
22701     },
22702
22703     /**
22704      * Toggles the editor between standard and source edit mode.
22705      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22706      */
22707     toggleSourceEdit : function(sourceEditMode)
22708     {
22709         this.editorcore.toggleSourceEdit(sourceEditMode);
22710         
22711         if(this.editorcore.sourceEditMode){
22712             Roo.log('editor - showing textarea');
22713             
22714 //            Roo.log('in');
22715 //            Roo.log(this.syncValue());
22716             this.editorcore.syncValue();
22717             this.el.removeClass('x-hidden');
22718             this.el.dom.removeAttribute('tabIndex');
22719             this.el.focus();
22720             
22721             for (var i = 0; i < this.toolbars.length; i++) {
22722                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22723                     this.toolbars[i].tb.hide();
22724                     this.toolbars[i].footer.hide();
22725                 }
22726             }
22727             
22728         }else{
22729             Roo.log('editor - hiding textarea');
22730 //            Roo.log('out')
22731 //            Roo.log(this.pushValue()); 
22732             this.editorcore.pushValue();
22733             
22734             this.el.addClass('x-hidden');
22735             this.el.dom.setAttribute('tabIndex', -1);
22736             
22737             for (var i = 0; i < this.toolbars.length; i++) {
22738                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22739                     this.toolbars[i].tb.show();
22740                     this.toolbars[i].footer.show();
22741                 }
22742             }
22743             
22744             //this.deferFocus();
22745         }
22746         
22747         this.setSize(this.wrap.getSize());
22748         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22749         
22750         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22751     },
22752  
22753     // private (for BoxComponent)
22754     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22755
22756     // private (for BoxComponent)
22757     getResizeEl : function(){
22758         return this.wrap;
22759     },
22760
22761     // private (for BoxComponent)
22762     getPositionEl : function(){
22763         return this.wrap;
22764     },
22765
22766     // private
22767     initEvents : function(){
22768         this.originalValue = this.getValue();
22769     },
22770
22771     /**
22772      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22773      * @method
22774      */
22775     markInvalid : Roo.emptyFn,
22776     /**
22777      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22778      * @method
22779      */
22780     clearInvalid : Roo.emptyFn,
22781
22782     setValue : function(v){
22783         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22784         this.editorcore.pushValue();
22785     },
22786
22787      
22788     // private
22789     deferFocus : function(){
22790         this.focus.defer(10, this);
22791     },
22792
22793     // doc'ed in Field
22794     focus : function(){
22795         this.editorcore.focus();
22796         
22797     },
22798       
22799
22800     // private
22801     onDestroy : function(){
22802         
22803         
22804         
22805         if(this.rendered){
22806             
22807             for (var i =0; i < this.toolbars.length;i++) {
22808                 // fixme - ask toolbars for heights?
22809                 this.toolbars[i].onDestroy();
22810             }
22811             
22812             this.wrap.dom.innerHTML = '';
22813             this.wrap.remove();
22814         }
22815     },
22816
22817     // private
22818     onFirstFocus : function(){
22819         //Roo.log("onFirstFocus");
22820         this.editorcore.onFirstFocus();
22821          for (var i =0; i < this.toolbars.length;i++) {
22822             this.toolbars[i].onFirstFocus();
22823         }
22824         
22825     },
22826     
22827     // private
22828     syncValue : function()
22829     {
22830         this.editorcore.syncValue();
22831     },
22832     
22833     pushValue : function()
22834     {
22835         this.editorcore.pushValue();
22836     },
22837     
22838     setStylesheets : function(stylesheets)
22839     {
22840         this.editorcore.setStylesheets(stylesheets);
22841     },
22842     
22843     removeStylesheets : function()
22844     {
22845         this.editorcore.removeStylesheets();
22846     }
22847      
22848     
22849     // hide stuff that is not compatible
22850     /**
22851      * @event blur
22852      * @hide
22853      */
22854     /**
22855      * @event change
22856      * @hide
22857      */
22858     /**
22859      * @event focus
22860      * @hide
22861      */
22862     /**
22863      * @event specialkey
22864      * @hide
22865      */
22866     /**
22867      * @cfg {String} fieldClass @hide
22868      */
22869     /**
22870      * @cfg {String} focusClass @hide
22871      */
22872     /**
22873      * @cfg {String} autoCreate @hide
22874      */
22875     /**
22876      * @cfg {String} inputType @hide
22877      */
22878     /**
22879      * @cfg {String} invalidClass @hide
22880      */
22881     /**
22882      * @cfg {String} invalidText @hide
22883      */
22884     /**
22885      * @cfg {String} msgFx @hide
22886      */
22887     /**
22888      * @cfg {String} validateOnBlur @hide
22889      */
22890 });
22891  
22892     // <script type="text/javascript">
22893 /*
22894  * Based on
22895  * Ext JS Library 1.1.1
22896  * Copyright(c) 2006-2007, Ext JS, LLC.
22897  *  
22898  
22899  */
22900
22901 /**
22902  * @class Roo.form.HtmlEditorToolbar1
22903  * Basic Toolbar
22904  * 
22905  * Usage:
22906  *
22907  new Roo.form.HtmlEditor({
22908     ....
22909     toolbars : [
22910         new Roo.form.HtmlEditorToolbar1({
22911             disable : { fonts: 1 , format: 1, ..., ... , ...],
22912             btns : [ .... ]
22913         })
22914     }
22915      
22916  * 
22917  * @cfg {Object} disable List of elements to disable..
22918  * @cfg {Array} btns List of additional buttons.
22919  * 
22920  * 
22921  * NEEDS Extra CSS? 
22922  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22923  */
22924  
22925 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22926 {
22927     
22928     Roo.apply(this, config);
22929     
22930     // default disabled, based on 'good practice'..
22931     this.disable = this.disable || {};
22932     Roo.applyIf(this.disable, {
22933         fontSize : true,
22934         colors : true,
22935         specialElements : true
22936     });
22937     
22938     
22939     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22940     // dont call parent... till later.
22941 }
22942
22943 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22944     
22945     tb: false,
22946     
22947     rendered: false,
22948     
22949     editor : false,
22950     editorcore : false,
22951     /**
22952      * @cfg {Object} disable  List of toolbar elements to disable
22953          
22954      */
22955     disable : false,
22956     
22957     
22958      /**
22959      * @cfg {String} createLinkText The default text for the create link prompt
22960      */
22961     createLinkText : 'Please enter the URL for the link:',
22962     /**
22963      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22964      */
22965     defaultLinkValue : 'http:/'+'/',
22966    
22967     
22968       /**
22969      * @cfg {Array} fontFamilies An array of available font families
22970      */
22971     fontFamilies : [
22972         'Arial',
22973         'Courier New',
22974         'Tahoma',
22975         'Times New Roman',
22976         'Verdana'
22977     ],
22978     
22979     specialChars : [
22980            "&#169;",
22981           "&#174;",     
22982           "&#8482;",    
22983           "&#163;" ,    
22984          // "&#8212;",    
22985           "&#8230;",    
22986           "&#247;" ,    
22987         //  "&#225;" ,     ?? a acute?
22988            "&#8364;"    , //Euro
22989        //   "&#8220;"    ,
22990         //  "&#8221;"    ,
22991         //  "&#8226;"    ,
22992           "&#176;"  //   , // degrees
22993
22994          // "&#233;"     , // e ecute
22995          // "&#250;"     , // u ecute?
22996     ],
22997     
22998     specialElements : [
22999         {
23000             text: "Insert Table",
23001             xtype: 'MenuItem',
23002             xns : Roo.Menu,
23003             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23004                 
23005         },
23006         {    
23007             text: "Insert Image",
23008             xtype: 'MenuItem',
23009             xns : Roo.Menu,
23010             ihtml : '<img src="about:blank"/>'
23011             
23012         }
23013         
23014          
23015     ],
23016     
23017     
23018     inputElements : [ 
23019             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23020             "input:submit", "input:button", "select", "textarea", "label" ],
23021     formats : [
23022         ["p"] ,  
23023         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23024         ["pre"],[ "code"], 
23025         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23026         ['div'],['span'],
23027         ['sup'],['sub']
23028     ],
23029     
23030     cleanStyles : [
23031         "font-size"
23032     ],
23033      /**
23034      * @cfg {String} defaultFont default font to use.
23035      */
23036     defaultFont: 'tahoma',
23037    
23038     fontSelect : false,
23039     
23040     
23041     formatCombo : false,
23042     
23043     init : function(editor)
23044     {
23045         this.editor = editor;
23046         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23047         var editorcore = this.editorcore;
23048         
23049         var _t = this;
23050         
23051         var fid = editorcore.frameId;
23052         var etb = this;
23053         function btn(id, toggle, handler){
23054             var xid = fid + '-'+ id ;
23055             return {
23056                 id : xid,
23057                 cmd : id,
23058                 cls : 'x-btn-icon x-edit-'+id,
23059                 enableToggle:toggle !== false,
23060                 scope: _t, // was editor...
23061                 handler:handler||_t.relayBtnCmd,
23062                 clickEvent:'mousedown',
23063                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23064                 tabIndex:-1
23065             };
23066         }
23067         
23068         
23069         
23070         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23071         this.tb = tb;
23072          // stop form submits
23073         tb.el.on('click', function(e){
23074             e.preventDefault(); // what does this do?
23075         });
23076
23077         if(!this.disable.font) { // && !Roo.isSafari){
23078             /* why no safari for fonts 
23079             editor.fontSelect = tb.el.createChild({
23080                 tag:'select',
23081                 tabIndex: -1,
23082                 cls:'x-font-select',
23083                 html: this.createFontOptions()
23084             });
23085             
23086             editor.fontSelect.on('change', function(){
23087                 var font = editor.fontSelect.dom.value;
23088                 editor.relayCmd('fontname', font);
23089                 editor.deferFocus();
23090             }, editor);
23091             
23092             tb.add(
23093                 editor.fontSelect.dom,
23094                 '-'
23095             );
23096             */
23097             
23098         };
23099         if(!this.disable.formats){
23100             this.formatCombo = new Roo.form.ComboBox({
23101                 store: new Roo.data.SimpleStore({
23102                     id : 'tag',
23103                     fields: ['tag'],
23104                     data : this.formats // from states.js
23105                 }),
23106                 blockFocus : true,
23107                 name : '',
23108                 //autoCreate : {tag: "div",  size: "20"},
23109                 displayField:'tag',
23110                 typeAhead: false,
23111                 mode: 'local',
23112                 editable : false,
23113                 triggerAction: 'all',
23114                 emptyText:'Add tag',
23115                 selectOnFocus:true,
23116                 width:135,
23117                 listeners : {
23118                     'select': function(c, r, i) {
23119                         editorcore.insertTag(r.get('tag'));
23120                         editor.focus();
23121                     }
23122                 }
23123
23124             });
23125             tb.addField(this.formatCombo);
23126             
23127         }
23128         
23129         if(!this.disable.format){
23130             tb.add(
23131                 btn('bold'),
23132                 btn('italic'),
23133                 btn('underline'),
23134                 btn('strikethrough')
23135             );
23136         };
23137         if(!this.disable.fontSize){
23138             tb.add(
23139                 '-',
23140                 
23141                 
23142                 btn('increasefontsize', false, editorcore.adjustFont),
23143                 btn('decreasefontsize', false, editorcore.adjustFont)
23144             );
23145         };
23146         
23147         
23148         if(!this.disable.colors){
23149             tb.add(
23150                 '-', {
23151                     id:editorcore.frameId +'-forecolor',
23152                     cls:'x-btn-icon x-edit-forecolor',
23153                     clickEvent:'mousedown',
23154                     tooltip: this.buttonTips['forecolor'] || undefined,
23155                     tabIndex:-1,
23156                     menu : new Roo.menu.ColorMenu({
23157                         allowReselect: true,
23158                         focus: Roo.emptyFn,
23159                         value:'000000',
23160                         plain:true,
23161                         selectHandler: function(cp, color){
23162                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23163                             editor.deferFocus();
23164                         },
23165                         scope: editorcore,
23166                         clickEvent:'mousedown'
23167                     })
23168                 }, {
23169                     id:editorcore.frameId +'backcolor',
23170                     cls:'x-btn-icon x-edit-backcolor',
23171                     clickEvent:'mousedown',
23172                     tooltip: this.buttonTips['backcolor'] || undefined,
23173                     tabIndex:-1,
23174                     menu : new Roo.menu.ColorMenu({
23175                         focus: Roo.emptyFn,
23176                         value:'FFFFFF',
23177                         plain:true,
23178                         allowReselect: true,
23179                         selectHandler: function(cp, color){
23180                             if(Roo.isGecko){
23181                                 editorcore.execCmd('useCSS', false);
23182                                 editorcore.execCmd('hilitecolor', color);
23183                                 editorcore.execCmd('useCSS', true);
23184                                 editor.deferFocus();
23185                             }else{
23186                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23187                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23188                                 editor.deferFocus();
23189                             }
23190                         },
23191                         scope:editorcore,
23192                         clickEvent:'mousedown'
23193                     })
23194                 }
23195             );
23196         };
23197         // now add all the items...
23198         
23199
23200         if(!this.disable.alignments){
23201             tb.add(
23202                 '-',
23203                 btn('justifyleft'),
23204                 btn('justifycenter'),
23205                 btn('justifyright')
23206             );
23207         };
23208
23209         //if(!Roo.isSafari){
23210             if(!this.disable.links){
23211                 tb.add(
23212                     '-',
23213                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23214                 );
23215             };
23216
23217             if(!this.disable.lists){
23218                 tb.add(
23219                     '-',
23220                     btn('insertorderedlist'),
23221                     btn('insertunorderedlist')
23222                 );
23223             }
23224             if(!this.disable.sourceEdit){
23225                 tb.add(
23226                     '-',
23227                     btn('sourceedit', true, function(btn){
23228                         this.toggleSourceEdit(btn.pressed);
23229                     })
23230                 );
23231             }
23232         //}
23233         
23234         var smenu = { };
23235         // special menu.. - needs to be tidied up..
23236         if (!this.disable.special) {
23237             smenu = {
23238                 text: "&#169;",
23239                 cls: 'x-edit-none',
23240                 
23241                 menu : {
23242                     items : []
23243                 }
23244             };
23245             for (var i =0; i < this.specialChars.length; i++) {
23246                 smenu.menu.items.push({
23247                     
23248                     html: this.specialChars[i],
23249                     handler: function(a,b) {
23250                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23251                         //editor.insertAtCursor(a.html);
23252                         
23253                     },
23254                     tabIndex:-1
23255                 });
23256             }
23257             
23258             
23259             tb.add(smenu);
23260             
23261             
23262         }
23263         
23264         var cmenu = { };
23265         if (!this.disable.cleanStyles) {
23266             cmenu = {
23267                 cls: 'x-btn-icon x-btn-clear',
23268                 
23269                 menu : {
23270                     items : []
23271                 }
23272             };
23273             for (var i =0; i < this.cleanStyles.length; i++) {
23274                 cmenu.menu.items.push({
23275                     actiontype : this.cleanStyles[i],
23276                     html: 'Remove ' + this.cleanStyles[i],
23277                     handler: function(a,b) {
23278 //                        Roo.log(a);
23279 //                        Roo.log(b);
23280                         var c = Roo.get(editorcore.doc.body);
23281                         c.select('[style]').each(function(s) {
23282                             s.dom.style.removeProperty(a.actiontype);
23283                         });
23284                         editorcore.syncValue();
23285                     },
23286                     tabIndex:-1
23287                 });
23288             }
23289              cmenu.menu.items.push({
23290                 actiontype : 'tablewidths',
23291                 html: 'Remove Table Widths',
23292                 handler: function(a,b) {
23293                     editorcore.cleanTableWidths();
23294                     editorcore.syncValue();
23295                 },
23296                 tabIndex:-1
23297             });
23298             cmenu.menu.items.push({
23299                 actiontype : 'word',
23300                 html: 'Remove MS Word Formating',
23301                 handler: function(a,b) {
23302                     editorcore.cleanWord();
23303                     editorcore.syncValue();
23304                 },
23305                 tabIndex:-1
23306             });
23307             
23308             cmenu.menu.items.push({
23309                 actiontype : 'all',
23310                 html: 'Remove All Styles',
23311                 handler: function(a,b) {
23312                     
23313                     var c = Roo.get(editorcore.doc.body);
23314                     c.select('[style]').each(function(s) {
23315                         s.dom.removeAttribute('style');
23316                     });
23317                     editorcore.syncValue();
23318                 },
23319                 tabIndex:-1
23320             });
23321             
23322             cmenu.menu.items.push({
23323                 actiontype : 'all',
23324                 html: 'Remove All CSS Classes',
23325                 handler: function(a,b) {
23326                     
23327                     var c = Roo.get(editorcore.doc.body);
23328                     c.select('[class]').each(function(s) {
23329                         s.dom.removeAttribute('class');
23330                     });
23331                     editorcore.cleanWord();
23332                     editorcore.syncValue();
23333                 },
23334                 tabIndex:-1
23335             });
23336             
23337              cmenu.menu.items.push({
23338                 actiontype : 'tidy',
23339                 html: 'Tidy HTML Source',
23340                 handler: function(a,b) {
23341                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23342                     editorcore.syncValue();
23343                 },
23344                 tabIndex:-1
23345             });
23346             
23347             
23348             tb.add(cmenu);
23349         }
23350          
23351         if (!this.disable.specialElements) {
23352             var semenu = {
23353                 text: "Other;",
23354                 cls: 'x-edit-none',
23355                 menu : {
23356                     items : []
23357                 }
23358             };
23359             for (var i =0; i < this.specialElements.length; i++) {
23360                 semenu.menu.items.push(
23361                     Roo.apply({ 
23362                         handler: function(a,b) {
23363                             editor.insertAtCursor(this.ihtml);
23364                         }
23365                     }, this.specialElements[i])
23366                 );
23367                     
23368             }
23369             
23370             tb.add(semenu);
23371             
23372             
23373         }
23374          
23375         
23376         if (this.btns) {
23377             for(var i =0; i< this.btns.length;i++) {
23378                 var b = Roo.factory(this.btns[i],Roo.form);
23379                 b.cls =  'x-edit-none';
23380                 
23381                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23382                     b.cls += ' x-init-enable';
23383                 }
23384                 
23385                 b.scope = editorcore;
23386                 tb.add(b);
23387             }
23388         
23389         }
23390         
23391         
23392         
23393         // disable everything...
23394         
23395         this.tb.items.each(function(item){
23396             
23397            if(
23398                 item.id != editorcore.frameId+ '-sourceedit' && 
23399                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23400             ){
23401                 
23402                 item.disable();
23403             }
23404         });
23405         this.rendered = true;
23406         
23407         // the all the btns;
23408         editor.on('editorevent', this.updateToolbar, this);
23409         // other toolbars need to implement this..
23410         //editor.on('editmodechange', this.updateToolbar, this);
23411     },
23412     
23413     
23414     relayBtnCmd : function(btn) {
23415         this.editorcore.relayCmd(btn.cmd);
23416     },
23417     // private used internally
23418     createLink : function(){
23419         Roo.log("create link?");
23420         var url = prompt(this.createLinkText, this.defaultLinkValue);
23421         if(url && url != 'http:/'+'/'){
23422             this.editorcore.relayCmd('createlink', url);
23423         }
23424     },
23425
23426     
23427     /**
23428      * Protected method that will not generally be called directly. It triggers
23429      * a toolbar update by reading the markup state of the current selection in the editor.
23430      */
23431     updateToolbar: function(){
23432
23433         if(!this.editorcore.activated){
23434             this.editor.onFirstFocus();
23435             return;
23436         }
23437
23438         var btns = this.tb.items.map, 
23439             doc = this.editorcore.doc,
23440             frameId = this.editorcore.frameId;
23441
23442         if(!this.disable.font && !Roo.isSafari){
23443             /*
23444             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23445             if(name != this.fontSelect.dom.value){
23446                 this.fontSelect.dom.value = name;
23447             }
23448             */
23449         }
23450         if(!this.disable.format){
23451             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23452             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23453             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23454             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23455         }
23456         if(!this.disable.alignments){
23457             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23458             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23459             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23460         }
23461         if(!Roo.isSafari && !this.disable.lists){
23462             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23463             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23464         }
23465         
23466         var ans = this.editorcore.getAllAncestors();
23467         if (this.formatCombo) {
23468             
23469             
23470             var store = this.formatCombo.store;
23471             this.formatCombo.setValue("");
23472             for (var i =0; i < ans.length;i++) {
23473                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23474                     // select it..
23475                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23476                     break;
23477                 }
23478             }
23479         }
23480         
23481         
23482         
23483         // hides menus... - so this cant be on a menu...
23484         Roo.menu.MenuMgr.hideAll();
23485
23486         //this.editorsyncValue();
23487     },
23488    
23489     
23490     createFontOptions : function(){
23491         var buf = [], fs = this.fontFamilies, ff, lc;
23492         
23493         
23494         
23495         for(var i = 0, len = fs.length; i< len; i++){
23496             ff = fs[i];
23497             lc = ff.toLowerCase();
23498             buf.push(
23499                 '<option value="',lc,'" style="font-family:',ff,';"',
23500                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23501                     ff,
23502                 '</option>'
23503             );
23504         }
23505         return buf.join('');
23506     },
23507     
23508     toggleSourceEdit : function(sourceEditMode){
23509         
23510         Roo.log("toolbar toogle");
23511         if(sourceEditMode === undefined){
23512             sourceEditMode = !this.sourceEditMode;
23513         }
23514         this.sourceEditMode = sourceEditMode === true;
23515         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23516         // just toggle the button?
23517         if(btn.pressed !== this.sourceEditMode){
23518             btn.toggle(this.sourceEditMode);
23519             return;
23520         }
23521         
23522         if(sourceEditMode){
23523             Roo.log("disabling buttons");
23524             this.tb.items.each(function(item){
23525                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23526                     item.disable();
23527                 }
23528             });
23529           
23530         }else{
23531             Roo.log("enabling buttons");
23532             if(this.editorcore.initialized){
23533                 this.tb.items.each(function(item){
23534                     item.enable();
23535                 });
23536             }
23537             
23538         }
23539         Roo.log("calling toggole on editor");
23540         // tell the editor that it's been pressed..
23541         this.editor.toggleSourceEdit(sourceEditMode);
23542        
23543     },
23544      /**
23545      * Object collection of toolbar tooltips for the buttons in the editor. The key
23546      * is the command id associated with that button and the value is a valid QuickTips object.
23547      * For example:
23548 <pre><code>
23549 {
23550     bold : {
23551         title: 'Bold (Ctrl+B)',
23552         text: 'Make the selected text bold.',
23553         cls: 'x-html-editor-tip'
23554     },
23555     italic : {
23556         title: 'Italic (Ctrl+I)',
23557         text: 'Make the selected text italic.',
23558         cls: 'x-html-editor-tip'
23559     },
23560     ...
23561 </code></pre>
23562     * @type Object
23563      */
23564     buttonTips : {
23565         bold : {
23566             title: 'Bold (Ctrl+B)',
23567             text: 'Make the selected text bold.',
23568             cls: 'x-html-editor-tip'
23569         },
23570         italic : {
23571             title: 'Italic (Ctrl+I)',
23572             text: 'Make the selected text italic.',
23573             cls: 'x-html-editor-tip'
23574         },
23575         underline : {
23576             title: 'Underline (Ctrl+U)',
23577             text: 'Underline the selected text.',
23578             cls: 'x-html-editor-tip'
23579         },
23580         strikethrough : {
23581             title: 'Strikethrough',
23582             text: 'Strikethrough the selected text.',
23583             cls: 'x-html-editor-tip'
23584         },
23585         increasefontsize : {
23586             title: 'Grow Text',
23587             text: 'Increase the font size.',
23588             cls: 'x-html-editor-tip'
23589         },
23590         decreasefontsize : {
23591             title: 'Shrink Text',
23592             text: 'Decrease the font size.',
23593             cls: 'x-html-editor-tip'
23594         },
23595         backcolor : {
23596             title: 'Text Highlight Color',
23597             text: 'Change the background color of the selected text.',
23598             cls: 'x-html-editor-tip'
23599         },
23600         forecolor : {
23601             title: 'Font Color',
23602             text: 'Change the color of the selected text.',
23603             cls: 'x-html-editor-tip'
23604         },
23605         justifyleft : {
23606             title: 'Align Text Left',
23607             text: 'Align text to the left.',
23608             cls: 'x-html-editor-tip'
23609         },
23610         justifycenter : {
23611             title: 'Center Text',
23612             text: 'Center text in the editor.',
23613             cls: 'x-html-editor-tip'
23614         },
23615         justifyright : {
23616             title: 'Align Text Right',
23617             text: 'Align text to the right.',
23618             cls: 'x-html-editor-tip'
23619         },
23620         insertunorderedlist : {
23621             title: 'Bullet List',
23622             text: 'Start a bulleted list.',
23623             cls: 'x-html-editor-tip'
23624         },
23625         insertorderedlist : {
23626             title: 'Numbered List',
23627             text: 'Start a numbered list.',
23628             cls: 'x-html-editor-tip'
23629         },
23630         createlink : {
23631             title: 'Hyperlink',
23632             text: 'Make the selected text a hyperlink.',
23633             cls: 'x-html-editor-tip'
23634         },
23635         sourceedit : {
23636             title: 'Source Edit',
23637             text: 'Switch to source editing mode.',
23638             cls: 'x-html-editor-tip'
23639         }
23640     },
23641     // private
23642     onDestroy : function(){
23643         if(this.rendered){
23644             
23645             this.tb.items.each(function(item){
23646                 if(item.menu){
23647                     item.menu.removeAll();
23648                     if(item.menu.el){
23649                         item.menu.el.destroy();
23650                     }
23651                 }
23652                 item.destroy();
23653             });
23654              
23655         }
23656     },
23657     onFirstFocus: function() {
23658         this.tb.items.each(function(item){
23659            item.enable();
23660         });
23661     }
23662 });
23663
23664
23665
23666
23667 // <script type="text/javascript">
23668 /*
23669  * Based on
23670  * Ext JS Library 1.1.1
23671  * Copyright(c) 2006-2007, Ext JS, LLC.
23672  *  
23673  
23674  */
23675
23676  
23677 /**
23678  * @class Roo.form.HtmlEditor.ToolbarContext
23679  * Context Toolbar
23680  * 
23681  * Usage:
23682  *
23683  new Roo.form.HtmlEditor({
23684     ....
23685     toolbars : [
23686         { xtype: 'ToolbarStandard', styles : {} }
23687         { xtype: 'ToolbarContext', disable : {} }
23688     ]
23689 })
23690
23691      
23692  * 
23693  * @config : {Object} disable List of elements to disable.. (not done yet.)
23694  * @config : {Object} styles  Map of styles available.
23695  * 
23696  */
23697
23698 Roo.form.HtmlEditor.ToolbarContext = function(config)
23699 {
23700     
23701     Roo.apply(this, config);
23702     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23703     // dont call parent... till later.
23704     this.styles = this.styles || {};
23705 }
23706
23707  
23708
23709 Roo.form.HtmlEditor.ToolbarContext.types = {
23710     'IMG' : {
23711         width : {
23712             title: "Width",
23713             width: 40
23714         },
23715         height:  {
23716             title: "Height",
23717             width: 40
23718         },
23719         align: {
23720             title: "Align",
23721             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23722             width : 80
23723             
23724         },
23725         border: {
23726             title: "Border",
23727             width: 40
23728         },
23729         alt: {
23730             title: "Alt",
23731             width: 120
23732         },
23733         src : {
23734             title: "Src",
23735             width: 220
23736         }
23737         
23738     },
23739     'A' : {
23740         name : {
23741             title: "Name",
23742             width: 50
23743         },
23744         target:  {
23745             title: "Target",
23746             width: 120
23747         },
23748         href:  {
23749             title: "Href",
23750             width: 220
23751         } // border?
23752         
23753     },
23754     'TABLE' : {
23755         rows : {
23756             title: "Rows",
23757             width: 20
23758         },
23759         cols : {
23760             title: "Cols",
23761             width: 20
23762         },
23763         width : {
23764             title: "Width",
23765             width: 40
23766         },
23767         height : {
23768             title: "Height",
23769             width: 40
23770         },
23771         border : {
23772             title: "Border",
23773             width: 20
23774         }
23775     },
23776     'TD' : {
23777         width : {
23778             title: "Width",
23779             width: 40
23780         },
23781         height : {
23782             title: "Height",
23783             width: 40
23784         },   
23785         align: {
23786             title: "Align",
23787             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23788             width: 80
23789         },
23790         valign: {
23791             title: "Valign",
23792             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23793             width: 80
23794         },
23795         colspan: {
23796             title: "Colspan",
23797             width: 20
23798             
23799         },
23800          'font-family'  : {
23801             title : "Font",
23802             style : 'fontFamily',
23803             displayField: 'display',
23804             optname : 'font-family',
23805             width: 140
23806         }
23807     },
23808     'INPUT' : {
23809         name : {
23810             title: "name",
23811             width: 120
23812         },
23813         value : {
23814             title: "Value",
23815             width: 120
23816         },
23817         width : {
23818             title: "Width",
23819             width: 40
23820         }
23821     },
23822     'LABEL' : {
23823         'for' : {
23824             title: "For",
23825             width: 120
23826         }
23827     },
23828     'TEXTAREA' : {
23829           name : {
23830             title: "name",
23831             width: 120
23832         },
23833         rows : {
23834             title: "Rows",
23835             width: 20
23836         },
23837         cols : {
23838             title: "Cols",
23839             width: 20
23840         }
23841     },
23842     'SELECT' : {
23843         name : {
23844             title: "name",
23845             width: 120
23846         },
23847         selectoptions : {
23848             title: "Options",
23849             width: 200
23850         }
23851     },
23852     
23853     // should we really allow this??
23854     // should this just be 
23855     'BODY' : {
23856         title : {
23857             title: "Title",
23858             width: 200,
23859             disabled : true
23860         }
23861     },
23862     'SPAN' : {
23863         'font-family'  : {
23864             title : "Font",
23865             style : 'fontFamily',
23866             displayField: 'display',
23867             optname : 'font-family',
23868             width: 140
23869         }
23870     },
23871     'DIV' : {
23872         'font-family'  : {
23873             title : "Font",
23874             style : 'fontFamily',
23875             displayField: 'display',
23876             optname : 'font-family',
23877             width: 140
23878         }
23879     },
23880      'P' : {
23881         'font-family'  : {
23882             title : "Font",
23883             style : 'fontFamily',
23884             displayField: 'display',
23885             optname : 'font-family',
23886             width: 140
23887         }
23888     },
23889     
23890     '*' : {
23891         // empty..
23892     }
23893
23894 };
23895
23896 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23897 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23898
23899 Roo.form.HtmlEditor.ToolbarContext.options = {
23900         'font-family'  : [ 
23901                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23902                 [ 'Courier New', 'Courier New'],
23903                 [ 'Tahoma', 'Tahoma'],
23904                 [ 'Times New Roman,serif', 'Times'],
23905                 [ 'Verdana','Verdana' ]
23906         ]
23907 };
23908
23909 // fixme - these need to be configurable..
23910  
23911
23912 //Roo.form.HtmlEditor.ToolbarContext.types
23913
23914
23915 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23916     
23917     tb: false,
23918     
23919     rendered: false,
23920     
23921     editor : false,
23922     editorcore : false,
23923     /**
23924      * @cfg {Object} disable  List of toolbar elements to disable
23925          
23926      */
23927     disable : false,
23928     /**
23929      * @cfg {Object} styles List of styles 
23930      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23931      *
23932      * These must be defined in the page, so they get rendered correctly..
23933      * .headline { }
23934      * TD.underline { }
23935      * 
23936      */
23937     styles : false,
23938     
23939     options: false,
23940     
23941     toolbars : false,
23942     
23943     init : function(editor)
23944     {
23945         this.editor = editor;
23946         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23947         var editorcore = this.editorcore;
23948         
23949         var fid = editorcore.frameId;
23950         var etb = this;
23951         function btn(id, toggle, handler){
23952             var xid = fid + '-'+ id ;
23953             return {
23954                 id : xid,
23955                 cmd : id,
23956                 cls : 'x-btn-icon x-edit-'+id,
23957                 enableToggle:toggle !== false,
23958                 scope: editorcore, // was editor...
23959                 handler:handler||editorcore.relayBtnCmd,
23960                 clickEvent:'mousedown',
23961                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23962                 tabIndex:-1
23963             };
23964         }
23965         // create a new element.
23966         var wdiv = editor.wrap.createChild({
23967                 tag: 'div'
23968             }, editor.wrap.dom.firstChild.nextSibling, true);
23969         
23970         // can we do this more than once??
23971         
23972          // stop form submits
23973       
23974  
23975         // disable everything...
23976         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23977         this.toolbars = {};
23978            
23979         for (var i in  ty) {
23980           
23981             this.toolbars[i] = this.buildToolbar(ty[i],i);
23982         }
23983         this.tb = this.toolbars.BODY;
23984         this.tb.el.show();
23985         this.buildFooter();
23986         this.footer.show();
23987         editor.on('hide', function( ) { this.footer.hide() }, this);
23988         editor.on('show', function( ) { this.footer.show() }, this);
23989         
23990          
23991         this.rendered = true;
23992         
23993         // the all the btns;
23994         editor.on('editorevent', this.updateToolbar, this);
23995         // other toolbars need to implement this..
23996         //editor.on('editmodechange', this.updateToolbar, this);
23997     },
23998     
23999     
24000     
24001     /**
24002      * Protected method that will not generally be called directly. It triggers
24003      * a toolbar update by reading the markup state of the current selection in the editor.
24004      *
24005      * Note you can force an update by calling on('editorevent', scope, false)
24006      */
24007     updateToolbar: function(editor,ev,sel){
24008
24009         //Roo.log(ev);
24010         // capture mouse up - this is handy for selecting images..
24011         // perhaps should go somewhere else...
24012         if(!this.editorcore.activated){
24013              this.editor.onFirstFocus();
24014             return;
24015         }
24016         
24017         
24018         
24019         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24020         // selectNode - might want to handle IE?
24021         if (ev &&
24022             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24023             ev.target && ev.target.tagName == 'IMG') {
24024             // they have click on an image...
24025             // let's see if we can change the selection...
24026             sel = ev.target;
24027          
24028               var nodeRange = sel.ownerDocument.createRange();
24029             try {
24030                 nodeRange.selectNode(sel);
24031             } catch (e) {
24032                 nodeRange.selectNodeContents(sel);
24033             }
24034             //nodeRange.collapse(true);
24035             var s = this.editorcore.win.getSelection();
24036             s.removeAllRanges();
24037             s.addRange(nodeRange);
24038         }  
24039         
24040       
24041         var updateFooter = sel ? false : true;
24042         
24043         
24044         var ans = this.editorcore.getAllAncestors();
24045         
24046         // pick
24047         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24048         
24049         if (!sel) { 
24050             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24051             sel = sel ? sel : this.editorcore.doc.body;
24052             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24053             
24054         }
24055         // pick a menu that exists..
24056         var tn = sel.tagName.toUpperCase();
24057         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24058         
24059         tn = sel.tagName.toUpperCase();
24060         
24061         var lastSel = this.tb.selectedNode;
24062         
24063         this.tb.selectedNode = sel;
24064         
24065         // if current menu does not match..
24066         
24067         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24068                 
24069             this.tb.el.hide();
24070             ///console.log("show: " + tn);
24071             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24072             this.tb.el.show();
24073             // update name
24074             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24075             
24076             
24077             // update attributes
24078             if (this.tb.fields) {
24079                 this.tb.fields.each(function(e) {
24080                     if (e.stylename) {
24081                         e.setValue(sel.style[e.stylename]);
24082                         return;
24083                     } 
24084                    e.setValue(sel.getAttribute(e.attrname));
24085                 });
24086             }
24087             
24088             var hasStyles = false;
24089             for(var i in this.styles) {
24090                 hasStyles = true;
24091                 break;
24092             }
24093             
24094             // update styles
24095             if (hasStyles) { 
24096                 var st = this.tb.fields.item(0);
24097                 
24098                 st.store.removeAll();
24099                
24100                 
24101                 var cn = sel.className.split(/\s+/);
24102                 
24103                 var avs = [];
24104                 if (this.styles['*']) {
24105                     
24106                     Roo.each(this.styles['*'], function(v) {
24107                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24108                     });
24109                 }
24110                 if (this.styles[tn]) { 
24111                     Roo.each(this.styles[tn], function(v) {
24112                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24113                     });
24114                 }
24115                 
24116                 st.store.loadData(avs);
24117                 st.collapse();
24118                 st.setValue(cn);
24119             }
24120             // flag our selected Node.
24121             this.tb.selectedNode = sel;
24122            
24123            
24124             Roo.menu.MenuMgr.hideAll();
24125
24126         }
24127         
24128         if (!updateFooter) {
24129             //this.footDisp.dom.innerHTML = ''; 
24130             return;
24131         }
24132         // update the footer
24133         //
24134         var html = '';
24135         
24136         this.footerEls = ans.reverse();
24137         Roo.each(this.footerEls, function(a,i) {
24138             if (!a) { return; }
24139             html += html.length ? ' &gt; '  :  '';
24140             
24141             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24142             
24143         });
24144        
24145         // 
24146         var sz = this.footDisp.up('td').getSize();
24147         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24148         this.footDisp.dom.style.marginLeft = '5px';
24149         
24150         this.footDisp.dom.style.overflow = 'hidden';
24151         
24152         this.footDisp.dom.innerHTML = html;
24153             
24154         //this.editorsyncValue();
24155     },
24156      
24157     
24158    
24159        
24160     // private
24161     onDestroy : function(){
24162         if(this.rendered){
24163             
24164             this.tb.items.each(function(item){
24165                 if(item.menu){
24166                     item.menu.removeAll();
24167                     if(item.menu.el){
24168                         item.menu.el.destroy();
24169                     }
24170                 }
24171                 item.destroy();
24172             });
24173              
24174         }
24175     },
24176     onFirstFocus: function() {
24177         // need to do this for all the toolbars..
24178         this.tb.items.each(function(item){
24179            item.enable();
24180         });
24181     },
24182     buildToolbar: function(tlist, nm)
24183     {
24184         var editor = this.editor;
24185         var editorcore = this.editorcore;
24186          // create a new element.
24187         var wdiv = editor.wrap.createChild({
24188                 tag: 'div'
24189             }, editor.wrap.dom.firstChild.nextSibling, true);
24190         
24191        
24192         var tb = new Roo.Toolbar(wdiv);
24193         // add the name..
24194         
24195         tb.add(nm+ ":&nbsp;");
24196         
24197         var styles = [];
24198         for(var i in this.styles) {
24199             styles.push(i);
24200         }
24201         
24202         // styles...
24203         if (styles && styles.length) {
24204             
24205             // this needs a multi-select checkbox...
24206             tb.addField( new Roo.form.ComboBox({
24207                 store: new Roo.data.SimpleStore({
24208                     id : 'val',
24209                     fields: ['val', 'selected'],
24210                     data : [] 
24211                 }),
24212                 name : '-roo-edit-className',
24213                 attrname : 'className',
24214                 displayField: 'val',
24215                 typeAhead: false,
24216                 mode: 'local',
24217                 editable : false,
24218                 triggerAction: 'all',
24219                 emptyText:'Select Style',
24220                 selectOnFocus:true,
24221                 width: 130,
24222                 listeners : {
24223                     'select': function(c, r, i) {
24224                         // initial support only for on class per el..
24225                         tb.selectedNode.className =  r ? r.get('val') : '';
24226                         editorcore.syncValue();
24227                     }
24228                 }
24229     
24230             }));
24231         }
24232         
24233         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24234         var tbops = tbc.options;
24235         
24236         for (var i in tlist) {
24237             
24238             var item = tlist[i];
24239             tb.add(item.title + ":&nbsp;");
24240             
24241             
24242             //optname == used so you can configure the options available..
24243             var opts = item.opts ? item.opts : false;
24244             if (item.optname) {
24245                 opts = tbops[item.optname];
24246            
24247             }
24248             
24249             if (opts) {
24250                 // opts == pulldown..
24251                 tb.addField( new Roo.form.ComboBox({
24252                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24253                         id : 'val',
24254                         fields: ['val', 'display'],
24255                         data : opts  
24256                     }),
24257                     name : '-roo-edit-' + i,
24258                     attrname : i,
24259                     stylename : item.style ? item.style : false,
24260                     displayField: item.displayField ? item.displayField : 'val',
24261                     valueField :  'val',
24262                     typeAhead: false,
24263                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24264                     editable : false,
24265                     triggerAction: 'all',
24266                     emptyText:'Select',
24267                     selectOnFocus:true,
24268                     width: item.width ? item.width  : 130,
24269                     listeners : {
24270                         'select': function(c, r, i) {
24271                             if (c.stylename) {
24272                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24273                                 return;
24274                             }
24275                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24276                         }
24277                     }
24278
24279                 }));
24280                 continue;
24281                     
24282                  
24283                 
24284                 tb.addField( new Roo.form.TextField({
24285                     name: i,
24286                     width: 100,
24287                     //allowBlank:false,
24288                     value: ''
24289                 }));
24290                 continue;
24291             }
24292             tb.addField( new Roo.form.TextField({
24293                 name: '-roo-edit-' + i,
24294                 attrname : i,
24295                 
24296                 width: item.width,
24297                 //allowBlank:true,
24298                 value: '',
24299                 listeners: {
24300                     'change' : function(f, nv, ov) {
24301                         tb.selectedNode.setAttribute(f.attrname, nv);
24302                         editorcore.syncValue();
24303                     }
24304                 }
24305             }));
24306              
24307         }
24308         
24309         var _this = this;
24310         
24311         if(nm == 'BODY'){
24312             tb.addSeparator();
24313         
24314             tb.addButton( {
24315                 text: 'Stylesheets',
24316
24317                 listeners : {
24318                     click : function ()
24319                     {
24320                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24321                     }
24322                 }
24323             });
24324         }
24325         
24326         tb.addFill();
24327         tb.addButton( {
24328             text: 'Remove Tag',
24329     
24330             listeners : {
24331                 click : function ()
24332                 {
24333                     // remove
24334                     // undo does not work.
24335                      
24336                     var sn = tb.selectedNode;
24337                     
24338                     var pn = sn.parentNode;
24339                     
24340                     var stn =  sn.childNodes[0];
24341                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24342                     while (sn.childNodes.length) {
24343                         var node = sn.childNodes[0];
24344                         sn.removeChild(node);
24345                         //Roo.log(node);
24346                         pn.insertBefore(node, sn);
24347                         
24348                     }
24349                     pn.removeChild(sn);
24350                     var range = editorcore.createRange();
24351         
24352                     range.setStart(stn,0);
24353                     range.setEnd(en,0); //????
24354                     //range.selectNode(sel);
24355                     
24356                     
24357                     var selection = editorcore.getSelection();
24358                     selection.removeAllRanges();
24359                     selection.addRange(range);
24360                     
24361                     
24362                     
24363                     //_this.updateToolbar(null, null, pn);
24364                     _this.updateToolbar(null, null, null);
24365                     _this.footDisp.dom.innerHTML = ''; 
24366                 }
24367             }
24368             
24369                     
24370                 
24371             
24372         });
24373         
24374         
24375         tb.el.on('click', function(e){
24376             e.preventDefault(); // what does this do?
24377         });
24378         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24379         tb.el.hide();
24380         tb.name = nm;
24381         // dont need to disable them... as they will get hidden
24382         return tb;
24383          
24384         
24385     },
24386     buildFooter : function()
24387     {
24388         
24389         var fel = this.editor.wrap.createChild();
24390         this.footer = new Roo.Toolbar(fel);
24391         // toolbar has scrolly on left / right?
24392         var footDisp= new Roo.Toolbar.Fill();
24393         var _t = this;
24394         this.footer.add(
24395             {
24396                 text : '&lt;',
24397                 xtype: 'Button',
24398                 handler : function() {
24399                     _t.footDisp.scrollTo('left',0,true)
24400                 }
24401             }
24402         );
24403         this.footer.add( footDisp );
24404         this.footer.add( 
24405             {
24406                 text : '&gt;',
24407                 xtype: 'Button',
24408                 handler : function() {
24409                     // no animation..
24410                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24411                 }
24412             }
24413         );
24414         var fel = Roo.get(footDisp.el);
24415         fel.addClass('x-editor-context');
24416         this.footDispWrap = fel; 
24417         this.footDispWrap.overflow  = 'hidden';
24418         
24419         this.footDisp = fel.createChild();
24420         this.footDispWrap.on('click', this.onContextClick, this)
24421         
24422         
24423     },
24424     onContextClick : function (ev,dom)
24425     {
24426         ev.preventDefault();
24427         var  cn = dom.className;
24428         //Roo.log(cn);
24429         if (!cn.match(/x-ed-loc-/)) {
24430             return;
24431         }
24432         var n = cn.split('-').pop();
24433         var ans = this.footerEls;
24434         var sel = ans[n];
24435         
24436          // pick
24437         var range = this.editorcore.createRange();
24438         
24439         range.selectNodeContents(sel);
24440         //range.selectNode(sel);
24441         
24442         
24443         var selection = this.editorcore.getSelection();
24444         selection.removeAllRanges();
24445         selection.addRange(range);
24446         
24447         
24448         
24449         this.updateToolbar(null, null, sel);
24450         
24451         
24452     }
24453     
24454     
24455     
24456     
24457     
24458 });
24459
24460
24461
24462
24463
24464 /*
24465  * Based on:
24466  * Ext JS Library 1.1.1
24467  * Copyright(c) 2006-2007, Ext JS, LLC.
24468  *
24469  * Originally Released Under LGPL - original licence link has changed is not relivant.
24470  *
24471  * Fork - LGPL
24472  * <script type="text/javascript">
24473  */
24474  
24475 /**
24476  * @class Roo.form.BasicForm
24477  * @extends Roo.util.Observable
24478  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24479  * @constructor
24480  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24481  * @param {Object} config Configuration options
24482  */
24483 Roo.form.BasicForm = function(el, config){
24484     this.allItems = [];
24485     this.childForms = [];
24486     Roo.apply(this, config);
24487     /*
24488      * The Roo.form.Field items in this form.
24489      * @type MixedCollection
24490      */
24491      
24492      
24493     this.items = new Roo.util.MixedCollection(false, function(o){
24494         return o.id || (o.id = Roo.id());
24495     });
24496     this.addEvents({
24497         /**
24498          * @event beforeaction
24499          * Fires before any action is performed. Return false to cancel the action.
24500          * @param {Form} this
24501          * @param {Action} action The action to be performed
24502          */
24503         beforeaction: true,
24504         /**
24505          * @event actionfailed
24506          * Fires when an action fails.
24507          * @param {Form} this
24508          * @param {Action} action The action that failed
24509          */
24510         actionfailed : true,
24511         /**
24512          * @event actioncomplete
24513          * Fires when an action is completed.
24514          * @param {Form} this
24515          * @param {Action} action The action that completed
24516          */
24517         actioncomplete : true
24518     });
24519     if(el){
24520         this.initEl(el);
24521     }
24522     Roo.form.BasicForm.superclass.constructor.call(this);
24523     
24524     Roo.form.BasicForm.popover.apply();
24525 };
24526
24527 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24528     /**
24529      * @cfg {String} method
24530      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24531      */
24532     /**
24533      * @cfg {DataReader} reader
24534      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24535      * This is optional as there is built-in support for processing JSON.
24536      */
24537     /**
24538      * @cfg {DataReader} errorReader
24539      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24540      * This is completely optional as there is built-in support for processing JSON.
24541      */
24542     /**
24543      * @cfg {String} url
24544      * The URL to use for form actions if one isn't supplied in the action options.
24545      */
24546     /**
24547      * @cfg {Boolean} fileUpload
24548      * Set to true if this form is a file upload.
24549      */
24550      
24551     /**
24552      * @cfg {Object} baseParams
24553      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24554      */
24555      /**
24556      
24557     /**
24558      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24559      */
24560     timeout: 30,
24561
24562     // private
24563     activeAction : null,
24564
24565     /**
24566      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24567      * or setValues() data instead of when the form was first created.
24568      */
24569     trackResetOnLoad : false,
24570     
24571     
24572     /**
24573      * childForms - used for multi-tab forms
24574      * @type {Array}
24575      */
24576     childForms : false,
24577     
24578     /**
24579      * allItems - full list of fields.
24580      * @type {Array}
24581      */
24582     allItems : false,
24583     
24584     /**
24585      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24586      * element by passing it or its id or mask the form itself by passing in true.
24587      * @type Mixed
24588      */
24589     waitMsgTarget : false,
24590     
24591     /**
24592      * @type Boolean
24593      */
24594     disableMask : false,
24595     
24596     /**
24597      * @cfg {Boolean} errorMask (true|false) default false
24598      */
24599     errorMask : false,
24600     
24601     /**
24602      * @cfg {Number} maskOffset Default 100
24603      */
24604     maskOffset : 100,
24605
24606     // private
24607     initEl : function(el){
24608         this.el = Roo.get(el);
24609         this.id = this.el.id || Roo.id();
24610         this.el.on('submit', this.onSubmit, this);
24611         this.el.addClass('x-form');
24612     },
24613
24614     // private
24615     onSubmit : function(e){
24616         e.stopEvent();
24617     },
24618
24619     /**
24620      * Returns true if client-side validation on the form is successful.
24621      * @return Boolean
24622      */
24623     isValid : function(){
24624         var valid = true;
24625         var target = false;
24626         this.items.each(function(f){
24627             if(f.validate()){
24628                 return;
24629             }
24630             
24631             valid = false;
24632                 
24633             if(!target && f.el.isVisible(true)){
24634                 target = f;
24635             }
24636         });
24637         
24638         if(this.errorMask && !valid){
24639             Roo.form.BasicForm.popover.mask(this, target);
24640         }
24641         
24642         return valid;
24643     },
24644     /**
24645      * Returns array of invalid form fields.
24646      * @return Array
24647      */
24648     
24649     invalidFields : function()
24650     {
24651         var ret = [];
24652         this.items.each(function(f){
24653             if(f.validate()){
24654                 return;
24655             }
24656             ret.push(f);
24657             
24658         });
24659         
24660         return ret;
24661     },
24662     
24663     
24664     /**
24665      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24666      * @return Boolean
24667      */
24668     isDirty : function(){
24669         var dirty = false;
24670         this.items.each(function(f){
24671            if(f.isDirty()){
24672                dirty = true;
24673                return false;
24674            }
24675         });
24676         return dirty;
24677     },
24678     
24679     /**
24680      * Returns true if any fields in this form have changed since their original load. (New version)
24681      * @return Boolean
24682      */
24683     
24684     hasChanged : function()
24685     {
24686         var dirty = false;
24687         this.items.each(function(f){
24688            if(f.hasChanged()){
24689                dirty = true;
24690                return false;
24691            }
24692         });
24693         return dirty;
24694         
24695     },
24696     /**
24697      * Resets all hasChanged to 'false' -
24698      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24699      * So hasChanged storage is only to be used for this purpose
24700      * @return Boolean
24701      */
24702     resetHasChanged : function()
24703     {
24704         this.items.each(function(f){
24705            f.resetHasChanged();
24706         });
24707         
24708     },
24709     
24710     
24711     /**
24712      * Performs a predefined action (submit or load) or custom actions you define on this form.
24713      * @param {String} actionName The name of the action type
24714      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24715      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24716      * accept other config options):
24717      * <pre>
24718 Property          Type             Description
24719 ----------------  ---------------  ----------------------------------------------------------------------------------
24720 url               String           The url for the action (defaults to the form's url)
24721 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24722 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24723 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24724                                    validate the form on the client (defaults to false)
24725      * </pre>
24726      * @return {BasicForm} this
24727      */
24728     doAction : function(action, options){
24729         if(typeof action == 'string'){
24730             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24731         }
24732         if(this.fireEvent('beforeaction', this, action) !== false){
24733             this.beforeAction(action);
24734             action.run.defer(100, action);
24735         }
24736         return this;
24737     },
24738
24739     /**
24740      * Shortcut to do a submit action.
24741      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24742      * @return {BasicForm} this
24743      */
24744     submit : function(options){
24745         this.doAction('submit', options);
24746         return this;
24747     },
24748
24749     /**
24750      * Shortcut to do a load action.
24751      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24752      * @return {BasicForm} this
24753      */
24754     load : function(options){
24755         this.doAction('load', options);
24756         return this;
24757     },
24758
24759     /**
24760      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24761      * @param {Record} record The record to edit
24762      * @return {BasicForm} this
24763      */
24764     updateRecord : function(record){
24765         record.beginEdit();
24766         var fs = record.fields;
24767         fs.each(function(f){
24768             var field = this.findField(f.name);
24769             if(field){
24770                 record.set(f.name, field.getValue());
24771             }
24772         }, this);
24773         record.endEdit();
24774         return this;
24775     },
24776
24777     /**
24778      * Loads an Roo.data.Record into this form.
24779      * @param {Record} record The record to load
24780      * @return {BasicForm} this
24781      */
24782     loadRecord : function(record){
24783         this.setValues(record.data);
24784         return this;
24785     },
24786
24787     // private
24788     beforeAction : function(action){
24789         var o = action.options;
24790         
24791         if(!this.disableMask) {
24792             if(this.waitMsgTarget === true){
24793                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24794             }else if(this.waitMsgTarget){
24795                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24796                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24797             }else {
24798                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24799             }
24800         }
24801         
24802          
24803     },
24804
24805     // private
24806     afterAction : function(action, success){
24807         this.activeAction = null;
24808         var o = action.options;
24809         
24810         if(!this.disableMask) {
24811             if(this.waitMsgTarget === true){
24812                 this.el.unmask();
24813             }else if(this.waitMsgTarget){
24814                 this.waitMsgTarget.unmask();
24815             }else{
24816                 Roo.MessageBox.updateProgress(1);
24817                 Roo.MessageBox.hide();
24818             }
24819         }
24820         
24821         if(success){
24822             if(o.reset){
24823                 this.reset();
24824             }
24825             Roo.callback(o.success, o.scope, [this, action]);
24826             this.fireEvent('actioncomplete', this, action);
24827             
24828         }else{
24829             
24830             // failure condition..
24831             // we have a scenario where updates need confirming.
24832             // eg. if a locking scenario exists..
24833             // we look for { errors : { needs_confirm : true }} in the response.
24834             if (
24835                 (typeof(action.result) != 'undefined')  &&
24836                 (typeof(action.result.errors) != 'undefined')  &&
24837                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24838            ){
24839                 var _t = this;
24840                 Roo.MessageBox.confirm(
24841                     "Change requires confirmation",
24842                     action.result.errorMsg,
24843                     function(r) {
24844                         if (r != 'yes') {
24845                             return;
24846                         }
24847                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24848                     }
24849                     
24850                 );
24851                 
24852                 
24853                 
24854                 return;
24855             }
24856             
24857             Roo.callback(o.failure, o.scope, [this, action]);
24858             // show an error message if no failed handler is set..
24859             if (!this.hasListener('actionfailed')) {
24860                 Roo.MessageBox.alert("Error",
24861                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24862                         action.result.errorMsg :
24863                         "Saving Failed, please check your entries or try again"
24864                 );
24865             }
24866             
24867             this.fireEvent('actionfailed', this, action);
24868         }
24869         
24870     },
24871
24872     /**
24873      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24874      * @param {String} id The value to search for
24875      * @return Field
24876      */
24877     findField : function(id){
24878         var field = this.items.get(id);
24879         if(!field){
24880             this.items.each(function(f){
24881                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24882                     field = f;
24883                     return false;
24884                 }
24885             });
24886         }
24887         return field || null;
24888     },
24889
24890     /**
24891      * Add a secondary form to this one, 
24892      * Used to provide tabbed forms. One form is primary, with hidden values 
24893      * which mirror the elements from the other forms.
24894      * 
24895      * @param {Roo.form.Form} form to add.
24896      * 
24897      */
24898     addForm : function(form)
24899     {
24900        
24901         if (this.childForms.indexOf(form) > -1) {
24902             // already added..
24903             return;
24904         }
24905         this.childForms.push(form);
24906         var n = '';
24907         Roo.each(form.allItems, function (fe) {
24908             
24909             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24910             if (this.findField(n)) { // already added..
24911                 return;
24912             }
24913             var add = new Roo.form.Hidden({
24914                 name : n
24915             });
24916             add.render(this.el);
24917             
24918             this.add( add );
24919         }, this);
24920         
24921     },
24922     /**
24923      * Mark fields in this form invalid in bulk.
24924      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24925      * @return {BasicForm} this
24926      */
24927     markInvalid : function(errors){
24928         if(errors instanceof Array){
24929             for(var i = 0, len = errors.length; i < len; i++){
24930                 var fieldError = errors[i];
24931                 var f = this.findField(fieldError.id);
24932                 if(f){
24933                     f.markInvalid(fieldError.msg);
24934                 }
24935             }
24936         }else{
24937             var field, id;
24938             for(id in errors){
24939                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24940                     field.markInvalid(errors[id]);
24941                 }
24942             }
24943         }
24944         Roo.each(this.childForms || [], function (f) {
24945             f.markInvalid(errors);
24946         });
24947         
24948         return this;
24949     },
24950
24951     /**
24952      * Set values for fields in this form in bulk.
24953      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24954      * @return {BasicForm} this
24955      */
24956     setValues : function(values){
24957         if(values instanceof Array){ // array of objects
24958             for(var i = 0, len = values.length; i < len; i++){
24959                 var v = values[i];
24960                 var f = this.findField(v.id);
24961                 if(f){
24962                     f.setValue(v.value);
24963                     if(this.trackResetOnLoad){
24964                         f.originalValue = f.getValue();
24965                     }
24966                 }
24967             }
24968         }else{ // object hash
24969             var field, id;
24970             for(id in values){
24971                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24972                     
24973                     if (field.setFromData && 
24974                         field.valueField && 
24975                         field.displayField &&
24976                         // combos' with local stores can 
24977                         // be queried via setValue()
24978                         // to set their value..
24979                         (field.store && !field.store.isLocal)
24980                         ) {
24981                         // it's a combo
24982                         var sd = { };
24983                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24984                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24985                         field.setFromData(sd);
24986                         
24987                     } else {
24988                         field.setValue(values[id]);
24989                     }
24990                     
24991                     
24992                     if(this.trackResetOnLoad){
24993                         field.originalValue = field.getValue();
24994                     }
24995                 }
24996             }
24997         }
24998         this.resetHasChanged();
24999         
25000         
25001         Roo.each(this.childForms || [], function (f) {
25002             f.setValues(values);
25003             f.resetHasChanged();
25004         });
25005                 
25006         return this;
25007     },
25008  
25009     /**
25010      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25011      * they are returned as an array.
25012      * @param {Boolean} asString
25013      * @return {Object}
25014      */
25015     getValues : function(asString){
25016         if (this.childForms) {
25017             // copy values from the child forms
25018             Roo.each(this.childForms, function (f) {
25019                 this.setValues(f.getValues());
25020             }, this);
25021         }
25022         
25023         // use formdata
25024         if (typeof(FormData) != 'undefined' && asString !== true) {
25025             // this relies on a 'recent' version of chrome apparently...
25026             try {
25027                 var fd = (new FormData(this.el.dom)).entries();
25028                 var ret = {};
25029                 var ent = fd.next();
25030                 while (!ent.done) {
25031                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25032                     ent = fd.next();
25033                 };
25034                 return ret;
25035             } catch(e) {
25036                 
25037             }
25038             
25039         }
25040         
25041         
25042         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25043         if(asString === true){
25044             return fs;
25045         }
25046         return Roo.urlDecode(fs);
25047     },
25048     
25049     /**
25050      * Returns the fields in this form as an object with key/value pairs. 
25051      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25052      * @return {Object}
25053      */
25054     getFieldValues : function(with_hidden)
25055     {
25056         if (this.childForms) {
25057             // copy values from the child forms
25058             // should this call getFieldValues - probably not as we do not currently copy
25059             // hidden fields when we generate..
25060             Roo.each(this.childForms, function (f) {
25061                 this.setValues(f.getValues());
25062             }, this);
25063         }
25064         
25065         var ret = {};
25066         this.items.each(function(f){
25067             if (!f.getName()) {
25068                 return;
25069             }
25070             var v = f.getValue();
25071             if (f.inputType =='radio') {
25072                 if (typeof(ret[f.getName()]) == 'undefined') {
25073                     ret[f.getName()] = ''; // empty..
25074                 }
25075                 
25076                 if (!f.el.dom.checked) {
25077                     return;
25078                     
25079                 }
25080                 v = f.el.dom.value;
25081                 
25082             }
25083             
25084             // not sure if this supported any more..
25085             if ((typeof(v) == 'object') && f.getRawValue) {
25086                 v = f.getRawValue() ; // dates..
25087             }
25088             // combo boxes where name != hiddenName...
25089             if (f.name != f.getName()) {
25090                 ret[f.name] = f.getRawValue();
25091             }
25092             ret[f.getName()] = v;
25093         });
25094         
25095         return ret;
25096     },
25097
25098     /**
25099      * Clears all invalid messages in this form.
25100      * @return {BasicForm} this
25101      */
25102     clearInvalid : function(){
25103         this.items.each(function(f){
25104            f.clearInvalid();
25105         });
25106         
25107         Roo.each(this.childForms || [], function (f) {
25108             f.clearInvalid();
25109         });
25110         
25111         
25112         return this;
25113     },
25114
25115     /**
25116      * Resets this form.
25117      * @return {BasicForm} this
25118      */
25119     reset : function(){
25120         this.items.each(function(f){
25121             f.reset();
25122         });
25123         
25124         Roo.each(this.childForms || [], function (f) {
25125             f.reset();
25126         });
25127         this.resetHasChanged();
25128         
25129         return this;
25130     },
25131
25132     /**
25133      * Add Roo.form components to this form.
25134      * @param {Field} field1
25135      * @param {Field} field2 (optional)
25136      * @param {Field} etc (optional)
25137      * @return {BasicForm} this
25138      */
25139     add : function(){
25140         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25141         return this;
25142     },
25143
25144
25145     /**
25146      * Removes a field from the items collection (does NOT remove its markup).
25147      * @param {Field} field
25148      * @return {BasicForm} this
25149      */
25150     remove : function(field){
25151         this.items.remove(field);
25152         return this;
25153     },
25154
25155     /**
25156      * Looks at the fields in this form, checks them for an id attribute,
25157      * and calls applyTo on the existing dom element with that id.
25158      * @return {BasicForm} this
25159      */
25160     render : function(){
25161         this.items.each(function(f){
25162             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25163                 f.applyTo(f.id);
25164             }
25165         });
25166         return this;
25167     },
25168
25169     /**
25170      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25171      * @param {Object} values
25172      * @return {BasicForm} this
25173      */
25174     applyToFields : function(o){
25175         this.items.each(function(f){
25176            Roo.apply(f, o);
25177         });
25178         return this;
25179     },
25180
25181     /**
25182      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25183      * @param {Object} values
25184      * @return {BasicForm} this
25185      */
25186     applyIfToFields : function(o){
25187         this.items.each(function(f){
25188            Roo.applyIf(f, o);
25189         });
25190         return this;
25191     }
25192 });
25193
25194 // back compat
25195 Roo.BasicForm = Roo.form.BasicForm;
25196
25197 Roo.apply(Roo.form.BasicForm, {
25198     
25199     popover : {
25200         
25201         padding : 5,
25202         
25203         isApplied : false,
25204         
25205         isMasked : false,
25206         
25207         form : false,
25208         
25209         target : false,
25210         
25211         intervalID : false,
25212         
25213         maskEl : false,
25214         
25215         apply : function()
25216         {
25217             if(this.isApplied){
25218                 return;
25219             }
25220             
25221             this.maskEl = {
25222                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25223                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25224                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25225                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25226             };
25227             
25228             this.maskEl.top.enableDisplayMode("block");
25229             this.maskEl.left.enableDisplayMode("block");
25230             this.maskEl.bottom.enableDisplayMode("block");
25231             this.maskEl.right.enableDisplayMode("block");
25232             
25233             Roo.get(document.body).on('click', function(){
25234                 this.unmask();
25235             }, this);
25236             
25237             Roo.get(document.body).on('touchstart', function(){
25238                 this.unmask();
25239             }, this);
25240             
25241             this.isApplied = true
25242         },
25243         
25244         mask : function(form, target)
25245         {
25246             this.form = form;
25247             
25248             this.target = target;
25249             
25250             if(!this.form.errorMask || !target.el){
25251                 return;
25252             }
25253             
25254             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25255             
25256             var ot = this.target.el.calcOffsetsTo(scrollable);
25257             
25258             var scrollTo = ot[1] - this.form.maskOffset;
25259             
25260             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25261             
25262             scrollable.scrollTo('top', scrollTo);
25263             
25264             var el = this.target.wrap || this.target.el;
25265             
25266             var box = el.getBox();
25267             
25268             this.maskEl.top.setStyle('position', 'absolute');
25269             this.maskEl.top.setStyle('z-index', 10000);
25270             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25271             this.maskEl.top.setLeft(0);
25272             this.maskEl.top.setTop(0);
25273             this.maskEl.top.show();
25274             
25275             this.maskEl.left.setStyle('position', 'absolute');
25276             this.maskEl.left.setStyle('z-index', 10000);
25277             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25278             this.maskEl.left.setLeft(0);
25279             this.maskEl.left.setTop(box.y - this.padding);
25280             this.maskEl.left.show();
25281
25282             this.maskEl.bottom.setStyle('position', 'absolute');
25283             this.maskEl.bottom.setStyle('z-index', 10000);
25284             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25285             this.maskEl.bottom.setLeft(0);
25286             this.maskEl.bottom.setTop(box.bottom + this.padding);
25287             this.maskEl.bottom.show();
25288
25289             this.maskEl.right.setStyle('position', 'absolute');
25290             this.maskEl.right.setStyle('z-index', 10000);
25291             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25292             this.maskEl.right.setLeft(box.right + this.padding);
25293             this.maskEl.right.setTop(box.y - this.padding);
25294             this.maskEl.right.show();
25295
25296             this.intervalID = window.setInterval(function() {
25297                 Roo.form.BasicForm.popover.unmask();
25298             }, 10000);
25299
25300             window.onwheel = function(){ return false;};
25301             
25302             (function(){ this.isMasked = true; }).defer(500, this);
25303             
25304         },
25305         
25306         unmask : function()
25307         {
25308             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25309                 return;
25310             }
25311             
25312             this.maskEl.top.setStyle('position', 'absolute');
25313             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25314             this.maskEl.top.hide();
25315
25316             this.maskEl.left.setStyle('position', 'absolute');
25317             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25318             this.maskEl.left.hide();
25319
25320             this.maskEl.bottom.setStyle('position', 'absolute');
25321             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25322             this.maskEl.bottom.hide();
25323
25324             this.maskEl.right.setStyle('position', 'absolute');
25325             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25326             this.maskEl.right.hide();
25327             
25328             window.onwheel = function(){ return true;};
25329             
25330             if(this.intervalID){
25331                 window.clearInterval(this.intervalID);
25332                 this.intervalID = false;
25333             }
25334             
25335             this.isMasked = false;
25336             
25337         }
25338         
25339     }
25340     
25341 });/*
25342  * Based on:
25343  * Ext JS Library 1.1.1
25344  * Copyright(c) 2006-2007, Ext JS, LLC.
25345  *
25346  * Originally Released Under LGPL - original licence link has changed is not relivant.
25347  *
25348  * Fork - LGPL
25349  * <script type="text/javascript">
25350  */
25351
25352 /**
25353  * @class Roo.form.Form
25354  * @extends Roo.form.BasicForm
25355  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25356  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25357  * @constructor
25358  * @param {Object} config Configuration options
25359  */
25360 Roo.form.Form = function(config){
25361     var xitems =  [];
25362     if (config.items) {
25363         xitems = config.items;
25364         delete config.items;
25365     }
25366    
25367     
25368     Roo.form.Form.superclass.constructor.call(this, null, config);
25369     this.url = this.url || this.action;
25370     if(!this.root){
25371         this.root = new Roo.form.Layout(Roo.applyIf({
25372             id: Roo.id()
25373         }, config));
25374     }
25375     this.active = this.root;
25376     /**
25377      * Array of all the buttons that have been added to this form via {@link addButton}
25378      * @type Array
25379      */
25380     this.buttons = [];
25381     this.allItems = [];
25382     this.addEvents({
25383         /**
25384          * @event clientvalidation
25385          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25386          * @param {Form} this
25387          * @param {Boolean} valid true if the form has passed client-side validation
25388          */
25389         clientvalidation: true,
25390         /**
25391          * @event rendered
25392          * Fires when the form is rendered
25393          * @param {Roo.form.Form} form
25394          */
25395         rendered : true
25396     });
25397     
25398     if (this.progressUrl) {
25399             // push a hidden field onto the list of fields..
25400             this.addxtype( {
25401                     xns: Roo.form, 
25402                     xtype : 'Hidden', 
25403                     name : 'UPLOAD_IDENTIFIER' 
25404             });
25405         }
25406         
25407     
25408     Roo.each(xitems, this.addxtype, this);
25409     
25410 };
25411
25412 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25413      /**
25414      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25415      */
25416     
25417     /**
25418      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25419      */
25420     /**
25421      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25422      */
25423     /**
25424      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25425      */
25426     buttonAlign:'center',
25427
25428     /**
25429      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25430      */
25431     minButtonWidth:75,
25432
25433     /**
25434      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25435      * This property cascades to child containers if not set.
25436      */
25437     labelAlign:'left',
25438
25439     /**
25440      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25441      * fires a looping event with that state. This is required to bind buttons to the valid
25442      * state using the config value formBind:true on the button.
25443      */
25444     monitorValid : false,
25445
25446     /**
25447      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25448      */
25449     monitorPoll : 200,
25450     
25451     /**
25452      * @cfg {String} progressUrl - Url to return progress data 
25453      */
25454     
25455     progressUrl : false,
25456     /**
25457      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25458      * sending a formdata with extra parameters - eg uploaded elements.
25459      */
25460     
25461     formData : false,
25462     
25463     /**
25464      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25465      * fields are added and the column is closed. If no fields are passed the column remains open
25466      * until end() is called.
25467      * @param {Object} config The config to pass to the column
25468      * @param {Field} field1 (optional)
25469      * @param {Field} field2 (optional)
25470      * @param {Field} etc (optional)
25471      * @return Column The column container object
25472      */
25473     column : function(c){
25474         var col = new Roo.form.Column(c);
25475         this.start(col);
25476         if(arguments.length > 1){ // duplicate code required because of Opera
25477             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25478             this.end();
25479         }
25480         return col;
25481     },
25482
25483     /**
25484      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25485      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25486      * until end() is called.
25487      * @param {Object} config The config to pass to the fieldset
25488      * @param {Field} field1 (optional)
25489      * @param {Field} field2 (optional)
25490      * @param {Field} etc (optional)
25491      * @return FieldSet The fieldset container object
25492      */
25493     fieldset : function(c){
25494         var fs = new Roo.form.FieldSet(c);
25495         this.start(fs);
25496         if(arguments.length > 1){ // duplicate code required because of Opera
25497             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25498             this.end();
25499         }
25500         return fs;
25501     },
25502
25503     /**
25504      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25505      * fields are added and the container is closed. If no fields are passed the container remains open
25506      * until end() is called.
25507      * @param {Object} config The config to pass to the Layout
25508      * @param {Field} field1 (optional)
25509      * @param {Field} field2 (optional)
25510      * @param {Field} etc (optional)
25511      * @return Layout The container object
25512      */
25513     container : function(c){
25514         var l = new Roo.form.Layout(c);
25515         this.start(l);
25516         if(arguments.length > 1){ // duplicate code required because of Opera
25517             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25518             this.end();
25519         }
25520         return l;
25521     },
25522
25523     /**
25524      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25525      * @param {Object} container A Roo.form.Layout or subclass of Layout
25526      * @return {Form} this
25527      */
25528     start : function(c){
25529         // cascade label info
25530         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25531         this.active.stack.push(c);
25532         c.ownerCt = this.active;
25533         this.active = c;
25534         return this;
25535     },
25536
25537     /**
25538      * Closes the current open container
25539      * @return {Form} this
25540      */
25541     end : function(){
25542         if(this.active == this.root){
25543             return this;
25544         }
25545         this.active = this.active.ownerCt;
25546         return this;
25547     },
25548
25549     /**
25550      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25551      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25552      * as the label of the field.
25553      * @param {Field} field1
25554      * @param {Field} field2 (optional)
25555      * @param {Field} etc. (optional)
25556      * @return {Form} this
25557      */
25558     add : function(){
25559         this.active.stack.push.apply(this.active.stack, arguments);
25560         this.allItems.push.apply(this.allItems,arguments);
25561         var r = [];
25562         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25563             if(a[i].isFormField){
25564                 r.push(a[i]);
25565             }
25566         }
25567         if(r.length > 0){
25568             Roo.form.Form.superclass.add.apply(this, r);
25569         }
25570         return this;
25571     },
25572     
25573
25574     
25575     
25576     
25577      /**
25578      * Find any element that has been added to a form, using it's ID or name
25579      * This can include framesets, columns etc. along with regular fields..
25580      * @param {String} id - id or name to find.
25581      
25582      * @return {Element} e - or false if nothing found.
25583      */
25584     findbyId : function(id)
25585     {
25586         var ret = false;
25587         if (!id) {
25588             return ret;
25589         }
25590         Roo.each(this.allItems, function(f){
25591             if (f.id == id || f.name == id ){
25592                 ret = f;
25593                 return false;
25594             }
25595         });
25596         return ret;
25597     },
25598
25599     
25600     
25601     /**
25602      * Render this form into the passed container. This should only be called once!
25603      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25604      * @return {Form} this
25605      */
25606     render : function(ct)
25607     {
25608         
25609         
25610         
25611         ct = Roo.get(ct);
25612         var o = this.autoCreate || {
25613             tag: 'form',
25614             method : this.method || 'POST',
25615             id : this.id || Roo.id()
25616         };
25617         this.initEl(ct.createChild(o));
25618
25619         this.root.render(this.el);
25620         
25621        
25622              
25623         this.items.each(function(f){
25624             f.render('x-form-el-'+f.id);
25625         });
25626
25627         if(this.buttons.length > 0){
25628             // tables are required to maintain order and for correct IE layout
25629             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25630                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25631                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25632             }}, null, true);
25633             var tr = tb.getElementsByTagName('tr')[0];
25634             for(var i = 0, len = this.buttons.length; i < len; i++) {
25635                 var b = this.buttons[i];
25636                 var td = document.createElement('td');
25637                 td.className = 'x-form-btn-td';
25638                 b.render(tr.appendChild(td));
25639             }
25640         }
25641         if(this.monitorValid){ // initialize after render
25642             this.startMonitoring();
25643         }
25644         this.fireEvent('rendered', this);
25645         return this;
25646     },
25647
25648     /**
25649      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25650      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25651      * object or a valid Roo.DomHelper element config
25652      * @param {Function} handler The function called when the button is clicked
25653      * @param {Object} scope (optional) The scope of the handler function
25654      * @return {Roo.Button}
25655      */
25656     addButton : function(config, handler, scope){
25657         var bc = {
25658             handler: handler,
25659             scope: scope,
25660             minWidth: this.minButtonWidth,
25661             hideParent:true
25662         };
25663         if(typeof config == "string"){
25664             bc.text = config;
25665         }else{
25666             Roo.apply(bc, config);
25667         }
25668         var btn = new Roo.Button(null, bc);
25669         this.buttons.push(btn);
25670         return btn;
25671     },
25672
25673      /**
25674      * Adds a series of form elements (using the xtype property as the factory method.
25675      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25676      * @param {Object} config 
25677      */
25678     
25679     addxtype : function()
25680     {
25681         var ar = Array.prototype.slice.call(arguments, 0);
25682         var ret = false;
25683         for(var i = 0; i < ar.length; i++) {
25684             if (!ar[i]) {
25685                 continue; // skip -- if this happends something invalid got sent, we 
25686                 // should ignore it, as basically that interface element will not show up
25687                 // and that should be pretty obvious!!
25688             }
25689             
25690             if (Roo.form[ar[i].xtype]) {
25691                 ar[i].form = this;
25692                 var fe = Roo.factory(ar[i], Roo.form);
25693                 if (!ret) {
25694                     ret = fe;
25695                 }
25696                 fe.form = this;
25697                 if (fe.store) {
25698                     fe.store.form = this;
25699                 }
25700                 if (fe.isLayout) {  
25701                          
25702                     this.start(fe);
25703                     this.allItems.push(fe);
25704                     if (fe.items && fe.addxtype) {
25705                         fe.addxtype.apply(fe, fe.items);
25706                         delete fe.items;
25707                     }
25708                      this.end();
25709                     continue;
25710                 }
25711                 
25712                 
25713                  
25714                 this.add(fe);
25715               //  console.log('adding ' + ar[i].xtype);
25716             }
25717             if (ar[i].xtype == 'Button') {  
25718                 //console.log('adding button');
25719                 //console.log(ar[i]);
25720                 this.addButton(ar[i]);
25721                 this.allItems.push(fe);
25722                 continue;
25723             }
25724             
25725             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25726                 alert('end is not supported on xtype any more, use items');
25727             //    this.end();
25728             //    //console.log('adding end');
25729             }
25730             
25731         }
25732         return ret;
25733     },
25734     
25735     /**
25736      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25737      * option "monitorValid"
25738      */
25739     startMonitoring : function(){
25740         if(!this.bound){
25741             this.bound = true;
25742             Roo.TaskMgr.start({
25743                 run : this.bindHandler,
25744                 interval : this.monitorPoll || 200,
25745                 scope: this
25746             });
25747         }
25748     },
25749
25750     /**
25751      * Stops monitoring of the valid state of this form
25752      */
25753     stopMonitoring : function(){
25754         this.bound = false;
25755     },
25756
25757     // private
25758     bindHandler : function(){
25759         if(!this.bound){
25760             return false; // stops binding
25761         }
25762         var valid = true;
25763         this.items.each(function(f){
25764             if(!f.isValid(true)){
25765                 valid = false;
25766                 return false;
25767             }
25768         });
25769         for(var i = 0, len = this.buttons.length; i < len; i++){
25770             var btn = this.buttons[i];
25771             if(btn.formBind === true && btn.disabled === valid){
25772                 btn.setDisabled(!valid);
25773             }
25774         }
25775         this.fireEvent('clientvalidation', this, valid);
25776     }
25777     
25778     
25779     
25780     
25781     
25782     
25783     
25784     
25785 });
25786
25787
25788 // back compat
25789 Roo.Form = Roo.form.Form;
25790 /*
25791  * Based on:
25792  * Ext JS Library 1.1.1
25793  * Copyright(c) 2006-2007, Ext JS, LLC.
25794  *
25795  * Originally Released Under LGPL - original licence link has changed is not relivant.
25796  *
25797  * Fork - LGPL
25798  * <script type="text/javascript">
25799  */
25800
25801 // as we use this in bootstrap.
25802 Roo.namespace('Roo.form');
25803  /**
25804  * @class Roo.form.Action
25805  * Internal Class used to handle form actions
25806  * @constructor
25807  * @param {Roo.form.BasicForm} el The form element or its id
25808  * @param {Object} config Configuration options
25809  */
25810
25811  
25812  
25813 // define the action interface
25814 Roo.form.Action = function(form, options){
25815     this.form = form;
25816     this.options = options || {};
25817 };
25818 /**
25819  * Client Validation Failed
25820  * @const 
25821  */
25822 Roo.form.Action.CLIENT_INVALID = 'client';
25823 /**
25824  * Server Validation Failed
25825  * @const 
25826  */
25827 Roo.form.Action.SERVER_INVALID = 'server';
25828  /**
25829  * Connect to Server Failed
25830  * @const 
25831  */
25832 Roo.form.Action.CONNECT_FAILURE = 'connect';
25833 /**
25834  * Reading Data from Server Failed
25835  * @const 
25836  */
25837 Roo.form.Action.LOAD_FAILURE = 'load';
25838
25839 Roo.form.Action.prototype = {
25840     type : 'default',
25841     failureType : undefined,
25842     response : undefined,
25843     result : undefined,
25844
25845     // interface method
25846     run : function(options){
25847
25848     },
25849
25850     // interface method
25851     success : function(response){
25852
25853     },
25854
25855     // interface method
25856     handleResponse : function(response){
25857
25858     },
25859
25860     // default connection failure
25861     failure : function(response){
25862         
25863         this.response = response;
25864         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25865         this.form.afterAction(this, false);
25866     },
25867
25868     processResponse : function(response){
25869         this.response = response;
25870         if(!response.responseText){
25871             return true;
25872         }
25873         this.result = this.handleResponse(response);
25874         return this.result;
25875     },
25876
25877     // utility functions used internally
25878     getUrl : function(appendParams){
25879         var url = this.options.url || this.form.url || this.form.el.dom.action;
25880         if(appendParams){
25881             var p = this.getParams();
25882             if(p){
25883                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25884             }
25885         }
25886         return url;
25887     },
25888
25889     getMethod : function(){
25890         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25891     },
25892
25893     getParams : function(){
25894         var bp = this.form.baseParams;
25895         var p = this.options.params;
25896         if(p){
25897             if(typeof p == "object"){
25898                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25899             }else if(typeof p == 'string' && bp){
25900                 p += '&' + Roo.urlEncode(bp);
25901             }
25902         }else if(bp){
25903             p = Roo.urlEncode(bp);
25904         }
25905         return p;
25906     },
25907
25908     createCallback : function(){
25909         return {
25910             success: this.success,
25911             failure: this.failure,
25912             scope: this,
25913             timeout: (this.form.timeout*1000),
25914             upload: this.form.fileUpload ? this.success : undefined
25915         };
25916     }
25917 };
25918
25919 Roo.form.Action.Submit = function(form, options){
25920     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25921 };
25922
25923 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25924     type : 'submit',
25925
25926     haveProgress : false,
25927     uploadComplete : false,
25928     
25929     // uploadProgress indicator.
25930     uploadProgress : function()
25931     {
25932         if (!this.form.progressUrl) {
25933             return;
25934         }
25935         
25936         if (!this.haveProgress) {
25937             Roo.MessageBox.progress("Uploading", "Uploading");
25938         }
25939         if (this.uploadComplete) {
25940            Roo.MessageBox.hide();
25941            return;
25942         }
25943         
25944         this.haveProgress = true;
25945    
25946         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25947         
25948         var c = new Roo.data.Connection();
25949         c.request({
25950             url : this.form.progressUrl,
25951             params: {
25952                 id : uid
25953             },
25954             method: 'GET',
25955             success : function(req){
25956                //console.log(data);
25957                 var rdata = false;
25958                 var edata;
25959                 try  {
25960                    rdata = Roo.decode(req.responseText)
25961                 } catch (e) {
25962                     Roo.log("Invalid data from server..");
25963                     Roo.log(edata);
25964                     return;
25965                 }
25966                 if (!rdata || !rdata.success) {
25967                     Roo.log(rdata);
25968                     Roo.MessageBox.alert(Roo.encode(rdata));
25969                     return;
25970                 }
25971                 var data = rdata.data;
25972                 
25973                 if (this.uploadComplete) {
25974                    Roo.MessageBox.hide();
25975                    return;
25976                 }
25977                    
25978                 if (data){
25979                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25980                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25981                     );
25982                 }
25983                 this.uploadProgress.defer(2000,this);
25984             },
25985        
25986             failure: function(data) {
25987                 Roo.log('progress url failed ');
25988                 Roo.log(data);
25989             },
25990             scope : this
25991         });
25992            
25993     },
25994     
25995     
25996     run : function()
25997     {
25998         // run get Values on the form, so it syncs any secondary forms.
25999         this.form.getValues();
26000         
26001         var o = this.options;
26002         var method = this.getMethod();
26003         var isPost = method == 'POST';
26004         if(o.clientValidation === false || this.form.isValid()){
26005             
26006             if (this.form.progressUrl) {
26007                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26008                     (new Date() * 1) + '' + Math.random());
26009                     
26010             } 
26011             
26012             
26013             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26014                 form:this.form.el.dom,
26015                 url:this.getUrl(!isPost),
26016                 method: method,
26017                 params:isPost ? this.getParams() : null,
26018                 isUpload: this.form.fileUpload,
26019                 formData : this.form.formData
26020             }));
26021             
26022             this.uploadProgress();
26023
26024         }else if (o.clientValidation !== false){ // client validation failed
26025             this.failureType = Roo.form.Action.CLIENT_INVALID;
26026             this.form.afterAction(this, false);
26027         }
26028     },
26029
26030     success : function(response)
26031     {
26032         this.uploadComplete= true;
26033         if (this.haveProgress) {
26034             Roo.MessageBox.hide();
26035         }
26036         
26037         
26038         var result = this.processResponse(response);
26039         if(result === true || result.success){
26040             this.form.afterAction(this, true);
26041             return;
26042         }
26043         if(result.errors){
26044             this.form.markInvalid(result.errors);
26045             this.failureType = Roo.form.Action.SERVER_INVALID;
26046         }
26047         this.form.afterAction(this, false);
26048     },
26049     failure : function(response)
26050     {
26051         this.uploadComplete= true;
26052         if (this.haveProgress) {
26053             Roo.MessageBox.hide();
26054         }
26055         
26056         this.response = response;
26057         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26058         this.form.afterAction(this, false);
26059     },
26060     
26061     handleResponse : function(response){
26062         if(this.form.errorReader){
26063             var rs = this.form.errorReader.read(response);
26064             var errors = [];
26065             if(rs.records){
26066                 for(var i = 0, len = rs.records.length; i < len; i++) {
26067                     var r = rs.records[i];
26068                     errors[i] = r.data;
26069                 }
26070             }
26071             if(errors.length < 1){
26072                 errors = null;
26073             }
26074             return {
26075                 success : rs.success,
26076                 errors : errors
26077             };
26078         }
26079         var ret = false;
26080         try {
26081             ret = Roo.decode(response.responseText);
26082         } catch (e) {
26083             ret = {
26084                 success: false,
26085                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26086                 errors : []
26087             };
26088         }
26089         return ret;
26090         
26091     }
26092 });
26093
26094
26095 Roo.form.Action.Load = function(form, options){
26096     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26097     this.reader = this.form.reader;
26098 };
26099
26100 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26101     type : 'load',
26102
26103     run : function(){
26104         
26105         Roo.Ajax.request(Roo.apply(
26106                 this.createCallback(), {
26107                     method:this.getMethod(),
26108                     url:this.getUrl(false),
26109                     params:this.getParams()
26110         }));
26111     },
26112
26113     success : function(response){
26114         
26115         var result = this.processResponse(response);
26116         if(result === true || !result.success || !result.data){
26117             this.failureType = Roo.form.Action.LOAD_FAILURE;
26118             this.form.afterAction(this, false);
26119             return;
26120         }
26121         this.form.clearInvalid();
26122         this.form.setValues(result.data);
26123         this.form.afterAction(this, true);
26124     },
26125
26126     handleResponse : function(response){
26127         if(this.form.reader){
26128             var rs = this.form.reader.read(response);
26129             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26130             return {
26131                 success : rs.success,
26132                 data : data
26133             };
26134         }
26135         return Roo.decode(response.responseText);
26136     }
26137 });
26138
26139 Roo.form.Action.ACTION_TYPES = {
26140     'load' : Roo.form.Action.Load,
26141     'submit' : Roo.form.Action.Submit
26142 };/*
26143  * Based on:
26144  * Ext JS Library 1.1.1
26145  * Copyright(c) 2006-2007, Ext JS, LLC.
26146  *
26147  * Originally Released Under LGPL - original licence link has changed is not relivant.
26148  *
26149  * Fork - LGPL
26150  * <script type="text/javascript">
26151  */
26152  
26153 /**
26154  * @class Roo.form.Layout
26155  * @extends Roo.Component
26156  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26157  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26158  * @constructor
26159  * @param {Object} config Configuration options
26160  */
26161 Roo.form.Layout = function(config){
26162     var xitems = [];
26163     if (config.items) {
26164         xitems = config.items;
26165         delete config.items;
26166     }
26167     Roo.form.Layout.superclass.constructor.call(this, config);
26168     this.stack = [];
26169     Roo.each(xitems, this.addxtype, this);
26170      
26171 };
26172
26173 Roo.extend(Roo.form.Layout, Roo.Component, {
26174     /**
26175      * @cfg {String/Object} autoCreate
26176      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26177      */
26178     /**
26179      * @cfg {String/Object/Function} style
26180      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26181      * a function which returns such a specification.
26182      */
26183     /**
26184      * @cfg {String} labelAlign
26185      * Valid values are "left," "top" and "right" (defaults to "left")
26186      */
26187     /**
26188      * @cfg {Number} labelWidth
26189      * Fixed width in pixels of all field labels (defaults to undefined)
26190      */
26191     /**
26192      * @cfg {Boolean} clear
26193      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26194      */
26195     clear : true,
26196     /**
26197      * @cfg {String} labelSeparator
26198      * The separator to use after field labels (defaults to ':')
26199      */
26200     labelSeparator : ':',
26201     /**
26202      * @cfg {Boolean} hideLabels
26203      * True to suppress the display of field labels in this layout (defaults to false)
26204      */
26205     hideLabels : false,
26206
26207     // private
26208     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26209     
26210     isLayout : true,
26211     
26212     // private
26213     onRender : function(ct, position){
26214         if(this.el){ // from markup
26215             this.el = Roo.get(this.el);
26216         }else {  // generate
26217             var cfg = this.getAutoCreate();
26218             this.el = ct.createChild(cfg, position);
26219         }
26220         if(this.style){
26221             this.el.applyStyles(this.style);
26222         }
26223         if(this.labelAlign){
26224             this.el.addClass('x-form-label-'+this.labelAlign);
26225         }
26226         if(this.hideLabels){
26227             this.labelStyle = "display:none";
26228             this.elementStyle = "padding-left:0;";
26229         }else{
26230             if(typeof this.labelWidth == 'number'){
26231                 this.labelStyle = "width:"+this.labelWidth+"px;";
26232                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26233             }
26234             if(this.labelAlign == 'top'){
26235                 this.labelStyle = "width:auto;";
26236                 this.elementStyle = "padding-left:0;";
26237             }
26238         }
26239         var stack = this.stack;
26240         var slen = stack.length;
26241         if(slen > 0){
26242             if(!this.fieldTpl){
26243                 var t = new Roo.Template(
26244                     '<div class="x-form-item {5}">',
26245                         '<label for="{0}" style="{2}">{1}{4}</label>',
26246                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26247                         '</div>',
26248                     '</div><div class="x-form-clear-left"></div>'
26249                 );
26250                 t.disableFormats = true;
26251                 t.compile();
26252                 Roo.form.Layout.prototype.fieldTpl = t;
26253             }
26254             for(var i = 0; i < slen; i++) {
26255                 if(stack[i].isFormField){
26256                     this.renderField(stack[i]);
26257                 }else{
26258                     this.renderComponent(stack[i]);
26259                 }
26260             }
26261         }
26262         if(this.clear){
26263             this.el.createChild({cls:'x-form-clear'});
26264         }
26265     },
26266
26267     // private
26268     renderField : function(f){
26269         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26270                f.id, //0
26271                f.fieldLabel, //1
26272                f.labelStyle||this.labelStyle||'', //2
26273                this.elementStyle||'', //3
26274                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26275                f.itemCls||this.itemCls||''  //5
26276        ], true).getPrevSibling());
26277     },
26278
26279     // private
26280     renderComponent : function(c){
26281         c.render(c.isLayout ? this.el : this.el.createChild());    
26282     },
26283     /**
26284      * Adds a object form elements (using the xtype property as the factory method.)
26285      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26286      * @param {Object} config 
26287      */
26288     addxtype : function(o)
26289     {
26290         // create the lement.
26291         o.form = this.form;
26292         var fe = Roo.factory(o, Roo.form);
26293         this.form.allItems.push(fe);
26294         this.stack.push(fe);
26295         
26296         if (fe.isFormField) {
26297             this.form.items.add(fe);
26298         }
26299          
26300         return fe;
26301     }
26302 });
26303
26304 /**
26305  * @class Roo.form.Column
26306  * @extends Roo.form.Layout
26307  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26308  * @constructor
26309  * @param {Object} config Configuration options
26310  */
26311 Roo.form.Column = function(config){
26312     Roo.form.Column.superclass.constructor.call(this, config);
26313 };
26314
26315 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26316     /**
26317      * @cfg {Number/String} width
26318      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26319      */
26320     /**
26321      * @cfg {String/Object} autoCreate
26322      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26323      */
26324
26325     // private
26326     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26327
26328     // private
26329     onRender : function(ct, position){
26330         Roo.form.Column.superclass.onRender.call(this, ct, position);
26331         if(this.width){
26332             this.el.setWidth(this.width);
26333         }
26334     }
26335 });
26336
26337
26338 /**
26339  * @class Roo.form.Row
26340  * @extends Roo.form.Layout
26341  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26342  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26343  * @constructor
26344  * @param {Object} config Configuration options
26345  */
26346
26347  
26348 Roo.form.Row = function(config){
26349     Roo.form.Row.superclass.constructor.call(this, config);
26350 };
26351  
26352 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26353       /**
26354      * @cfg {Number/String} width
26355      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26356      */
26357     /**
26358      * @cfg {Number/String} height
26359      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26360      */
26361     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26362     
26363     padWidth : 20,
26364     // private
26365     onRender : function(ct, position){
26366         //console.log('row render');
26367         if(!this.rowTpl){
26368             var t = new Roo.Template(
26369                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26370                     '<label for="{0}" style="{2}">{1}{4}</label>',
26371                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26372                     '</div>',
26373                 '</div>'
26374             );
26375             t.disableFormats = true;
26376             t.compile();
26377             Roo.form.Layout.prototype.rowTpl = t;
26378         }
26379         this.fieldTpl = this.rowTpl;
26380         
26381         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26382         var labelWidth = 100;
26383         
26384         if ((this.labelAlign != 'top')) {
26385             if (typeof this.labelWidth == 'number') {
26386                 labelWidth = this.labelWidth
26387             }
26388             this.padWidth =  20 + labelWidth;
26389             
26390         }
26391         
26392         Roo.form.Column.superclass.onRender.call(this, ct, position);
26393         if(this.width){
26394             this.el.setWidth(this.width);
26395         }
26396         if(this.height){
26397             this.el.setHeight(this.height);
26398         }
26399     },
26400     
26401     // private
26402     renderField : function(f){
26403         f.fieldEl = this.fieldTpl.append(this.el, [
26404                f.id, f.fieldLabel,
26405                f.labelStyle||this.labelStyle||'',
26406                this.elementStyle||'',
26407                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26408                f.itemCls||this.itemCls||'',
26409                f.width ? f.width + this.padWidth : 160 + this.padWidth
26410        ],true);
26411     }
26412 });
26413  
26414
26415 /**
26416  * @class Roo.form.FieldSet
26417  * @extends Roo.form.Layout
26418  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26419  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26420  * @constructor
26421  * @param {Object} config Configuration options
26422  */
26423 Roo.form.FieldSet = function(config){
26424     Roo.form.FieldSet.superclass.constructor.call(this, config);
26425 };
26426
26427 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26428     /**
26429      * @cfg {String} legend
26430      * The text to display as the legend for the FieldSet (defaults to '')
26431      */
26432     /**
26433      * @cfg {String/Object} autoCreate
26434      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26435      */
26436
26437     // private
26438     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26439
26440     // private
26441     onRender : function(ct, position){
26442         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26443         if(this.legend){
26444             this.setLegend(this.legend);
26445         }
26446     },
26447
26448     // private
26449     setLegend : function(text){
26450         if(this.rendered){
26451             this.el.child('legend').update(text);
26452         }
26453     }
26454 });/*
26455  * Based on:
26456  * Ext JS Library 1.1.1
26457  * Copyright(c) 2006-2007, Ext JS, LLC.
26458  *
26459  * Originally Released Under LGPL - original licence link has changed is not relivant.
26460  *
26461  * Fork - LGPL
26462  * <script type="text/javascript">
26463  */
26464 /**
26465  * @class Roo.form.VTypes
26466  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26467  * @singleton
26468  */
26469 Roo.form.VTypes = function(){
26470     // closure these in so they are only created once.
26471     var alpha = /^[a-zA-Z_]+$/;
26472     var alphanum = /^[a-zA-Z0-9_]+$/;
26473     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26474     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26475
26476     // All these messages and functions are configurable
26477     return {
26478         /**
26479          * The function used to validate email addresses
26480          * @param {String} value The email address
26481          */
26482         'email' : function(v){
26483             return email.test(v);
26484         },
26485         /**
26486          * The error text to display when the email validation function returns false
26487          * @type String
26488          */
26489         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26490         /**
26491          * The keystroke filter mask to be applied on email input
26492          * @type RegExp
26493          */
26494         'emailMask' : /[a-z0-9_\.\-@]/i,
26495
26496         /**
26497          * The function used to validate URLs
26498          * @param {String} value The URL
26499          */
26500         'url' : function(v){
26501             return url.test(v);
26502         },
26503         /**
26504          * The error text to display when the url validation function returns false
26505          * @type String
26506          */
26507         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26508         
26509         /**
26510          * The function used to validate alpha values
26511          * @param {String} value The value
26512          */
26513         'alpha' : function(v){
26514             return alpha.test(v);
26515         },
26516         /**
26517          * The error text to display when the alpha validation function returns false
26518          * @type String
26519          */
26520         'alphaText' : 'This field should only contain letters and _',
26521         /**
26522          * The keystroke filter mask to be applied on alpha input
26523          * @type RegExp
26524          */
26525         'alphaMask' : /[a-z_]/i,
26526
26527         /**
26528          * The function used to validate alphanumeric values
26529          * @param {String} value The value
26530          */
26531         'alphanum' : function(v){
26532             return alphanum.test(v);
26533         },
26534         /**
26535          * The error text to display when the alphanumeric validation function returns false
26536          * @type String
26537          */
26538         'alphanumText' : 'This field should only contain letters, numbers and _',
26539         /**
26540          * The keystroke filter mask to be applied on alphanumeric input
26541          * @type RegExp
26542          */
26543         'alphanumMask' : /[a-z0-9_]/i
26544     };
26545 }();//<script type="text/javascript">
26546
26547 /**
26548  * @class Roo.form.FCKeditor
26549  * @extends Roo.form.TextArea
26550  * Wrapper around the FCKEditor http://www.fckeditor.net
26551  * @constructor
26552  * Creates a new FCKeditor
26553  * @param {Object} config Configuration options
26554  */
26555 Roo.form.FCKeditor = function(config){
26556     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26557     this.addEvents({
26558          /**
26559          * @event editorinit
26560          * Fired when the editor is initialized - you can add extra handlers here..
26561          * @param {FCKeditor} this
26562          * @param {Object} the FCK object.
26563          */
26564         editorinit : true
26565     });
26566     
26567     
26568 };
26569 Roo.form.FCKeditor.editors = { };
26570 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26571 {
26572     //defaultAutoCreate : {
26573     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26574     //},
26575     // private
26576     /**
26577      * @cfg {Object} fck options - see fck manual for details.
26578      */
26579     fckconfig : false,
26580     
26581     /**
26582      * @cfg {Object} fck toolbar set (Basic or Default)
26583      */
26584     toolbarSet : 'Basic',
26585     /**
26586      * @cfg {Object} fck BasePath
26587      */ 
26588     basePath : '/fckeditor/',
26589     
26590     
26591     frame : false,
26592     
26593     value : '',
26594     
26595    
26596     onRender : function(ct, position)
26597     {
26598         if(!this.el){
26599             this.defaultAutoCreate = {
26600                 tag: "textarea",
26601                 style:"width:300px;height:60px;",
26602                 autocomplete: "new-password"
26603             };
26604         }
26605         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26606         /*
26607         if(this.grow){
26608             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26609             if(this.preventScrollbars){
26610                 this.el.setStyle("overflow", "hidden");
26611             }
26612             this.el.setHeight(this.growMin);
26613         }
26614         */
26615         //console.log('onrender' + this.getId() );
26616         Roo.form.FCKeditor.editors[this.getId()] = this;
26617          
26618
26619         this.replaceTextarea() ;
26620         
26621     },
26622     
26623     getEditor : function() {
26624         return this.fckEditor;
26625     },
26626     /**
26627      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26628      * @param {Mixed} value The value to set
26629      */
26630     
26631     
26632     setValue : function(value)
26633     {
26634         //console.log('setValue: ' + value);
26635         
26636         if(typeof(value) == 'undefined') { // not sure why this is happending...
26637             return;
26638         }
26639         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26640         
26641         //if(!this.el || !this.getEditor()) {
26642         //    this.value = value;
26643             //this.setValue.defer(100,this,[value]);    
26644         //    return;
26645         //} 
26646         
26647         if(!this.getEditor()) {
26648             return;
26649         }
26650         
26651         this.getEditor().SetData(value);
26652         
26653         //
26654
26655     },
26656
26657     /**
26658      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26659      * @return {Mixed} value The field value
26660      */
26661     getValue : function()
26662     {
26663         
26664         if (this.frame && this.frame.dom.style.display == 'none') {
26665             return Roo.form.FCKeditor.superclass.getValue.call(this);
26666         }
26667         
26668         if(!this.el || !this.getEditor()) {
26669            
26670            // this.getValue.defer(100,this); 
26671             return this.value;
26672         }
26673        
26674         
26675         var value=this.getEditor().GetData();
26676         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26677         return Roo.form.FCKeditor.superclass.getValue.call(this);
26678         
26679
26680     },
26681
26682     /**
26683      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26684      * @return {Mixed} value The field value
26685      */
26686     getRawValue : function()
26687     {
26688         if (this.frame && this.frame.dom.style.display == 'none') {
26689             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26690         }
26691         
26692         if(!this.el || !this.getEditor()) {
26693             //this.getRawValue.defer(100,this); 
26694             return this.value;
26695             return;
26696         }
26697         
26698         
26699         
26700         var value=this.getEditor().GetData();
26701         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26702         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26703          
26704     },
26705     
26706     setSize : function(w,h) {
26707         
26708         
26709         
26710         //if (this.frame && this.frame.dom.style.display == 'none') {
26711         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26712         //    return;
26713         //}
26714         //if(!this.el || !this.getEditor()) {
26715         //    this.setSize.defer(100,this, [w,h]); 
26716         //    return;
26717         //}
26718         
26719         
26720         
26721         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26722         
26723         this.frame.dom.setAttribute('width', w);
26724         this.frame.dom.setAttribute('height', h);
26725         this.frame.setSize(w,h);
26726         
26727     },
26728     
26729     toggleSourceEdit : function(value) {
26730         
26731       
26732          
26733         this.el.dom.style.display = value ? '' : 'none';
26734         this.frame.dom.style.display = value ?  'none' : '';
26735         
26736     },
26737     
26738     
26739     focus: function(tag)
26740     {
26741         if (this.frame.dom.style.display == 'none') {
26742             return Roo.form.FCKeditor.superclass.focus.call(this);
26743         }
26744         if(!this.el || !this.getEditor()) {
26745             this.focus.defer(100,this, [tag]); 
26746             return;
26747         }
26748         
26749         
26750         
26751         
26752         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26753         this.getEditor().Focus();
26754         if (tgs.length) {
26755             if (!this.getEditor().Selection.GetSelection()) {
26756                 this.focus.defer(100,this, [tag]); 
26757                 return;
26758             }
26759             
26760             
26761             var r = this.getEditor().EditorDocument.createRange();
26762             r.setStart(tgs[0],0);
26763             r.setEnd(tgs[0],0);
26764             this.getEditor().Selection.GetSelection().removeAllRanges();
26765             this.getEditor().Selection.GetSelection().addRange(r);
26766             this.getEditor().Focus();
26767         }
26768         
26769     },
26770     
26771     
26772     
26773     replaceTextarea : function()
26774     {
26775         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26776             return ;
26777         }
26778         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26779         //{
26780             // We must check the elements firstly using the Id and then the name.
26781         var oTextarea = document.getElementById( this.getId() );
26782         
26783         var colElementsByName = document.getElementsByName( this.getId() ) ;
26784          
26785         oTextarea.style.display = 'none' ;
26786
26787         if ( oTextarea.tabIndex ) {            
26788             this.TabIndex = oTextarea.tabIndex ;
26789         }
26790         
26791         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26792         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26793         this.frame = Roo.get(this.getId() + '___Frame')
26794     },
26795     
26796     _getConfigHtml : function()
26797     {
26798         var sConfig = '' ;
26799
26800         for ( var o in this.fckconfig ) {
26801             sConfig += sConfig.length > 0  ? '&amp;' : '';
26802             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26803         }
26804
26805         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26806     },
26807     
26808     
26809     _getIFrameHtml : function()
26810     {
26811         var sFile = 'fckeditor.html' ;
26812         /* no idea what this is about..
26813         try
26814         {
26815             if ( (/fcksource=true/i).test( window.top.location.search ) )
26816                 sFile = 'fckeditor.original.html' ;
26817         }
26818         catch (e) { 
26819         */
26820
26821         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26822         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26823         
26824         
26825         var html = '<iframe id="' + this.getId() +
26826             '___Frame" src="' + sLink +
26827             '" width="' + this.width +
26828             '" height="' + this.height + '"' +
26829             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26830             ' frameborder="0" scrolling="no"></iframe>' ;
26831
26832         return html ;
26833     },
26834     
26835     _insertHtmlBefore : function( html, element )
26836     {
26837         if ( element.insertAdjacentHTML )       {
26838             // IE
26839             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26840         } else { // Gecko
26841             var oRange = document.createRange() ;
26842             oRange.setStartBefore( element ) ;
26843             var oFragment = oRange.createContextualFragment( html );
26844             element.parentNode.insertBefore( oFragment, element ) ;
26845         }
26846     }
26847     
26848     
26849   
26850     
26851     
26852     
26853     
26854
26855 });
26856
26857 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26858
26859 function FCKeditor_OnComplete(editorInstance){
26860     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26861     f.fckEditor = editorInstance;
26862     //console.log("loaded");
26863     f.fireEvent('editorinit', f, editorInstance);
26864
26865   
26866
26867  
26868
26869
26870
26871
26872
26873
26874
26875
26876
26877
26878
26879
26880
26881
26882
26883 //<script type="text/javascript">
26884 /**
26885  * @class Roo.form.GridField
26886  * @extends Roo.form.Field
26887  * Embed a grid (or editable grid into a form)
26888  * STATUS ALPHA
26889  * 
26890  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26891  * it needs 
26892  * xgrid.store = Roo.data.Store
26893  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26894  * xgrid.store.reader = Roo.data.JsonReader 
26895  * 
26896  * 
26897  * @constructor
26898  * Creates a new GridField
26899  * @param {Object} config Configuration options
26900  */
26901 Roo.form.GridField = function(config){
26902     Roo.form.GridField.superclass.constructor.call(this, config);
26903      
26904 };
26905
26906 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26907     /**
26908      * @cfg {Number} width  - used to restrict width of grid..
26909      */
26910     width : 100,
26911     /**
26912      * @cfg {Number} height - used to restrict height of grid..
26913      */
26914     height : 50,
26915      /**
26916      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26917          * 
26918          *}
26919      */
26920     xgrid : false, 
26921     /**
26922      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26923      * {tag: "input", type: "checkbox", autocomplete: "off"})
26924      */
26925    // defaultAutoCreate : { tag: 'div' },
26926     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26927     /**
26928      * @cfg {String} addTitle Text to include for adding a title.
26929      */
26930     addTitle : false,
26931     //
26932     onResize : function(){
26933         Roo.form.Field.superclass.onResize.apply(this, arguments);
26934     },
26935
26936     initEvents : function(){
26937         // Roo.form.Checkbox.superclass.initEvents.call(this);
26938         // has no events...
26939        
26940     },
26941
26942
26943     getResizeEl : function(){
26944         return this.wrap;
26945     },
26946
26947     getPositionEl : function(){
26948         return this.wrap;
26949     },
26950
26951     // private
26952     onRender : function(ct, position){
26953         
26954         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26955         var style = this.style;
26956         delete this.style;
26957         
26958         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26959         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26960         this.viewEl = this.wrap.createChild({ tag: 'div' });
26961         if (style) {
26962             this.viewEl.applyStyles(style);
26963         }
26964         if (this.width) {
26965             this.viewEl.setWidth(this.width);
26966         }
26967         if (this.height) {
26968             this.viewEl.setHeight(this.height);
26969         }
26970         //if(this.inputValue !== undefined){
26971         //this.setValue(this.value);
26972         
26973         
26974         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26975         
26976         
26977         this.grid.render();
26978         this.grid.getDataSource().on('remove', this.refreshValue, this);
26979         this.grid.getDataSource().on('update', this.refreshValue, this);
26980         this.grid.on('afteredit', this.refreshValue, this);
26981  
26982     },
26983      
26984     
26985     /**
26986      * Sets the value of the item. 
26987      * @param {String} either an object  or a string..
26988      */
26989     setValue : function(v){
26990         //this.value = v;
26991         v = v || []; // empty set..
26992         // this does not seem smart - it really only affects memoryproxy grids..
26993         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26994             var ds = this.grid.getDataSource();
26995             // assumes a json reader..
26996             var data = {}
26997             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26998             ds.loadData( data);
26999         }
27000         // clear selection so it does not get stale.
27001         if (this.grid.sm) { 
27002             this.grid.sm.clearSelections();
27003         }
27004         
27005         Roo.form.GridField.superclass.setValue.call(this, v);
27006         this.refreshValue();
27007         // should load data in the grid really....
27008     },
27009     
27010     // private
27011     refreshValue: function() {
27012          var val = [];
27013         this.grid.getDataSource().each(function(r) {
27014             val.push(r.data);
27015         });
27016         this.el.dom.value = Roo.encode(val);
27017     }
27018     
27019      
27020     
27021     
27022 });/*
27023  * Based on:
27024  * Ext JS Library 1.1.1
27025  * Copyright(c) 2006-2007, Ext JS, LLC.
27026  *
27027  * Originally Released Under LGPL - original licence link has changed is not relivant.
27028  *
27029  * Fork - LGPL
27030  * <script type="text/javascript">
27031  */
27032 /**
27033  * @class Roo.form.DisplayField
27034  * @extends Roo.form.Field
27035  * A generic Field to display non-editable data.
27036  * @cfg {Boolean} closable (true|false) default false
27037  * @constructor
27038  * Creates a new Display Field item.
27039  * @param {Object} config Configuration options
27040  */
27041 Roo.form.DisplayField = function(config){
27042     Roo.form.DisplayField.superclass.constructor.call(this, config);
27043     
27044     this.addEvents({
27045         /**
27046          * @event close
27047          * Fires after the click the close btn
27048              * @param {Roo.form.DisplayField} this
27049              */
27050         close : true
27051     });
27052 };
27053
27054 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27055     inputType:      'hidden',
27056     allowBlank:     true,
27057     readOnly:         true,
27058     
27059  
27060     /**
27061      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27062      */
27063     focusClass : undefined,
27064     /**
27065      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27066      */
27067     fieldClass: 'x-form-field',
27068     
27069      /**
27070      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27071      */
27072     valueRenderer: undefined,
27073     
27074     width: 100,
27075     /**
27076      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27077      * {tag: "input", type: "checkbox", autocomplete: "off"})
27078      */
27079      
27080  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27081  
27082     closable : false,
27083     
27084     onResize : function(){
27085         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27086         
27087     },
27088
27089     initEvents : function(){
27090         // Roo.form.Checkbox.superclass.initEvents.call(this);
27091         // has no events...
27092         
27093         if(this.closable){
27094             this.closeEl.on('click', this.onClose, this);
27095         }
27096        
27097     },
27098
27099
27100     getResizeEl : function(){
27101         return this.wrap;
27102     },
27103
27104     getPositionEl : function(){
27105         return this.wrap;
27106     },
27107
27108     // private
27109     onRender : function(ct, position){
27110         
27111         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27112         //if(this.inputValue !== undefined){
27113         this.wrap = this.el.wrap();
27114         
27115         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27116         
27117         if(this.closable){
27118             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27119         }
27120         
27121         if (this.bodyStyle) {
27122             this.viewEl.applyStyles(this.bodyStyle);
27123         }
27124         //this.viewEl.setStyle('padding', '2px');
27125         
27126         this.setValue(this.value);
27127         
27128     },
27129 /*
27130     // private
27131     initValue : Roo.emptyFn,
27132
27133   */
27134
27135         // private
27136     onClick : function(){
27137         
27138     },
27139
27140     /**
27141      * Sets the checked state of the checkbox.
27142      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27143      */
27144     setValue : function(v){
27145         this.value = v;
27146         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27147         // this might be called before we have a dom element..
27148         if (!this.viewEl) {
27149             return;
27150         }
27151         this.viewEl.dom.innerHTML = html;
27152         Roo.form.DisplayField.superclass.setValue.call(this, v);
27153
27154     },
27155     
27156     onClose : function(e)
27157     {
27158         e.preventDefault();
27159         
27160         this.fireEvent('close', this);
27161     }
27162 });/*
27163  * 
27164  * Licence- LGPL
27165  * 
27166  */
27167
27168 /**
27169  * @class Roo.form.DayPicker
27170  * @extends Roo.form.Field
27171  * A Day picker show [M] [T] [W] ....
27172  * @constructor
27173  * Creates a new Day Picker
27174  * @param {Object} config Configuration options
27175  */
27176 Roo.form.DayPicker= function(config){
27177     Roo.form.DayPicker.superclass.constructor.call(this, config);
27178      
27179 };
27180
27181 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27182     /**
27183      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27184      */
27185     focusClass : undefined,
27186     /**
27187      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27188      */
27189     fieldClass: "x-form-field",
27190    
27191     /**
27192      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27193      * {tag: "input", type: "checkbox", autocomplete: "off"})
27194      */
27195     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27196     
27197    
27198     actionMode : 'viewEl', 
27199     //
27200     // private
27201  
27202     inputType : 'hidden',
27203     
27204      
27205     inputElement: false, // real input element?
27206     basedOn: false, // ????
27207     
27208     isFormField: true, // not sure where this is needed!!!!
27209
27210     onResize : function(){
27211         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27212         if(!this.boxLabel){
27213             this.el.alignTo(this.wrap, 'c-c');
27214         }
27215     },
27216
27217     initEvents : function(){
27218         Roo.form.Checkbox.superclass.initEvents.call(this);
27219         this.el.on("click", this.onClick,  this);
27220         this.el.on("change", this.onClick,  this);
27221     },
27222
27223
27224     getResizeEl : function(){
27225         return this.wrap;
27226     },
27227
27228     getPositionEl : function(){
27229         return this.wrap;
27230     },
27231
27232     
27233     // private
27234     onRender : function(ct, position){
27235         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27236        
27237         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27238         
27239         var r1 = '<table><tr>';
27240         var r2 = '<tr class="x-form-daypick-icons">';
27241         for (var i=0; i < 7; i++) {
27242             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27243             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27244         }
27245         
27246         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27247         viewEl.select('img').on('click', this.onClick, this);
27248         this.viewEl = viewEl;   
27249         
27250         
27251         // this will not work on Chrome!!!
27252         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27253         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27254         
27255         
27256           
27257
27258     },
27259
27260     // private
27261     initValue : Roo.emptyFn,
27262
27263     /**
27264      * Returns the checked state of the checkbox.
27265      * @return {Boolean} True if checked, else false
27266      */
27267     getValue : function(){
27268         return this.el.dom.value;
27269         
27270     },
27271
27272         // private
27273     onClick : function(e){ 
27274         //this.setChecked(!this.checked);
27275         Roo.get(e.target).toggleClass('x-menu-item-checked');
27276         this.refreshValue();
27277         //if(this.el.dom.checked != this.checked){
27278         //    this.setValue(this.el.dom.checked);
27279        // }
27280     },
27281     
27282     // private
27283     refreshValue : function()
27284     {
27285         var val = '';
27286         this.viewEl.select('img',true).each(function(e,i,n)  {
27287             val += e.is(".x-menu-item-checked") ? String(n) : '';
27288         });
27289         this.setValue(val, true);
27290     },
27291
27292     /**
27293      * Sets the checked state of the checkbox.
27294      * On is always based on a string comparison between inputValue and the param.
27295      * @param {Boolean/String} value - the value to set 
27296      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27297      */
27298     setValue : function(v,suppressEvent){
27299         if (!this.el.dom) {
27300             return;
27301         }
27302         var old = this.el.dom.value ;
27303         this.el.dom.value = v;
27304         if (suppressEvent) {
27305             return ;
27306         }
27307          
27308         // update display..
27309         this.viewEl.select('img',true).each(function(e,i,n)  {
27310             
27311             var on = e.is(".x-menu-item-checked");
27312             var newv = v.indexOf(String(n)) > -1;
27313             if (on != newv) {
27314                 e.toggleClass('x-menu-item-checked');
27315             }
27316             
27317         });
27318         
27319         
27320         this.fireEvent('change', this, v, old);
27321         
27322         
27323     },
27324    
27325     // handle setting of hidden value by some other method!!?!?
27326     setFromHidden: function()
27327     {
27328         if(!this.el){
27329             return;
27330         }
27331         //console.log("SET FROM HIDDEN");
27332         //alert('setFrom hidden');
27333         this.setValue(this.el.dom.value);
27334     },
27335     
27336     onDestroy : function()
27337     {
27338         if(this.viewEl){
27339             Roo.get(this.viewEl).remove();
27340         }
27341          
27342         Roo.form.DayPicker.superclass.onDestroy.call(this);
27343     }
27344
27345 });/*
27346  * RooJS Library 1.1.1
27347  * Copyright(c) 2008-2011  Alan Knowles
27348  *
27349  * License - LGPL
27350  */
27351  
27352
27353 /**
27354  * @class Roo.form.ComboCheck
27355  * @extends Roo.form.ComboBox
27356  * A combobox for multiple select items.
27357  *
27358  * FIXME - could do with a reset button..
27359  * 
27360  * @constructor
27361  * Create a new ComboCheck
27362  * @param {Object} config Configuration options
27363  */
27364 Roo.form.ComboCheck = function(config){
27365     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27366     // should verify some data...
27367     // like
27368     // hiddenName = required..
27369     // displayField = required
27370     // valudField == required
27371     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27372     var _t = this;
27373     Roo.each(req, function(e) {
27374         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27375             throw "Roo.form.ComboCheck : missing value for: " + e;
27376         }
27377     });
27378     
27379     
27380 };
27381
27382 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27383      
27384      
27385     editable : false,
27386      
27387     selectedClass: 'x-menu-item-checked', 
27388     
27389     // private
27390     onRender : function(ct, position){
27391         var _t = this;
27392         
27393         
27394         
27395         if(!this.tpl){
27396             var cls = 'x-combo-list';
27397
27398             
27399             this.tpl =  new Roo.Template({
27400                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27401                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27402                    '<span>{' + this.displayField + '}</span>' +
27403                     '</div>' 
27404                 
27405             });
27406         }
27407  
27408         
27409         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27410         this.view.singleSelect = false;
27411         this.view.multiSelect = true;
27412         this.view.toggleSelect = true;
27413         this.pageTb.add(new Roo.Toolbar.Fill(), {
27414             
27415             text: 'Done',
27416             handler: function()
27417             {
27418                 _t.collapse();
27419             }
27420         });
27421     },
27422     
27423     onViewOver : function(e, t){
27424         // do nothing...
27425         return;
27426         
27427     },
27428     
27429     onViewClick : function(doFocus,index){
27430         return;
27431         
27432     },
27433     select: function () {
27434         //Roo.log("SELECT CALLED");
27435     },
27436      
27437     selectByValue : function(xv, scrollIntoView){
27438         var ar = this.getValueArray();
27439         var sels = [];
27440         
27441         Roo.each(ar, function(v) {
27442             if(v === undefined || v === null){
27443                 return;
27444             }
27445             var r = this.findRecord(this.valueField, v);
27446             if(r){
27447                 sels.push(this.store.indexOf(r))
27448                 
27449             }
27450         },this);
27451         this.view.select(sels);
27452         return false;
27453     },
27454     
27455     
27456     
27457     onSelect : function(record, index){
27458        // Roo.log("onselect Called");
27459        // this is only called by the clear button now..
27460         this.view.clearSelections();
27461         this.setValue('[]');
27462         if (this.value != this.valueBefore) {
27463             this.fireEvent('change', this, this.value, this.valueBefore);
27464             this.valueBefore = this.value;
27465         }
27466     },
27467     getValueArray : function()
27468     {
27469         var ar = [] ;
27470         
27471         try {
27472             //Roo.log(this.value);
27473             if (typeof(this.value) == 'undefined') {
27474                 return [];
27475             }
27476             var ar = Roo.decode(this.value);
27477             return  ar instanceof Array ? ar : []; //?? valid?
27478             
27479         } catch(e) {
27480             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27481             return [];
27482         }
27483          
27484     },
27485     expand : function ()
27486     {
27487         
27488         Roo.form.ComboCheck.superclass.expand.call(this);
27489         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27490         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27491         
27492
27493     },
27494     
27495     collapse : function(){
27496         Roo.form.ComboCheck.superclass.collapse.call(this);
27497         var sl = this.view.getSelectedIndexes();
27498         var st = this.store;
27499         var nv = [];
27500         var tv = [];
27501         var r;
27502         Roo.each(sl, function(i) {
27503             r = st.getAt(i);
27504             nv.push(r.get(this.valueField));
27505         },this);
27506         this.setValue(Roo.encode(nv));
27507         if (this.value != this.valueBefore) {
27508
27509             this.fireEvent('change', this, this.value, this.valueBefore);
27510             this.valueBefore = this.value;
27511         }
27512         
27513     },
27514     
27515     setValue : function(v){
27516         // Roo.log(v);
27517         this.value = v;
27518         
27519         var vals = this.getValueArray();
27520         var tv = [];
27521         Roo.each(vals, function(k) {
27522             var r = this.findRecord(this.valueField, k);
27523             if(r){
27524                 tv.push(r.data[this.displayField]);
27525             }else if(this.valueNotFoundText !== undefined){
27526                 tv.push( this.valueNotFoundText );
27527             }
27528         },this);
27529        // Roo.log(tv);
27530         
27531         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27532         this.hiddenField.value = v;
27533         this.value = v;
27534     }
27535     
27536 });/*
27537  * Based on:
27538  * Ext JS Library 1.1.1
27539  * Copyright(c) 2006-2007, Ext JS, LLC.
27540  *
27541  * Originally Released Under LGPL - original licence link has changed is not relivant.
27542  *
27543  * Fork - LGPL
27544  * <script type="text/javascript">
27545  */
27546  
27547 /**
27548  * @class Roo.form.Signature
27549  * @extends Roo.form.Field
27550  * Signature field.  
27551  * @constructor
27552  * 
27553  * @param {Object} config Configuration options
27554  */
27555
27556 Roo.form.Signature = function(config){
27557     Roo.form.Signature.superclass.constructor.call(this, config);
27558     
27559     this.addEvents({// not in used??
27560          /**
27561          * @event confirm
27562          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27563              * @param {Roo.form.Signature} combo This combo box
27564              */
27565         'confirm' : true,
27566         /**
27567          * @event reset
27568          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27569              * @param {Roo.form.ComboBox} combo This combo box
27570              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27571              */
27572         'reset' : true
27573     });
27574 };
27575
27576 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27577     /**
27578      * @cfg {Object} labels Label to use when rendering a form.
27579      * defaults to 
27580      * labels : { 
27581      *      clear : "Clear",
27582      *      confirm : "Confirm"
27583      *  }
27584      */
27585     labels : { 
27586         clear : "Clear",
27587         confirm : "Confirm"
27588     },
27589     /**
27590      * @cfg {Number} width The signature panel width (defaults to 300)
27591      */
27592     width: 300,
27593     /**
27594      * @cfg {Number} height The signature panel height (defaults to 100)
27595      */
27596     height : 100,
27597     /**
27598      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27599      */
27600     allowBlank : false,
27601     
27602     //private
27603     // {Object} signPanel The signature SVG panel element (defaults to {})
27604     signPanel : {},
27605     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27606     isMouseDown : false,
27607     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27608     isConfirmed : false,
27609     // {String} signatureTmp SVG mapping string (defaults to empty string)
27610     signatureTmp : '',
27611     
27612     
27613     defaultAutoCreate : { // modified by initCompnoent..
27614         tag: "input",
27615         type:"hidden"
27616     },
27617
27618     // private
27619     onRender : function(ct, position){
27620         
27621         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27622         
27623         this.wrap = this.el.wrap({
27624             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27625         });
27626         
27627         this.createToolbar(this);
27628         this.signPanel = this.wrap.createChild({
27629                 tag: 'div',
27630                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27631             }, this.el
27632         );
27633             
27634         this.svgID = Roo.id();
27635         this.svgEl = this.signPanel.createChild({
27636               xmlns : 'http://www.w3.org/2000/svg',
27637               tag : 'svg',
27638               id : this.svgID + "-svg",
27639               width: this.width,
27640               height: this.height,
27641               viewBox: '0 0 '+this.width+' '+this.height,
27642               cn : [
27643                 {
27644                     tag: "rect",
27645                     id: this.svgID + "-svg-r",
27646                     width: this.width,
27647                     height: this.height,
27648                     fill: "#ffa"
27649                 },
27650                 {
27651                     tag: "line",
27652                     id: this.svgID + "-svg-l",
27653                     x1: "0", // start
27654                     y1: (this.height*0.8), // start set the line in 80% of height
27655                     x2: this.width, // end
27656                     y2: (this.height*0.8), // end set the line in 80% of height
27657                     'stroke': "#666",
27658                     'stroke-width': "1",
27659                     'stroke-dasharray': "3",
27660                     'shape-rendering': "crispEdges",
27661                     'pointer-events': "none"
27662                 },
27663                 {
27664                     tag: "path",
27665                     id: this.svgID + "-svg-p",
27666                     'stroke': "navy",
27667                     'stroke-width': "3",
27668                     'fill': "none",
27669                     'pointer-events': 'none'
27670                 }
27671               ]
27672         });
27673         this.createSVG();
27674         this.svgBox = this.svgEl.dom.getScreenCTM();
27675     },
27676     createSVG : function(){ 
27677         var svg = this.signPanel;
27678         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27679         var t = this;
27680
27681         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27682         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27683         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27684         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27685         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27686         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27687         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27688         
27689     },
27690     isTouchEvent : function(e){
27691         return e.type.match(/^touch/);
27692     },
27693     getCoords : function (e) {
27694         var pt    = this.svgEl.dom.createSVGPoint();
27695         pt.x = e.clientX; 
27696         pt.y = e.clientY;
27697         if (this.isTouchEvent(e)) {
27698             pt.x =  e.targetTouches[0].clientX;
27699             pt.y = e.targetTouches[0].clientY;
27700         }
27701         var a = this.svgEl.dom.getScreenCTM();
27702         var b = a.inverse();
27703         var mx = pt.matrixTransform(b);
27704         return mx.x + ',' + mx.y;
27705     },
27706     //mouse event headler 
27707     down : function (e) {
27708         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27709         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27710         
27711         this.isMouseDown = true;
27712         
27713         e.preventDefault();
27714     },
27715     move : function (e) {
27716         if (this.isMouseDown) {
27717             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27718             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27719         }
27720         
27721         e.preventDefault();
27722     },
27723     up : function (e) {
27724         this.isMouseDown = false;
27725         var sp = this.signatureTmp.split(' ');
27726         
27727         if(sp.length > 1){
27728             if(!sp[sp.length-2].match(/^L/)){
27729                 sp.pop();
27730                 sp.pop();
27731                 sp.push("");
27732                 this.signatureTmp = sp.join(" ");
27733             }
27734         }
27735         if(this.getValue() != this.signatureTmp){
27736             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27737             this.isConfirmed = false;
27738         }
27739         e.preventDefault();
27740     },
27741     
27742     /**
27743      * Protected method that will not generally be called directly. It
27744      * is called when the editor creates its toolbar. Override this method if you need to
27745      * add custom toolbar buttons.
27746      * @param {HtmlEditor} editor
27747      */
27748     createToolbar : function(editor){
27749          function btn(id, toggle, handler){
27750             var xid = fid + '-'+ id ;
27751             return {
27752                 id : xid,
27753                 cmd : id,
27754                 cls : 'x-btn-icon x-edit-'+id,
27755                 enableToggle:toggle !== false,
27756                 scope: editor, // was editor...
27757                 handler:handler||editor.relayBtnCmd,
27758                 clickEvent:'mousedown',
27759                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27760                 tabIndex:-1
27761             };
27762         }
27763         
27764         
27765         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27766         this.tb = tb;
27767         this.tb.add(
27768            {
27769                 cls : ' x-signature-btn x-signature-'+id,
27770                 scope: editor, // was editor...
27771                 handler: this.reset,
27772                 clickEvent:'mousedown',
27773                 text: this.labels.clear
27774             },
27775             {
27776                  xtype : 'Fill',
27777                  xns: Roo.Toolbar
27778             }, 
27779             {
27780                 cls : '  x-signature-btn x-signature-'+id,
27781                 scope: editor, // was editor...
27782                 handler: this.confirmHandler,
27783                 clickEvent:'mousedown',
27784                 text: this.labels.confirm
27785             }
27786         );
27787     
27788     },
27789     //public
27790     /**
27791      * when user is clicked confirm then show this image.....
27792      * 
27793      * @return {String} Image Data URI
27794      */
27795     getImageDataURI : function(){
27796         var svg = this.svgEl.dom.parentNode.innerHTML;
27797         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27798         return src; 
27799     },
27800     /**
27801      * 
27802      * @return {Boolean} this.isConfirmed
27803      */
27804     getConfirmed : function(){
27805         return this.isConfirmed;
27806     },
27807     /**
27808      * 
27809      * @return {Number} this.width
27810      */
27811     getWidth : function(){
27812         return this.width;
27813     },
27814     /**
27815      * 
27816      * @return {Number} this.height
27817      */
27818     getHeight : function(){
27819         return this.height;
27820     },
27821     // private
27822     getSignature : function(){
27823         return this.signatureTmp;
27824     },
27825     // private
27826     reset : function(){
27827         this.signatureTmp = '';
27828         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27829         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27830         this.isConfirmed = false;
27831         Roo.form.Signature.superclass.reset.call(this);
27832     },
27833     setSignature : function(s){
27834         this.signatureTmp = s;
27835         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27836         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27837         this.setValue(s);
27838         this.isConfirmed = false;
27839         Roo.form.Signature.superclass.reset.call(this);
27840     }, 
27841     test : function(){
27842 //        Roo.log(this.signPanel.dom.contentWindow.up())
27843     },
27844     //private
27845     setConfirmed : function(){
27846         
27847         
27848         
27849 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27850     },
27851     // private
27852     confirmHandler : function(){
27853         if(!this.getSignature()){
27854             return;
27855         }
27856         
27857         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27858         this.setValue(this.getSignature());
27859         this.isConfirmed = true;
27860         
27861         this.fireEvent('confirm', this);
27862     },
27863     // private
27864     // Subclasses should provide the validation implementation by overriding this
27865     validateValue : function(value){
27866         if(this.allowBlank){
27867             return true;
27868         }
27869         
27870         if(this.isConfirmed){
27871             return true;
27872         }
27873         return false;
27874     }
27875 });/*
27876  * Based on:
27877  * Ext JS Library 1.1.1
27878  * Copyright(c) 2006-2007, Ext JS, LLC.
27879  *
27880  * Originally Released Under LGPL - original licence link has changed is not relivant.
27881  *
27882  * Fork - LGPL
27883  * <script type="text/javascript">
27884  */
27885  
27886
27887 /**
27888  * @class Roo.form.ComboBox
27889  * @extends Roo.form.TriggerField
27890  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27891  * @constructor
27892  * Create a new ComboBox.
27893  * @param {Object} config Configuration options
27894  */
27895 Roo.form.Select = function(config){
27896     Roo.form.Select.superclass.constructor.call(this, config);
27897      
27898 };
27899
27900 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27901     /**
27902      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27903      */
27904     /**
27905      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27906      * rendering into an Roo.Editor, defaults to false)
27907      */
27908     /**
27909      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27910      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27911      */
27912     /**
27913      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27914      */
27915     /**
27916      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27917      * the dropdown list (defaults to undefined, with no header element)
27918      */
27919
27920      /**
27921      * @cfg {String/Roo.Template} tpl The template to use to render the output
27922      */
27923      
27924     // private
27925     defaultAutoCreate : {tag: "select"  },
27926     /**
27927      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27928      */
27929     listWidth: undefined,
27930     /**
27931      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27932      * mode = 'remote' or 'text' if mode = 'local')
27933      */
27934     displayField: undefined,
27935     /**
27936      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27937      * mode = 'remote' or 'value' if mode = 'local'). 
27938      * Note: use of a valueField requires the user make a selection
27939      * in order for a value to be mapped.
27940      */
27941     valueField: undefined,
27942     
27943     
27944     /**
27945      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27946      * field's data value (defaults to the underlying DOM element's name)
27947      */
27948     hiddenName: undefined,
27949     /**
27950      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27951      */
27952     listClass: '',
27953     /**
27954      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27955      */
27956     selectedClass: 'x-combo-selected',
27957     /**
27958      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27959      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27960      * which displays a downward arrow icon).
27961      */
27962     triggerClass : 'x-form-arrow-trigger',
27963     /**
27964      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27965      */
27966     shadow:'sides',
27967     /**
27968      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27969      * anchor positions (defaults to 'tl-bl')
27970      */
27971     listAlign: 'tl-bl?',
27972     /**
27973      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27974      */
27975     maxHeight: 300,
27976     /**
27977      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27978      * query specified by the allQuery config option (defaults to 'query')
27979      */
27980     triggerAction: 'query',
27981     /**
27982      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27983      * (defaults to 4, does not apply if editable = false)
27984      */
27985     minChars : 4,
27986     /**
27987      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27988      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27989      */
27990     typeAhead: false,
27991     /**
27992      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27993      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27994      */
27995     queryDelay: 500,
27996     /**
27997      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27998      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27999      */
28000     pageSize: 0,
28001     /**
28002      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28003      * when editable = true (defaults to false)
28004      */
28005     selectOnFocus:false,
28006     /**
28007      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28008      */
28009     queryParam: 'query',
28010     /**
28011      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28012      * when mode = 'remote' (defaults to 'Loading...')
28013      */
28014     loadingText: 'Loading...',
28015     /**
28016      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28017      */
28018     resizable: false,
28019     /**
28020      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28021      */
28022     handleHeight : 8,
28023     /**
28024      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28025      * traditional select (defaults to true)
28026      */
28027     editable: true,
28028     /**
28029      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28030      */
28031     allQuery: '',
28032     /**
28033      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28034      */
28035     mode: 'remote',
28036     /**
28037      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28038      * listWidth has a higher value)
28039      */
28040     minListWidth : 70,
28041     /**
28042      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28043      * allow the user to set arbitrary text into the field (defaults to false)
28044      */
28045     forceSelection:false,
28046     /**
28047      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28048      * if typeAhead = true (defaults to 250)
28049      */
28050     typeAheadDelay : 250,
28051     /**
28052      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28053      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28054      */
28055     valueNotFoundText : undefined,
28056     
28057     /**
28058      * @cfg {String} defaultValue The value displayed after loading the store.
28059      */
28060     defaultValue: '',
28061     
28062     /**
28063      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28064      */
28065     blockFocus : false,
28066     
28067     /**
28068      * @cfg {Boolean} disableClear Disable showing of clear button.
28069      */
28070     disableClear : false,
28071     /**
28072      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28073      */
28074     alwaysQuery : false,
28075     
28076     //private
28077     addicon : false,
28078     editicon: false,
28079     
28080     // element that contains real text value.. (when hidden is used..)
28081      
28082     // private
28083     onRender : function(ct, position){
28084         Roo.form.Field.prototype.onRender.call(this, ct, position);
28085         
28086         if(this.store){
28087             this.store.on('beforeload', this.onBeforeLoad, this);
28088             this.store.on('load', this.onLoad, this);
28089             this.store.on('loadexception', this.onLoadException, this);
28090             this.store.load({});
28091         }
28092         
28093         
28094         
28095     },
28096
28097     // private
28098     initEvents : function(){
28099         //Roo.form.ComboBox.superclass.initEvents.call(this);
28100  
28101     },
28102
28103     onDestroy : function(){
28104        
28105         if(this.store){
28106             this.store.un('beforeload', this.onBeforeLoad, this);
28107             this.store.un('load', this.onLoad, this);
28108             this.store.un('loadexception', this.onLoadException, this);
28109         }
28110         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28111     },
28112
28113     // private
28114     fireKey : function(e){
28115         if(e.isNavKeyPress() && !this.list.isVisible()){
28116             this.fireEvent("specialkey", this, e);
28117         }
28118     },
28119
28120     // private
28121     onResize: function(w, h){
28122         
28123         return; 
28124     
28125         
28126     },
28127
28128     /**
28129      * Allow or prevent the user from directly editing the field text.  If false is passed,
28130      * the user will only be able to select from the items defined in the dropdown list.  This method
28131      * is the runtime equivalent of setting the 'editable' config option at config time.
28132      * @param {Boolean} value True to allow the user to directly edit the field text
28133      */
28134     setEditable : function(value){
28135          
28136     },
28137
28138     // private
28139     onBeforeLoad : function(){
28140         
28141         Roo.log("Select before load");
28142         return;
28143     
28144         this.innerList.update(this.loadingText ?
28145                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28146         //this.restrictHeight();
28147         this.selectedIndex = -1;
28148     },
28149
28150     // private
28151     onLoad : function(){
28152
28153     
28154         var dom = this.el.dom;
28155         dom.innerHTML = '';
28156          var od = dom.ownerDocument;
28157          
28158         if (this.emptyText) {
28159             var op = od.createElement('option');
28160             op.setAttribute('value', '');
28161             op.innerHTML = String.format('{0}', this.emptyText);
28162             dom.appendChild(op);
28163         }
28164         if(this.store.getCount() > 0){
28165            
28166             var vf = this.valueField;
28167             var df = this.displayField;
28168             this.store.data.each(function(r) {
28169                 // which colmsn to use... testing - cdoe / title..
28170                 var op = od.createElement('option');
28171                 op.setAttribute('value', r.data[vf]);
28172                 op.innerHTML = String.format('{0}', r.data[df]);
28173                 dom.appendChild(op);
28174             });
28175             if (typeof(this.defaultValue != 'undefined')) {
28176                 this.setValue(this.defaultValue);
28177             }
28178             
28179              
28180         }else{
28181             //this.onEmptyResults();
28182         }
28183         //this.el.focus();
28184     },
28185     // private
28186     onLoadException : function()
28187     {
28188         dom.innerHTML = '';
28189             
28190         Roo.log("Select on load exception");
28191         return;
28192     
28193         this.collapse();
28194         Roo.log(this.store.reader.jsonData);
28195         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28196             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28197         }
28198         
28199         
28200     },
28201     // private
28202     onTypeAhead : function(){
28203          
28204     },
28205
28206     // private
28207     onSelect : function(record, index){
28208         Roo.log('on select?');
28209         return;
28210         if(this.fireEvent('beforeselect', this, record, index) !== false){
28211             this.setFromData(index > -1 ? record.data : false);
28212             this.collapse();
28213             this.fireEvent('select', this, record, index);
28214         }
28215     },
28216
28217     /**
28218      * Returns the currently selected field value or empty string if no value is set.
28219      * @return {String} value The selected value
28220      */
28221     getValue : function(){
28222         var dom = this.el.dom;
28223         this.value = dom.options[dom.selectedIndex].value;
28224         return this.value;
28225         
28226     },
28227
28228     /**
28229      * Clears any text/value currently set in the field
28230      */
28231     clearValue : function(){
28232         this.value = '';
28233         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28234         
28235     },
28236
28237     /**
28238      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28239      * will be displayed in the field.  If the value does not match the data value of an existing item,
28240      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28241      * Otherwise the field will be blank (although the value will still be set).
28242      * @param {String} value The value to match
28243      */
28244     setValue : function(v){
28245         var d = this.el.dom;
28246         for (var i =0; i < d.options.length;i++) {
28247             if (v == d.options[i].value) {
28248                 d.selectedIndex = i;
28249                 this.value = v;
28250                 return;
28251             }
28252         }
28253         this.clearValue();
28254     },
28255     /**
28256      * @property {Object} the last set data for the element
28257      */
28258     
28259     lastData : false,
28260     /**
28261      * Sets the value of the field based on a object which is related to the record format for the store.
28262      * @param {Object} value the value to set as. or false on reset?
28263      */
28264     setFromData : function(o){
28265         Roo.log('setfrom data?');
28266          
28267         
28268         
28269     },
28270     // private
28271     reset : function(){
28272         this.clearValue();
28273     },
28274     // private
28275     findRecord : function(prop, value){
28276         
28277         return false;
28278     
28279         var record;
28280         if(this.store.getCount() > 0){
28281             this.store.each(function(r){
28282                 if(r.data[prop] == value){
28283                     record = r;
28284                     return false;
28285                 }
28286                 return true;
28287             });
28288         }
28289         return record;
28290     },
28291     
28292     getName: function()
28293     {
28294         // returns hidden if it's set..
28295         if (!this.rendered) {return ''};
28296         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28297         
28298     },
28299      
28300
28301     
28302
28303     // private
28304     onEmptyResults : function(){
28305         Roo.log('empty results');
28306         //this.collapse();
28307     },
28308
28309     /**
28310      * Returns true if the dropdown list is expanded, else false.
28311      */
28312     isExpanded : function(){
28313         return false;
28314     },
28315
28316     /**
28317      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28318      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28319      * @param {String} value The data value of the item to select
28320      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28321      * selected item if it is not currently in view (defaults to true)
28322      * @return {Boolean} True if the value matched an item in the list, else false
28323      */
28324     selectByValue : function(v, scrollIntoView){
28325         Roo.log('select By Value');
28326         return false;
28327     
28328         if(v !== undefined && v !== null){
28329             var r = this.findRecord(this.valueField || this.displayField, v);
28330             if(r){
28331                 this.select(this.store.indexOf(r), scrollIntoView);
28332                 return true;
28333             }
28334         }
28335         return false;
28336     },
28337
28338     /**
28339      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28340      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28341      * @param {Number} index The zero-based index of the list item to select
28342      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28343      * selected item if it is not currently in view (defaults to true)
28344      */
28345     select : function(index, scrollIntoView){
28346         Roo.log('select ');
28347         return  ;
28348         
28349         this.selectedIndex = index;
28350         this.view.select(index);
28351         if(scrollIntoView !== false){
28352             var el = this.view.getNode(index);
28353             if(el){
28354                 this.innerList.scrollChildIntoView(el, false);
28355             }
28356         }
28357     },
28358
28359       
28360
28361     // private
28362     validateBlur : function(){
28363         
28364         return;
28365         
28366     },
28367
28368     // private
28369     initQuery : function(){
28370         this.doQuery(this.getRawValue());
28371     },
28372
28373     // private
28374     doForce : function(){
28375         if(this.el.dom.value.length > 0){
28376             this.el.dom.value =
28377                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28378              
28379         }
28380     },
28381
28382     /**
28383      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28384      * query allowing the query action to be canceled if needed.
28385      * @param {String} query The SQL query to execute
28386      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28387      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28388      * saved in the current store (defaults to false)
28389      */
28390     doQuery : function(q, forceAll){
28391         
28392         Roo.log('doQuery?');
28393         if(q === undefined || q === null){
28394             q = '';
28395         }
28396         var qe = {
28397             query: q,
28398             forceAll: forceAll,
28399             combo: this,
28400             cancel:false
28401         };
28402         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28403             return false;
28404         }
28405         q = qe.query;
28406         forceAll = qe.forceAll;
28407         if(forceAll === true || (q.length >= this.minChars)){
28408             if(this.lastQuery != q || this.alwaysQuery){
28409                 this.lastQuery = q;
28410                 if(this.mode == 'local'){
28411                     this.selectedIndex = -1;
28412                     if(forceAll){
28413                         this.store.clearFilter();
28414                     }else{
28415                         this.store.filter(this.displayField, q);
28416                     }
28417                     this.onLoad();
28418                 }else{
28419                     this.store.baseParams[this.queryParam] = q;
28420                     this.store.load({
28421                         params: this.getParams(q)
28422                     });
28423                     this.expand();
28424                 }
28425             }else{
28426                 this.selectedIndex = -1;
28427                 this.onLoad();   
28428             }
28429         }
28430     },
28431
28432     // private
28433     getParams : function(q){
28434         var p = {};
28435         //p[this.queryParam] = q;
28436         if(this.pageSize){
28437             p.start = 0;
28438             p.limit = this.pageSize;
28439         }
28440         return p;
28441     },
28442
28443     /**
28444      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28445      */
28446     collapse : function(){
28447         
28448     },
28449
28450     // private
28451     collapseIf : function(e){
28452         
28453     },
28454
28455     /**
28456      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28457      */
28458     expand : function(){
28459         
28460     } ,
28461
28462     // private
28463      
28464
28465     /** 
28466     * @cfg {Boolean} grow 
28467     * @hide 
28468     */
28469     /** 
28470     * @cfg {Number} growMin 
28471     * @hide 
28472     */
28473     /** 
28474     * @cfg {Number} growMax 
28475     * @hide 
28476     */
28477     /**
28478      * @hide
28479      * @method autoSize
28480      */
28481     
28482     setWidth : function()
28483     {
28484         
28485     },
28486     getResizeEl : function(){
28487         return this.el;
28488     }
28489 });//<script type="text/javasscript">
28490  
28491
28492 /**
28493  * @class Roo.DDView
28494  * A DnD enabled version of Roo.View.
28495  * @param {Element/String} container The Element in which to create the View.
28496  * @param {String} tpl The template string used to create the markup for each element of the View
28497  * @param {Object} config The configuration properties. These include all the config options of
28498  * {@link Roo.View} plus some specific to this class.<br>
28499  * <p>
28500  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28501  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28502  * <p>
28503  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28504 .x-view-drag-insert-above {
28505         border-top:1px dotted #3366cc;
28506 }
28507 .x-view-drag-insert-below {
28508         border-bottom:1px dotted #3366cc;
28509 }
28510 </code></pre>
28511  * 
28512  */
28513  
28514 Roo.DDView = function(container, tpl, config) {
28515     Roo.DDView.superclass.constructor.apply(this, arguments);
28516     this.getEl().setStyle("outline", "0px none");
28517     this.getEl().unselectable();
28518     if (this.dragGroup) {
28519         this.setDraggable(this.dragGroup.split(","));
28520     }
28521     if (this.dropGroup) {
28522         this.setDroppable(this.dropGroup.split(","));
28523     }
28524     if (this.deletable) {
28525         this.setDeletable();
28526     }
28527     this.isDirtyFlag = false;
28528         this.addEvents({
28529                 "drop" : true
28530         });
28531 };
28532
28533 Roo.extend(Roo.DDView, Roo.View, {
28534 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28535 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28536 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28537 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28538
28539         isFormField: true,
28540
28541         reset: Roo.emptyFn,
28542         
28543         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28544
28545         validate: function() {
28546                 return true;
28547         },
28548         
28549         destroy: function() {
28550                 this.purgeListeners();
28551                 this.getEl.removeAllListeners();
28552                 this.getEl().remove();
28553                 if (this.dragZone) {
28554                         if (this.dragZone.destroy) {
28555                                 this.dragZone.destroy();
28556                         }
28557                 }
28558                 if (this.dropZone) {
28559                         if (this.dropZone.destroy) {
28560                                 this.dropZone.destroy();
28561                         }
28562                 }
28563         },
28564
28565 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28566         getName: function() {
28567                 return this.name;
28568         },
28569
28570 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28571         setValue: function(v) {
28572                 if (!this.store) {
28573                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28574                 }
28575                 var data = {};
28576                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28577                 this.store.proxy = new Roo.data.MemoryProxy(data);
28578                 this.store.load();
28579         },
28580
28581 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28582         getValue: function() {
28583                 var result = '(';
28584                 this.store.each(function(rec) {
28585                         result += rec.id + ',';
28586                 });
28587                 return result.substr(0, result.length - 1) + ')';
28588         },
28589         
28590         getIds: function() {
28591                 var i = 0, result = new Array(this.store.getCount());
28592                 this.store.each(function(rec) {
28593                         result[i++] = rec.id;
28594                 });
28595                 return result;
28596         },
28597         
28598         isDirty: function() {
28599                 return this.isDirtyFlag;
28600         },
28601
28602 /**
28603  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28604  *      whole Element becomes the target, and this causes the drop gesture to append.
28605  */
28606     getTargetFromEvent : function(e) {
28607                 var target = e.getTarget();
28608                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28609                 target = target.parentNode;
28610                 }
28611                 if (!target) {
28612                         target = this.el.dom.lastChild || this.el.dom;
28613                 }
28614                 return target;
28615     },
28616
28617 /**
28618  *      Create the drag data which consists of an object which has the property "ddel" as
28619  *      the drag proxy element. 
28620  */
28621     getDragData : function(e) {
28622         var target = this.findItemFromChild(e.getTarget());
28623                 if(target) {
28624                         this.handleSelection(e);
28625                         var selNodes = this.getSelectedNodes();
28626             var dragData = {
28627                 source: this,
28628                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28629                 nodes: selNodes,
28630                 records: []
28631                         };
28632                         var selectedIndices = this.getSelectedIndexes();
28633                         for (var i = 0; i < selectedIndices.length; i++) {
28634                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28635                         }
28636                         if (selNodes.length == 1) {
28637                                 dragData.ddel = target.cloneNode(true); // the div element
28638                         } else {
28639                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28640                                 div.className = 'multi-proxy';
28641                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28642                                         div.appendChild(selNodes[i].cloneNode(true));
28643                                 }
28644                                 dragData.ddel = div;
28645                         }
28646             //console.log(dragData)
28647             //console.log(dragData.ddel.innerHTML)
28648                         return dragData;
28649                 }
28650         //console.log('nodragData')
28651                 return false;
28652     },
28653     
28654 /**     Specify to which ddGroup items in this DDView may be dragged. */
28655     setDraggable: function(ddGroup) {
28656         if (ddGroup instanceof Array) {
28657                 Roo.each(ddGroup, this.setDraggable, this);
28658                 return;
28659         }
28660         if (this.dragZone) {
28661                 this.dragZone.addToGroup(ddGroup);
28662         } else {
28663                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28664                                 containerScroll: true,
28665                                 ddGroup: ddGroup 
28666
28667                         });
28668 //                      Draggability implies selection. DragZone's mousedown selects the element.
28669                         if (!this.multiSelect) { this.singleSelect = true; }
28670
28671 //                      Wire the DragZone's handlers up to methods in *this*
28672                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28673                 }
28674     },
28675
28676 /**     Specify from which ddGroup this DDView accepts drops. */
28677     setDroppable: function(ddGroup) {
28678         if (ddGroup instanceof Array) {
28679                 Roo.each(ddGroup, this.setDroppable, this);
28680                 return;
28681         }
28682         if (this.dropZone) {
28683                 this.dropZone.addToGroup(ddGroup);
28684         } else {
28685                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28686                                 containerScroll: true,
28687                                 ddGroup: ddGroup
28688                         });
28689
28690 //                      Wire the DropZone's handlers up to methods in *this*
28691                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28692                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28693                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28694                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28695                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28696                 }
28697     },
28698
28699 /**     Decide whether to drop above or below a View node. */
28700     getDropPoint : function(e, n, dd){
28701         if (n == this.el.dom) { return "above"; }
28702                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28703                 var c = t + (b - t) / 2;
28704                 var y = Roo.lib.Event.getPageY(e);
28705                 if(y <= c) {
28706                         return "above";
28707                 }else{
28708                         return "below";
28709                 }
28710     },
28711
28712     onNodeEnter : function(n, dd, e, data){
28713                 return false;
28714     },
28715     
28716     onNodeOver : function(n, dd, e, data){
28717                 var pt = this.getDropPoint(e, n, dd);
28718                 // set the insert point style on the target node
28719                 var dragElClass = this.dropNotAllowed;
28720                 if (pt) {
28721                         var targetElClass;
28722                         if (pt == "above"){
28723                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28724                                 targetElClass = "x-view-drag-insert-above";
28725                         } else {
28726                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28727                                 targetElClass = "x-view-drag-insert-below";
28728                         }
28729                         if (this.lastInsertClass != targetElClass){
28730                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28731                                 this.lastInsertClass = targetElClass;
28732                         }
28733                 }
28734                 return dragElClass;
28735         },
28736
28737     onNodeOut : function(n, dd, e, data){
28738                 this.removeDropIndicators(n);
28739     },
28740
28741     onNodeDrop : function(n, dd, e, data){
28742         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28743                 return false;
28744         }
28745         var pt = this.getDropPoint(e, n, dd);
28746                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28747                 if (pt == "below") { insertAt++; }
28748                 for (var i = 0; i < data.records.length; i++) {
28749                         var r = data.records[i];
28750                         var dup = this.store.getById(r.id);
28751                         if (dup && (dd != this.dragZone)) {
28752                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28753                         } else {
28754                                 if (data.copy) {
28755                                         this.store.insert(insertAt++, r.copy());
28756                                 } else {
28757                                         data.source.isDirtyFlag = true;
28758                                         r.store.remove(r);
28759                                         this.store.insert(insertAt++, r);
28760                                 }
28761                                 this.isDirtyFlag = true;
28762                         }
28763                 }
28764                 this.dragZone.cachedTarget = null;
28765                 return true;
28766     },
28767
28768     removeDropIndicators : function(n){
28769                 if(n){
28770                         Roo.fly(n).removeClass([
28771                                 "x-view-drag-insert-above",
28772                                 "x-view-drag-insert-below"]);
28773                         this.lastInsertClass = "_noclass";
28774                 }
28775     },
28776
28777 /**
28778  *      Utility method. Add a delete option to the DDView's context menu.
28779  *      @param {String} imageUrl The URL of the "delete" icon image.
28780  */
28781         setDeletable: function(imageUrl) {
28782                 if (!this.singleSelect && !this.multiSelect) {
28783                         this.singleSelect = true;
28784                 }
28785                 var c = this.getContextMenu();
28786                 this.contextMenu.on("itemclick", function(item) {
28787                         switch (item.id) {
28788                                 case "delete":
28789                                         this.remove(this.getSelectedIndexes());
28790                                         break;
28791                         }
28792                 }, this);
28793                 this.contextMenu.add({
28794                         icon: imageUrl,
28795                         id: "delete",
28796                         text: 'Delete'
28797                 });
28798         },
28799         
28800 /**     Return the context menu for this DDView. */
28801         getContextMenu: function() {
28802                 if (!this.contextMenu) {
28803 //                      Create the View's context menu
28804                         this.contextMenu = new Roo.menu.Menu({
28805                                 id: this.id + "-contextmenu"
28806                         });
28807                         this.el.on("contextmenu", this.showContextMenu, this);
28808                 }
28809                 return this.contextMenu;
28810         },
28811         
28812         disableContextMenu: function() {
28813                 if (this.contextMenu) {
28814                         this.el.un("contextmenu", this.showContextMenu, this);
28815                 }
28816         },
28817
28818         showContextMenu: function(e, item) {
28819         item = this.findItemFromChild(e.getTarget());
28820                 if (item) {
28821                         e.stopEvent();
28822                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28823                         this.contextMenu.showAt(e.getXY());
28824             }
28825     },
28826
28827 /**
28828  *      Remove {@link Roo.data.Record}s at the specified indices.
28829  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28830  */
28831     remove: function(selectedIndices) {
28832                 selectedIndices = [].concat(selectedIndices);
28833                 for (var i = 0; i < selectedIndices.length; i++) {
28834                         var rec = this.store.getAt(selectedIndices[i]);
28835                         this.store.remove(rec);
28836                 }
28837     },
28838
28839 /**
28840  *      Double click fires the event, but also, if this is draggable, and there is only one other
28841  *      related DropZone, it transfers the selected node.
28842  */
28843     onDblClick : function(e){
28844         var item = this.findItemFromChild(e.getTarget());
28845         if(item){
28846             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28847                 return false;
28848             }
28849             if (this.dragGroup) {
28850                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28851                     while (targets.indexOf(this.dropZone) > -1) {
28852                             targets.remove(this.dropZone);
28853                                 }
28854                     if (targets.length == 1) {
28855                                         this.dragZone.cachedTarget = null;
28856                         var el = Roo.get(targets[0].getEl());
28857                         var box = el.getBox(true);
28858                         targets[0].onNodeDrop(el.dom, {
28859                                 target: el.dom,
28860                                 xy: [box.x, box.y + box.height - 1]
28861                         }, null, this.getDragData(e));
28862                     }
28863                 }
28864         }
28865     },
28866     
28867     handleSelection: function(e) {
28868                 this.dragZone.cachedTarget = null;
28869         var item = this.findItemFromChild(e.getTarget());
28870         if (!item) {
28871                 this.clearSelections(true);
28872                 return;
28873         }
28874                 if (item && (this.multiSelect || this.singleSelect)){
28875                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28876                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28877                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28878                                 this.unselect(item);
28879                         } else {
28880                                 this.select(item, this.multiSelect && e.ctrlKey);
28881                                 this.lastSelection = item;
28882                         }
28883                 }
28884     },
28885
28886     onItemClick : function(item, index, e){
28887                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28888                         return false;
28889                 }
28890                 return true;
28891     },
28892
28893     unselect : function(nodeInfo, suppressEvent){
28894                 var node = this.getNode(nodeInfo);
28895                 if(node && this.isSelected(node)){
28896                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28897                                 Roo.fly(node).removeClass(this.selectedClass);
28898                                 this.selections.remove(node);
28899                                 if(!suppressEvent){
28900                                         this.fireEvent("selectionchange", this, this.selections);
28901                                 }
28902                         }
28903                 }
28904     }
28905 });
28906 /*
28907  * Based on:
28908  * Ext JS Library 1.1.1
28909  * Copyright(c) 2006-2007, Ext JS, LLC.
28910  *
28911  * Originally Released Under LGPL - original licence link has changed is not relivant.
28912  *
28913  * Fork - LGPL
28914  * <script type="text/javascript">
28915  */
28916  
28917 /**
28918  * @class Roo.LayoutManager
28919  * @extends Roo.util.Observable
28920  * Base class for layout managers.
28921  */
28922 Roo.LayoutManager = function(container, config){
28923     Roo.LayoutManager.superclass.constructor.call(this);
28924     this.el = Roo.get(container);
28925     // ie scrollbar fix
28926     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28927         document.body.scroll = "no";
28928     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28929         this.el.position('relative');
28930     }
28931     this.id = this.el.id;
28932     this.el.addClass("x-layout-container");
28933     /** false to disable window resize monitoring @type Boolean */
28934     this.monitorWindowResize = true;
28935     this.regions = {};
28936     this.addEvents({
28937         /**
28938          * @event layout
28939          * Fires when a layout is performed. 
28940          * @param {Roo.LayoutManager} this
28941          */
28942         "layout" : true,
28943         /**
28944          * @event regionresized
28945          * Fires when the user resizes a region. 
28946          * @param {Roo.LayoutRegion} region The resized region
28947          * @param {Number} newSize The new size (width for east/west, height for north/south)
28948          */
28949         "regionresized" : true,
28950         /**
28951          * @event regioncollapsed
28952          * Fires when a region is collapsed. 
28953          * @param {Roo.LayoutRegion} region The collapsed region
28954          */
28955         "regioncollapsed" : true,
28956         /**
28957          * @event regionexpanded
28958          * Fires when a region is expanded.  
28959          * @param {Roo.LayoutRegion} region The expanded region
28960          */
28961         "regionexpanded" : true
28962     });
28963     this.updating = false;
28964     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28965 };
28966
28967 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28968     /**
28969      * Returns true if this layout is currently being updated
28970      * @return {Boolean}
28971      */
28972     isUpdating : function(){
28973         return this.updating; 
28974     },
28975     
28976     /**
28977      * Suspend the LayoutManager from doing auto-layouts while
28978      * making multiple add or remove calls
28979      */
28980     beginUpdate : function(){
28981         this.updating = true;    
28982     },
28983     
28984     /**
28985      * Restore auto-layouts and optionally disable the manager from performing a layout
28986      * @param {Boolean} noLayout true to disable a layout update 
28987      */
28988     endUpdate : function(noLayout){
28989         this.updating = false;
28990         if(!noLayout){
28991             this.layout();
28992         }    
28993     },
28994     
28995     layout: function(){
28996         
28997     },
28998     
28999     onRegionResized : function(region, newSize){
29000         this.fireEvent("regionresized", region, newSize);
29001         this.layout();
29002     },
29003     
29004     onRegionCollapsed : function(region){
29005         this.fireEvent("regioncollapsed", region);
29006     },
29007     
29008     onRegionExpanded : function(region){
29009         this.fireEvent("regionexpanded", region);
29010     },
29011         
29012     /**
29013      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29014      * performs box-model adjustments.
29015      * @return {Object} The size as an object {width: (the width), height: (the height)}
29016      */
29017     getViewSize : function(){
29018         var size;
29019         if(this.el.dom != document.body){
29020             size = this.el.getSize();
29021         }else{
29022             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29023         }
29024         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29025         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29026         return size;
29027     },
29028     
29029     /**
29030      * Returns the Element this layout is bound to.
29031      * @return {Roo.Element}
29032      */
29033     getEl : function(){
29034         return this.el;
29035     },
29036     
29037     /**
29038      * Returns the specified region.
29039      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29040      * @return {Roo.LayoutRegion}
29041      */
29042     getRegion : function(target){
29043         return this.regions[target.toLowerCase()];
29044     },
29045     
29046     onWindowResize : function(){
29047         if(this.monitorWindowResize){
29048             this.layout();
29049         }
29050     }
29051 });/*
29052  * Based on:
29053  * Ext JS Library 1.1.1
29054  * Copyright(c) 2006-2007, Ext JS, LLC.
29055  *
29056  * Originally Released Under LGPL - original licence link has changed is not relivant.
29057  *
29058  * Fork - LGPL
29059  * <script type="text/javascript">
29060  */
29061 /**
29062  * @class Roo.BorderLayout
29063  * @extends Roo.LayoutManager
29064  * @children Roo.ContentPanel
29065  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29066  * please see: <br><br>
29067  * <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>
29068  * <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>
29069  * Example:
29070  <pre><code>
29071  var layout = new Roo.BorderLayout(document.body, {
29072     north: {
29073         initialSize: 25,
29074         titlebar: false
29075     },
29076     west: {
29077         split:true,
29078         initialSize: 200,
29079         minSize: 175,
29080         maxSize: 400,
29081         titlebar: true,
29082         collapsible: true
29083     },
29084     east: {
29085         split:true,
29086         initialSize: 202,
29087         minSize: 175,
29088         maxSize: 400,
29089         titlebar: true,
29090         collapsible: true
29091     },
29092     south: {
29093         split:true,
29094         initialSize: 100,
29095         minSize: 100,
29096         maxSize: 200,
29097         titlebar: true,
29098         collapsible: true
29099     },
29100     center: {
29101         titlebar: true,
29102         autoScroll:true,
29103         resizeTabs: true,
29104         minTabWidth: 50,
29105         preferredTabWidth: 150
29106     }
29107 });
29108
29109 // shorthand
29110 var CP = Roo.ContentPanel;
29111
29112 layout.beginUpdate();
29113 layout.add("north", new CP("north", "North"));
29114 layout.add("south", new CP("south", {title: "South", closable: true}));
29115 layout.add("west", new CP("west", {title: "West"}));
29116 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29117 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29118 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29119 layout.getRegion("center").showPanel("center1");
29120 layout.endUpdate();
29121 </code></pre>
29122
29123 <b>The container the layout is rendered into can be either the body element or any other element.
29124 If it is not the body element, the container needs to either be an absolute positioned element,
29125 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29126 the container size if it is not the body element.</b>
29127
29128 * @constructor
29129 * Create a new BorderLayout
29130 * @param {String/HTMLElement/Element} container The container this layout is bound to
29131 * @param {Object} config Configuration options
29132  */
29133 Roo.BorderLayout = function(container, config){
29134     config = config || {};
29135     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29136     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29137     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29138         var target = this.factory.validRegions[i];
29139         if(config[target]){
29140             this.addRegion(target, config[target]);
29141         }
29142     }
29143 };
29144
29145 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29146         
29147         /**
29148          * @cfg {Roo.LayoutRegion} east
29149          */
29150         /**
29151          * @cfg {Roo.LayoutRegion} west
29152          */
29153         /**
29154          * @cfg {Roo.LayoutRegion} north
29155          */
29156         /**
29157          * @cfg {Roo.LayoutRegion} south
29158          */
29159         /**
29160          * @cfg {Roo.LayoutRegion} center
29161          */
29162     /**
29163      * Creates and adds a new region if it doesn't already exist.
29164      * @param {String} target The target region key (north, south, east, west or center).
29165      * @param {Object} config The regions config object
29166      * @return {BorderLayoutRegion} The new region
29167      */
29168     addRegion : function(target, config){
29169         if(!this.regions[target]){
29170             var r = this.factory.create(target, this, config);
29171             this.bindRegion(target, r);
29172         }
29173         return this.regions[target];
29174     },
29175
29176     // private (kinda)
29177     bindRegion : function(name, r){
29178         this.regions[name] = r;
29179         r.on("visibilitychange", this.layout, this);
29180         r.on("paneladded", this.layout, this);
29181         r.on("panelremoved", this.layout, this);
29182         r.on("invalidated", this.layout, this);
29183         r.on("resized", this.onRegionResized, this);
29184         r.on("collapsed", this.onRegionCollapsed, this);
29185         r.on("expanded", this.onRegionExpanded, this);
29186     },
29187
29188     /**
29189      * Performs a layout update.
29190      */
29191     layout : function(){
29192         if(this.updating) {
29193             return;
29194         }
29195         var size = this.getViewSize();
29196         var w = size.width;
29197         var h = size.height;
29198         var centerW = w;
29199         var centerH = h;
29200         var centerY = 0;
29201         var centerX = 0;
29202         //var x = 0, y = 0;
29203
29204         var rs = this.regions;
29205         var north = rs["north"];
29206         var south = rs["south"]; 
29207         var west = rs["west"];
29208         var east = rs["east"];
29209         var center = rs["center"];
29210         //if(this.hideOnLayout){ // not supported anymore
29211             //c.el.setStyle("display", "none");
29212         //}
29213         if(north && north.isVisible()){
29214             var b = north.getBox();
29215             var m = north.getMargins();
29216             b.width = w - (m.left+m.right);
29217             b.x = m.left;
29218             b.y = m.top;
29219             centerY = b.height + b.y + m.bottom;
29220             centerH -= centerY;
29221             north.updateBox(this.safeBox(b));
29222         }
29223         if(south && south.isVisible()){
29224             var b = south.getBox();
29225             var m = south.getMargins();
29226             b.width = w - (m.left+m.right);
29227             b.x = m.left;
29228             var totalHeight = (b.height + m.top + m.bottom);
29229             b.y = h - totalHeight + m.top;
29230             centerH -= totalHeight;
29231             south.updateBox(this.safeBox(b));
29232         }
29233         if(west && west.isVisible()){
29234             var b = west.getBox();
29235             var m = west.getMargins();
29236             b.height = centerH - (m.top+m.bottom);
29237             b.x = m.left;
29238             b.y = centerY + m.top;
29239             var totalWidth = (b.width + m.left + m.right);
29240             centerX += totalWidth;
29241             centerW -= totalWidth;
29242             west.updateBox(this.safeBox(b));
29243         }
29244         if(east && east.isVisible()){
29245             var b = east.getBox();
29246             var m = east.getMargins();
29247             b.height = centerH - (m.top+m.bottom);
29248             var totalWidth = (b.width + m.left + m.right);
29249             b.x = w - totalWidth + m.left;
29250             b.y = centerY + m.top;
29251             centerW -= totalWidth;
29252             east.updateBox(this.safeBox(b));
29253         }
29254         if(center){
29255             var m = center.getMargins();
29256             var centerBox = {
29257                 x: centerX + m.left,
29258                 y: centerY + m.top,
29259                 width: centerW - (m.left+m.right),
29260                 height: centerH - (m.top+m.bottom)
29261             };
29262             //if(this.hideOnLayout){
29263                 //center.el.setStyle("display", "block");
29264             //}
29265             center.updateBox(this.safeBox(centerBox));
29266         }
29267         this.el.repaint();
29268         this.fireEvent("layout", this);
29269     },
29270
29271     // private
29272     safeBox : function(box){
29273         box.width = Math.max(0, box.width);
29274         box.height = Math.max(0, box.height);
29275         return box;
29276     },
29277
29278     /**
29279      * Adds a ContentPanel (or subclass) to this layout.
29280      * @param {String} target The target region key (north, south, east, west or center).
29281      * @param {Roo.ContentPanel} panel The panel to add
29282      * @return {Roo.ContentPanel} The added panel
29283      */
29284     add : function(target, panel){
29285          
29286         target = target.toLowerCase();
29287         return this.regions[target].add(panel);
29288     },
29289
29290     /**
29291      * Remove a ContentPanel (or subclass) to this layout.
29292      * @param {String} target The target region key (north, south, east, west or center).
29293      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29294      * @return {Roo.ContentPanel} The removed panel
29295      */
29296     remove : function(target, panel){
29297         target = target.toLowerCase();
29298         return this.regions[target].remove(panel);
29299     },
29300
29301     /**
29302      * Searches all regions for a panel with the specified id
29303      * @param {String} panelId
29304      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29305      */
29306     findPanel : function(panelId){
29307         var rs = this.regions;
29308         for(var target in rs){
29309             if(typeof rs[target] != "function"){
29310                 var p = rs[target].getPanel(panelId);
29311                 if(p){
29312                     return p;
29313                 }
29314             }
29315         }
29316         return null;
29317     },
29318
29319     /**
29320      * Searches all regions for a panel with the specified id and activates (shows) it.
29321      * @param {String/ContentPanel} panelId The panels id or the panel itself
29322      * @return {Roo.ContentPanel} The shown panel or null
29323      */
29324     showPanel : function(panelId) {
29325       var rs = this.regions;
29326       for(var target in rs){
29327          var r = rs[target];
29328          if(typeof r != "function"){
29329             if(r.hasPanel(panelId)){
29330                return r.showPanel(panelId);
29331             }
29332          }
29333       }
29334       return null;
29335    },
29336
29337    /**
29338      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29339      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29340      */
29341     restoreState : function(provider){
29342         if(!provider){
29343             provider = Roo.state.Manager;
29344         }
29345         var sm = new Roo.LayoutStateManager();
29346         sm.init(this, provider);
29347     },
29348
29349     /**
29350      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29351      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29352      * a valid ContentPanel config object.  Example:
29353      * <pre><code>
29354 // Create the main layout
29355 var layout = new Roo.BorderLayout('main-ct', {
29356     west: {
29357         split:true,
29358         minSize: 175,
29359         titlebar: true
29360     },
29361     center: {
29362         title:'Components'
29363     }
29364 }, 'main-ct');
29365
29366 // Create and add multiple ContentPanels at once via configs
29367 layout.batchAdd({
29368    west: {
29369        id: 'source-files',
29370        autoCreate:true,
29371        title:'Ext Source Files',
29372        autoScroll:true,
29373        fitToFrame:true
29374    },
29375    center : {
29376        el: cview,
29377        autoScroll:true,
29378        fitToFrame:true,
29379        toolbar: tb,
29380        resizeEl:'cbody'
29381    }
29382 });
29383 </code></pre>
29384      * @param {Object} regions An object containing ContentPanel configs by region name
29385      */
29386     batchAdd : function(regions){
29387         this.beginUpdate();
29388         for(var rname in regions){
29389             var lr = this.regions[rname];
29390             if(lr){
29391                 this.addTypedPanels(lr, regions[rname]);
29392             }
29393         }
29394         this.endUpdate();
29395     },
29396
29397     // private
29398     addTypedPanels : function(lr, ps){
29399         if(typeof ps == 'string'){
29400             lr.add(new Roo.ContentPanel(ps));
29401         }
29402         else if(ps instanceof Array){
29403             for(var i =0, len = ps.length; i < len; i++){
29404                 this.addTypedPanels(lr, ps[i]);
29405             }
29406         }
29407         else if(!ps.events){ // raw config?
29408             var el = ps.el;
29409             delete ps.el; // prevent conflict
29410             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29411         }
29412         else {  // panel object assumed!
29413             lr.add(ps);
29414         }
29415     },
29416     /**
29417      * Adds a xtype elements to the layout.
29418      * <pre><code>
29419
29420 layout.addxtype({
29421        xtype : 'ContentPanel',
29422        region: 'west',
29423        items: [ .... ]
29424    }
29425 );
29426
29427 layout.addxtype({
29428         xtype : 'NestedLayoutPanel',
29429         region: 'west',
29430         layout: {
29431            center: { },
29432            west: { }   
29433         },
29434         items : [ ... list of content panels or nested layout panels.. ]
29435    }
29436 );
29437 </code></pre>
29438      * @param {Object} cfg Xtype definition of item to add.
29439      */
29440     addxtype : function(cfg)
29441     {
29442         // basically accepts a pannel...
29443         // can accept a layout region..!?!?
29444         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29445         
29446         if (!cfg.xtype.match(/Panel$/)) {
29447             return false;
29448         }
29449         var ret = false;
29450         
29451         if (typeof(cfg.region) == 'undefined') {
29452             Roo.log("Failed to add Panel, region was not set");
29453             Roo.log(cfg);
29454             return false;
29455         }
29456         var region = cfg.region;
29457         delete cfg.region;
29458         
29459           
29460         var xitems = [];
29461         if (cfg.items) {
29462             xitems = cfg.items;
29463             delete cfg.items;
29464         }
29465         var nb = false;
29466         
29467         switch(cfg.xtype) 
29468         {
29469             case 'ContentPanel':  // ContentPanel (el, cfg)
29470             case 'ScrollPanel':  // ContentPanel (el, cfg)
29471             case 'ViewPanel': 
29472                 if(cfg.autoCreate) {
29473                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29474                 } else {
29475                     var el = this.el.createChild();
29476                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29477                 }
29478                 
29479                 this.add(region, ret);
29480                 break;
29481             
29482             
29483             case 'TreePanel': // our new panel!
29484                 cfg.el = this.el.createChild();
29485                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29486                 this.add(region, ret);
29487                 break;
29488             
29489             case 'NestedLayoutPanel': 
29490                 // create a new Layout (which is  a Border Layout...
29491                 var el = this.el.createChild();
29492                 var clayout = cfg.layout;
29493                 delete cfg.layout;
29494                 clayout.items   = clayout.items  || [];
29495                 // replace this exitems with the clayout ones..
29496                 xitems = clayout.items;
29497                  
29498                 
29499                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29500                     cfg.background = false;
29501                 }
29502                 var layout = new Roo.BorderLayout(el, clayout);
29503                 
29504                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29505                 //console.log('adding nested layout panel '  + cfg.toSource());
29506                 this.add(region, ret);
29507                 nb = {}; /// find first...
29508                 break;
29509                 
29510             case 'GridPanel': 
29511             
29512                 // needs grid and region
29513                 
29514                 //var el = this.getRegion(region).el.createChild();
29515                 var el = this.el.createChild();
29516                 // create the grid first...
29517                 
29518                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29519                 delete cfg.grid;
29520                 if (region == 'center' && this.active ) {
29521                     cfg.background = false;
29522                 }
29523                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29524                 
29525                 this.add(region, ret);
29526                 if (cfg.background) {
29527                     ret.on('activate', function(gp) {
29528                         if (!gp.grid.rendered) {
29529                             gp.grid.render();
29530                         }
29531                     });
29532                 } else {
29533                     grid.render();
29534                 }
29535                 break;
29536            
29537            
29538            
29539                 
29540                 
29541                 
29542             default:
29543                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29544                     
29545                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29546                     this.add(region, ret);
29547                 } else {
29548                 
29549                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29550                     return null;
29551                 }
29552                 
29553              // GridPanel (grid, cfg)
29554             
29555         }
29556         this.beginUpdate();
29557         // add children..
29558         var region = '';
29559         var abn = {};
29560         Roo.each(xitems, function(i)  {
29561             region = nb && i.region ? i.region : false;
29562             
29563             var add = ret.addxtype(i);
29564            
29565             if (region) {
29566                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29567                 if (!i.background) {
29568                     abn[region] = nb[region] ;
29569                 }
29570             }
29571             
29572         });
29573         this.endUpdate();
29574
29575         // make the last non-background panel active..
29576         //if (nb) { Roo.log(abn); }
29577         if (nb) {
29578             
29579             for(var r in abn) {
29580                 region = this.getRegion(r);
29581                 if (region) {
29582                     // tried using nb[r], but it does not work..
29583                      
29584                     region.showPanel(abn[r]);
29585                    
29586                 }
29587             }
29588         }
29589         return ret;
29590         
29591     }
29592 });
29593
29594 /**
29595  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29596  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29597  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29598  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29599  * <pre><code>
29600 // shorthand
29601 var CP = Roo.ContentPanel;
29602
29603 var layout = Roo.BorderLayout.create({
29604     north: {
29605         initialSize: 25,
29606         titlebar: false,
29607         panels: [new CP("north", "North")]
29608     },
29609     west: {
29610         split:true,
29611         initialSize: 200,
29612         minSize: 175,
29613         maxSize: 400,
29614         titlebar: true,
29615         collapsible: true,
29616         panels: [new CP("west", {title: "West"})]
29617     },
29618     east: {
29619         split:true,
29620         initialSize: 202,
29621         minSize: 175,
29622         maxSize: 400,
29623         titlebar: true,
29624         collapsible: true,
29625         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29626     },
29627     south: {
29628         split:true,
29629         initialSize: 100,
29630         minSize: 100,
29631         maxSize: 200,
29632         titlebar: true,
29633         collapsible: true,
29634         panels: [new CP("south", {title: "South", closable: true})]
29635     },
29636     center: {
29637         titlebar: true,
29638         autoScroll:true,
29639         resizeTabs: true,
29640         minTabWidth: 50,
29641         preferredTabWidth: 150,
29642         panels: [
29643             new CP("center1", {title: "Close Me", closable: true}),
29644             new CP("center2", {title: "Center Panel", closable: false})
29645         ]
29646     }
29647 }, document.body);
29648
29649 layout.getRegion("center").showPanel("center1");
29650 </code></pre>
29651  * @param config
29652  * @param targetEl
29653  */
29654 Roo.BorderLayout.create = function(config, targetEl){
29655     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29656     layout.beginUpdate();
29657     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29658     for(var j = 0, jlen = regions.length; j < jlen; j++){
29659         var lr = regions[j];
29660         if(layout.regions[lr] && config[lr].panels){
29661             var r = layout.regions[lr];
29662             var ps = config[lr].panels;
29663             layout.addTypedPanels(r, ps);
29664         }
29665     }
29666     layout.endUpdate();
29667     return layout;
29668 };
29669
29670 // private
29671 Roo.BorderLayout.RegionFactory = {
29672     // private
29673     validRegions : ["north","south","east","west","center"],
29674
29675     // private
29676     create : function(target, mgr, config){
29677         target = target.toLowerCase();
29678         if(config.lightweight || config.basic){
29679             return new Roo.BasicLayoutRegion(mgr, config, target);
29680         }
29681         switch(target){
29682             case "north":
29683                 return new Roo.NorthLayoutRegion(mgr, config);
29684             case "south":
29685                 return new Roo.SouthLayoutRegion(mgr, config);
29686             case "east":
29687                 return new Roo.EastLayoutRegion(mgr, config);
29688             case "west":
29689                 return new Roo.WestLayoutRegion(mgr, config);
29690             case "center":
29691                 return new Roo.CenterLayoutRegion(mgr, config);
29692         }
29693         throw 'Layout region "'+target+'" not supported.';
29694     }
29695 };/*
29696  * Based on:
29697  * Ext JS Library 1.1.1
29698  * Copyright(c) 2006-2007, Ext JS, LLC.
29699  *
29700  * Originally Released Under LGPL - original licence link has changed is not relivant.
29701  *
29702  * Fork - LGPL
29703  * <script type="text/javascript">
29704  */
29705  
29706 /**
29707  * @class Roo.BasicLayoutRegion
29708  * @extends Roo.util.Observable
29709  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29710  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29711  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29712  */
29713 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29714     this.mgr = mgr;
29715     this.position  = pos;
29716     this.events = {
29717         /**
29718          * @scope Roo.BasicLayoutRegion
29719          */
29720         
29721         /**
29722          * @event beforeremove
29723          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29724          * @param {Roo.LayoutRegion} this
29725          * @param {Roo.ContentPanel} panel The panel
29726          * @param {Object} e The cancel event object
29727          */
29728         "beforeremove" : true,
29729         /**
29730          * @event invalidated
29731          * Fires when the layout for this region is changed.
29732          * @param {Roo.LayoutRegion} this
29733          */
29734         "invalidated" : true,
29735         /**
29736          * @event visibilitychange
29737          * Fires when this region is shown or hidden 
29738          * @param {Roo.LayoutRegion} this
29739          * @param {Boolean} visibility true or false
29740          */
29741         "visibilitychange" : true,
29742         /**
29743          * @event paneladded
29744          * Fires when a panel is added. 
29745          * @param {Roo.LayoutRegion} this
29746          * @param {Roo.ContentPanel} panel The panel
29747          */
29748         "paneladded" : true,
29749         /**
29750          * @event panelremoved
29751          * Fires when a panel is removed. 
29752          * @param {Roo.LayoutRegion} this
29753          * @param {Roo.ContentPanel} panel The panel
29754          */
29755         "panelremoved" : true,
29756         /**
29757          * @event beforecollapse
29758          * Fires when this region before collapse.
29759          * @param {Roo.LayoutRegion} this
29760          */
29761         "beforecollapse" : true,
29762         /**
29763          * @event collapsed
29764          * Fires when this region is collapsed.
29765          * @param {Roo.LayoutRegion} this
29766          */
29767         "collapsed" : true,
29768         /**
29769          * @event expanded
29770          * Fires when this region is expanded.
29771          * @param {Roo.LayoutRegion} this
29772          */
29773         "expanded" : true,
29774         /**
29775          * @event slideshow
29776          * Fires when this region is slid into view.
29777          * @param {Roo.LayoutRegion} this
29778          */
29779         "slideshow" : true,
29780         /**
29781          * @event slidehide
29782          * Fires when this region slides out of view. 
29783          * @param {Roo.LayoutRegion} this
29784          */
29785         "slidehide" : true,
29786         /**
29787          * @event panelactivated
29788          * Fires when a panel is activated. 
29789          * @param {Roo.LayoutRegion} this
29790          * @param {Roo.ContentPanel} panel The activated panel
29791          */
29792         "panelactivated" : true,
29793         /**
29794          * @event resized
29795          * Fires when the user resizes this region. 
29796          * @param {Roo.LayoutRegion} this
29797          * @param {Number} newSize The new size (width for east/west, height for north/south)
29798          */
29799         "resized" : true
29800     };
29801     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29802     this.panels = new Roo.util.MixedCollection();
29803     this.panels.getKey = this.getPanelId.createDelegate(this);
29804     this.box = null;
29805     this.activePanel = null;
29806     // ensure listeners are added...
29807     
29808     if (config.listeners || config.events) {
29809         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29810             listeners : config.listeners || {},
29811             events : config.events || {}
29812         });
29813     }
29814     
29815     if(skipConfig !== true){
29816         this.applyConfig(config);
29817     }
29818 };
29819
29820 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29821     getPanelId : function(p){
29822         return p.getId();
29823     },
29824     
29825     applyConfig : function(config){
29826         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29827         this.config = config;
29828         
29829     },
29830     
29831     /**
29832      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29833      * the width, for horizontal (north, south) the height.
29834      * @param {Number} newSize The new width or height
29835      */
29836     resizeTo : function(newSize){
29837         var el = this.el ? this.el :
29838                  (this.activePanel ? this.activePanel.getEl() : null);
29839         if(el){
29840             switch(this.position){
29841                 case "east":
29842                 case "west":
29843                     el.setWidth(newSize);
29844                     this.fireEvent("resized", this, newSize);
29845                 break;
29846                 case "north":
29847                 case "south":
29848                     el.setHeight(newSize);
29849                     this.fireEvent("resized", this, newSize);
29850                 break;                
29851             }
29852         }
29853     },
29854     
29855     getBox : function(){
29856         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29857     },
29858     
29859     getMargins : function(){
29860         return this.margins;
29861     },
29862     
29863     updateBox : function(box){
29864         this.box = box;
29865         var el = this.activePanel.getEl();
29866         el.dom.style.left = box.x + "px";
29867         el.dom.style.top = box.y + "px";
29868         this.activePanel.setSize(box.width, box.height);
29869     },
29870     
29871     /**
29872      * Returns the container element for this region.
29873      * @return {Roo.Element}
29874      */
29875     getEl : function(){
29876         return this.activePanel;
29877     },
29878     
29879     /**
29880      * Returns true if this region is currently visible.
29881      * @return {Boolean}
29882      */
29883     isVisible : function(){
29884         return this.activePanel ? true : false;
29885     },
29886     
29887     setActivePanel : function(panel){
29888         panel = this.getPanel(panel);
29889         if(this.activePanel && this.activePanel != panel){
29890             this.activePanel.setActiveState(false);
29891             this.activePanel.getEl().setLeftTop(-10000,-10000);
29892         }
29893         this.activePanel = panel;
29894         panel.setActiveState(true);
29895         if(this.box){
29896             panel.setSize(this.box.width, this.box.height);
29897         }
29898         this.fireEvent("panelactivated", this, panel);
29899         this.fireEvent("invalidated");
29900     },
29901     
29902     /**
29903      * Show the specified panel.
29904      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29905      * @return {Roo.ContentPanel} The shown panel or null
29906      */
29907     showPanel : function(panel){
29908         if(panel = this.getPanel(panel)){
29909             this.setActivePanel(panel);
29910         }
29911         return panel;
29912     },
29913     
29914     /**
29915      * Get the active panel for this region.
29916      * @return {Roo.ContentPanel} The active panel or null
29917      */
29918     getActivePanel : function(){
29919         return this.activePanel;
29920     },
29921     
29922     /**
29923      * Add the passed ContentPanel(s)
29924      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29925      * @return {Roo.ContentPanel} The panel added (if only one was added)
29926      */
29927     add : function(panel){
29928         if(arguments.length > 1){
29929             for(var i = 0, len = arguments.length; i < len; i++) {
29930                 this.add(arguments[i]);
29931             }
29932             return null;
29933         }
29934         if(this.hasPanel(panel)){
29935             this.showPanel(panel);
29936             return panel;
29937         }
29938         var el = panel.getEl();
29939         if(el.dom.parentNode != this.mgr.el.dom){
29940             this.mgr.el.dom.appendChild(el.dom);
29941         }
29942         if(panel.setRegion){
29943             panel.setRegion(this);
29944         }
29945         this.panels.add(panel);
29946         el.setStyle("position", "absolute");
29947         if(!panel.background){
29948             this.setActivePanel(panel);
29949             if(this.config.initialSize && this.panels.getCount()==1){
29950                 this.resizeTo(this.config.initialSize);
29951             }
29952         }
29953         this.fireEvent("paneladded", this, panel);
29954         return panel;
29955     },
29956     
29957     /**
29958      * Returns true if the panel is in this region.
29959      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29960      * @return {Boolean}
29961      */
29962     hasPanel : function(panel){
29963         if(typeof panel == "object"){ // must be panel obj
29964             panel = panel.getId();
29965         }
29966         return this.getPanel(panel) ? true : false;
29967     },
29968     
29969     /**
29970      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29971      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29972      * @param {Boolean} preservePanel Overrides the config preservePanel option
29973      * @return {Roo.ContentPanel} The panel that was removed
29974      */
29975     remove : function(panel, preservePanel){
29976         panel = this.getPanel(panel);
29977         if(!panel){
29978             return null;
29979         }
29980         var e = {};
29981         this.fireEvent("beforeremove", this, panel, e);
29982         if(e.cancel === true){
29983             return null;
29984         }
29985         var panelId = panel.getId();
29986         this.panels.removeKey(panelId);
29987         return panel;
29988     },
29989     
29990     /**
29991      * Returns the panel specified or null if it's not in this region.
29992      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29993      * @return {Roo.ContentPanel}
29994      */
29995     getPanel : function(id){
29996         if(typeof id == "object"){ // must be panel obj
29997             return id;
29998         }
29999         return this.panels.get(id);
30000     },
30001     
30002     /**
30003      * Returns this regions position (north/south/east/west/center).
30004      * @return {String} 
30005      */
30006     getPosition: function(){
30007         return this.position;    
30008     }
30009 });/*
30010  * Based on:
30011  * Ext JS Library 1.1.1
30012  * Copyright(c) 2006-2007, Ext JS, LLC.
30013  *
30014  * Originally Released Under LGPL - original licence link has changed is not relivant.
30015  *
30016  * Fork - LGPL
30017  * <script type="text/javascript">
30018  */
30019  
30020 /**
30021  * @class Roo.LayoutRegion
30022  * @extends Roo.BasicLayoutRegion
30023  * This class represents a region in a layout manager.
30024  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30025  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30026  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30027  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30028  * @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})
30029  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30030  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30031  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30032  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30033  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30034  * @cfg {String}    title           The title for the region (overrides panel titles)
30035  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30036  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30037  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30038  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30039  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30040  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30041  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30042  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30043  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30044  * @cfg {Boolean}   showPin         True to show a pin button
30045  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30046  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30047  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30048  * @cfg {Number}    width           For East/West panels
30049  * @cfg {Number}    height          For North/South panels
30050  * @cfg {Boolean}   split           To show the splitter
30051  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30052  */
30053 Roo.LayoutRegion = function(mgr, config, pos){
30054     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30055     var dh = Roo.DomHelper;
30056     /** This region's container element 
30057     * @type Roo.Element */
30058     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30059     /** This region's title element 
30060     * @type Roo.Element */
30061
30062     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30063         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30064         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30065     ]}, true);
30066     this.titleEl.enableDisplayMode();
30067     /** This region's title text element 
30068     * @type HTMLElement */
30069     this.titleTextEl = this.titleEl.dom.firstChild;
30070     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30071     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30072     this.closeBtn.enableDisplayMode();
30073     this.closeBtn.on("click", this.closeClicked, this);
30074     this.closeBtn.hide();
30075
30076     this.createBody(config);
30077     this.visible = true;
30078     this.collapsed = false;
30079
30080     if(config.hideWhenEmpty){
30081         this.hide();
30082         this.on("paneladded", this.validateVisibility, this);
30083         this.on("panelremoved", this.validateVisibility, this);
30084     }
30085     this.applyConfig(config);
30086 };
30087
30088 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30089
30090     createBody : function(){
30091         /** This region's body element 
30092         * @type Roo.Element */
30093         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30094     },
30095
30096     applyConfig : function(c){
30097         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30098             var dh = Roo.DomHelper;
30099             if(c.titlebar !== false){
30100                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30101                 this.collapseBtn.on("click", this.collapse, this);
30102                 this.collapseBtn.enableDisplayMode();
30103
30104                 if(c.showPin === true || this.showPin){
30105                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30106                     this.stickBtn.enableDisplayMode();
30107                     this.stickBtn.on("click", this.expand, this);
30108                     this.stickBtn.hide();
30109                 }
30110             }
30111             /** This region's collapsed element
30112             * @type Roo.Element */
30113             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30114                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30115             ]}, true);
30116             if(c.floatable !== false){
30117                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30118                this.collapsedEl.on("click", this.collapseClick, this);
30119             }
30120
30121             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30122                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30123                    id: "message", unselectable: "on", style:{"float":"left"}});
30124                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30125              }
30126             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30127             this.expandBtn.on("click", this.expand, this);
30128         }
30129         if(this.collapseBtn){
30130             this.collapseBtn.setVisible(c.collapsible == true);
30131         }
30132         this.cmargins = c.cmargins || this.cmargins ||
30133                          (this.position == "west" || this.position == "east" ?
30134                              {top: 0, left: 2, right:2, bottom: 0} :
30135                              {top: 2, left: 0, right:0, bottom: 2});
30136         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30137         this.bottomTabs = c.tabPosition != "top";
30138         this.autoScroll = c.autoScroll || false;
30139         if(this.autoScroll){
30140             this.bodyEl.setStyle("overflow", "auto");
30141         }else{
30142             this.bodyEl.setStyle("overflow", "hidden");
30143         }
30144         //if(c.titlebar !== false){
30145             if((!c.titlebar && !c.title) || c.titlebar === false){
30146                 this.titleEl.hide();
30147             }else{
30148                 this.titleEl.show();
30149                 if(c.title){
30150                     this.titleTextEl.innerHTML = c.title;
30151                 }
30152             }
30153         //}
30154         this.duration = c.duration || .30;
30155         this.slideDuration = c.slideDuration || .45;
30156         this.config = c;
30157         if(c.collapsed){
30158             this.collapse(true);
30159         }
30160         if(c.hidden){
30161             this.hide();
30162         }
30163     },
30164     /**
30165      * Returns true if this region is currently visible.
30166      * @return {Boolean}
30167      */
30168     isVisible : function(){
30169         return this.visible;
30170     },
30171
30172     /**
30173      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30174      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30175      */
30176     setCollapsedTitle : function(title){
30177         title = title || "&#160;";
30178         if(this.collapsedTitleTextEl){
30179             this.collapsedTitleTextEl.innerHTML = title;
30180         }
30181     },
30182
30183     getBox : function(){
30184         var b;
30185         if(!this.collapsed){
30186             b = this.el.getBox(false, true);
30187         }else{
30188             b = this.collapsedEl.getBox(false, true);
30189         }
30190         return b;
30191     },
30192
30193     getMargins : function(){
30194         return this.collapsed ? this.cmargins : this.margins;
30195     },
30196
30197     highlight : function(){
30198         this.el.addClass("x-layout-panel-dragover");
30199     },
30200
30201     unhighlight : function(){
30202         this.el.removeClass("x-layout-panel-dragover");
30203     },
30204
30205     updateBox : function(box){
30206         this.box = box;
30207         if(!this.collapsed){
30208             this.el.dom.style.left = box.x + "px";
30209             this.el.dom.style.top = box.y + "px";
30210             this.updateBody(box.width, box.height);
30211         }else{
30212             this.collapsedEl.dom.style.left = box.x + "px";
30213             this.collapsedEl.dom.style.top = box.y + "px";
30214             this.collapsedEl.setSize(box.width, box.height);
30215         }
30216         if(this.tabs){
30217             this.tabs.autoSizeTabs();
30218         }
30219     },
30220
30221     updateBody : function(w, h){
30222         if(w !== null){
30223             this.el.setWidth(w);
30224             w -= this.el.getBorderWidth("rl");
30225             if(this.config.adjustments){
30226                 w += this.config.adjustments[0];
30227             }
30228         }
30229         if(h !== null){
30230             this.el.setHeight(h);
30231             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30232             h -= this.el.getBorderWidth("tb");
30233             if(this.config.adjustments){
30234                 h += this.config.adjustments[1];
30235             }
30236             this.bodyEl.setHeight(h);
30237             if(this.tabs){
30238                 h = this.tabs.syncHeight(h);
30239             }
30240         }
30241         if(this.panelSize){
30242             w = w !== null ? w : this.panelSize.width;
30243             h = h !== null ? h : this.panelSize.height;
30244         }
30245         if(this.activePanel){
30246             var el = this.activePanel.getEl();
30247             w = w !== null ? w : el.getWidth();
30248             h = h !== null ? h : el.getHeight();
30249             this.panelSize = {width: w, height: h};
30250             this.activePanel.setSize(w, h);
30251         }
30252         if(Roo.isIE && this.tabs){
30253             this.tabs.el.repaint();
30254         }
30255     },
30256
30257     /**
30258      * Returns the container element for this region.
30259      * @return {Roo.Element}
30260      */
30261     getEl : function(){
30262         return this.el;
30263     },
30264
30265     /**
30266      * Hides this region.
30267      */
30268     hide : function(){
30269         if(!this.collapsed){
30270             this.el.dom.style.left = "-2000px";
30271             this.el.hide();
30272         }else{
30273             this.collapsedEl.dom.style.left = "-2000px";
30274             this.collapsedEl.hide();
30275         }
30276         this.visible = false;
30277         this.fireEvent("visibilitychange", this, false);
30278     },
30279
30280     /**
30281      * Shows this region if it was previously hidden.
30282      */
30283     show : function(){
30284         if(!this.collapsed){
30285             this.el.show();
30286         }else{
30287             this.collapsedEl.show();
30288         }
30289         this.visible = true;
30290         this.fireEvent("visibilitychange", this, true);
30291     },
30292
30293     closeClicked : function(){
30294         if(this.activePanel){
30295             this.remove(this.activePanel);
30296         }
30297     },
30298
30299     collapseClick : function(e){
30300         if(this.isSlid){
30301            e.stopPropagation();
30302            this.slideIn();
30303         }else{
30304            e.stopPropagation();
30305            this.slideOut();
30306         }
30307     },
30308
30309     /**
30310      * Collapses this region.
30311      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30312      */
30313     collapse : function(skipAnim, skipCheck){
30314         if(this.collapsed) {
30315             return;
30316         }
30317         
30318         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30319             
30320             this.collapsed = true;
30321             if(this.split){
30322                 this.split.el.hide();
30323             }
30324             if(this.config.animate && skipAnim !== true){
30325                 this.fireEvent("invalidated", this);
30326                 this.animateCollapse();
30327             }else{
30328                 this.el.setLocation(-20000,-20000);
30329                 this.el.hide();
30330                 this.collapsedEl.show();
30331                 this.fireEvent("collapsed", this);
30332                 this.fireEvent("invalidated", this);
30333             }
30334         }
30335         
30336     },
30337
30338     animateCollapse : function(){
30339         // overridden
30340     },
30341
30342     /**
30343      * Expands this region if it was previously collapsed.
30344      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30345      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30346      */
30347     expand : function(e, skipAnim){
30348         if(e) {
30349             e.stopPropagation();
30350         }
30351         if(!this.collapsed || this.el.hasActiveFx()) {
30352             return;
30353         }
30354         if(this.isSlid){
30355             this.afterSlideIn();
30356             skipAnim = true;
30357         }
30358         this.collapsed = false;
30359         if(this.config.animate && skipAnim !== true){
30360             this.animateExpand();
30361         }else{
30362             this.el.show();
30363             if(this.split){
30364                 this.split.el.show();
30365             }
30366             this.collapsedEl.setLocation(-2000,-2000);
30367             this.collapsedEl.hide();
30368             this.fireEvent("invalidated", this);
30369             this.fireEvent("expanded", this);
30370         }
30371     },
30372
30373     animateExpand : function(){
30374         // overridden
30375     },
30376
30377     initTabs : function()
30378     {
30379         this.bodyEl.setStyle("overflow", "hidden");
30380         var ts = new Roo.TabPanel(
30381                 this.bodyEl.dom,
30382                 {
30383                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30384                     disableTooltips: this.config.disableTabTips,
30385                     toolbar : this.config.toolbar
30386                 }
30387         );
30388         if(this.config.hideTabs){
30389             ts.stripWrap.setDisplayed(false);
30390         }
30391         this.tabs = ts;
30392         ts.resizeTabs = this.config.resizeTabs === true;
30393         ts.minTabWidth = this.config.minTabWidth || 40;
30394         ts.maxTabWidth = this.config.maxTabWidth || 250;
30395         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30396         ts.monitorResize = false;
30397         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30398         ts.bodyEl.addClass('x-layout-tabs-body');
30399         this.panels.each(this.initPanelAsTab, this);
30400     },
30401
30402     initPanelAsTab : function(panel){
30403         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30404                     this.config.closeOnTab && panel.isClosable());
30405         if(panel.tabTip !== undefined){
30406             ti.setTooltip(panel.tabTip);
30407         }
30408         ti.on("activate", function(){
30409               this.setActivePanel(panel);
30410         }, this);
30411         if(this.config.closeOnTab){
30412             ti.on("beforeclose", function(t, e){
30413                 e.cancel = true;
30414                 this.remove(panel);
30415             }, this);
30416         }
30417         return ti;
30418     },
30419
30420     updatePanelTitle : function(panel, title){
30421         if(this.activePanel == panel){
30422             this.updateTitle(title);
30423         }
30424         if(this.tabs){
30425             var ti = this.tabs.getTab(panel.getEl().id);
30426             ti.setText(title);
30427             if(panel.tabTip !== undefined){
30428                 ti.setTooltip(panel.tabTip);
30429             }
30430         }
30431     },
30432
30433     updateTitle : function(title){
30434         if(this.titleTextEl && !this.config.title){
30435             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30436         }
30437     },
30438
30439     setActivePanel : function(panel){
30440         panel = this.getPanel(panel);
30441         if(this.activePanel && this.activePanel != panel){
30442             this.activePanel.setActiveState(false);
30443         }
30444         this.activePanel = panel;
30445         panel.setActiveState(true);
30446         if(this.panelSize){
30447             panel.setSize(this.panelSize.width, this.panelSize.height);
30448         }
30449         if(this.closeBtn){
30450             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30451         }
30452         this.updateTitle(panel.getTitle());
30453         if(this.tabs){
30454             this.fireEvent("invalidated", this);
30455         }
30456         this.fireEvent("panelactivated", this, panel);
30457     },
30458
30459     /**
30460      * Shows the specified panel.
30461      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30462      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30463      */
30464     showPanel : function(panel)
30465     {
30466         panel = this.getPanel(panel);
30467         if(panel){
30468             if(this.tabs){
30469                 var tab = this.tabs.getTab(panel.getEl().id);
30470                 if(tab.isHidden()){
30471                     this.tabs.unhideTab(tab.id);
30472                 }
30473                 tab.activate();
30474             }else{
30475                 this.setActivePanel(panel);
30476             }
30477         }
30478         return panel;
30479     },
30480
30481     /**
30482      * Get the active panel for this region.
30483      * @return {Roo.ContentPanel} The active panel or null
30484      */
30485     getActivePanel : function(){
30486         return this.activePanel;
30487     },
30488
30489     validateVisibility : function(){
30490         if(this.panels.getCount() < 1){
30491             this.updateTitle("&#160;");
30492             this.closeBtn.hide();
30493             this.hide();
30494         }else{
30495             if(!this.isVisible()){
30496                 this.show();
30497             }
30498         }
30499     },
30500
30501     /**
30502      * Adds the passed ContentPanel(s) to this region.
30503      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30504      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30505      */
30506     add : function(panel){
30507         if(arguments.length > 1){
30508             for(var i = 0, len = arguments.length; i < len; i++) {
30509                 this.add(arguments[i]);
30510             }
30511             return null;
30512         }
30513         if(this.hasPanel(panel)){
30514             this.showPanel(panel);
30515             return panel;
30516         }
30517         panel.setRegion(this);
30518         this.panels.add(panel);
30519         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30520             this.bodyEl.dom.appendChild(panel.getEl().dom);
30521             if(panel.background !== true){
30522                 this.setActivePanel(panel);
30523             }
30524             this.fireEvent("paneladded", this, panel);
30525             return panel;
30526         }
30527         if(!this.tabs){
30528             this.initTabs();
30529         }else{
30530             this.initPanelAsTab(panel);
30531         }
30532         if(panel.background !== true){
30533             this.tabs.activate(panel.getEl().id);
30534         }
30535         this.fireEvent("paneladded", this, panel);
30536         return panel;
30537     },
30538
30539     /**
30540      * Hides the tab for the specified panel.
30541      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30542      */
30543     hidePanel : function(panel){
30544         if(this.tabs && (panel = this.getPanel(panel))){
30545             this.tabs.hideTab(panel.getEl().id);
30546         }
30547     },
30548
30549     /**
30550      * Unhides the tab for a previously hidden panel.
30551      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30552      */
30553     unhidePanel : function(panel){
30554         if(this.tabs && (panel = this.getPanel(panel))){
30555             this.tabs.unhideTab(panel.getEl().id);
30556         }
30557     },
30558
30559     clearPanels : function(){
30560         while(this.panels.getCount() > 0){
30561              this.remove(this.panels.first());
30562         }
30563     },
30564
30565     /**
30566      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30567      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30568      * @param {Boolean} preservePanel Overrides the config preservePanel option
30569      * @return {Roo.ContentPanel} The panel that was removed
30570      */
30571     remove : function(panel, preservePanel){
30572         panel = this.getPanel(panel);
30573         if(!panel){
30574             return null;
30575         }
30576         var e = {};
30577         this.fireEvent("beforeremove", this, panel, e);
30578         if(e.cancel === true){
30579             return null;
30580         }
30581         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30582         var panelId = panel.getId();
30583         this.panels.removeKey(panelId);
30584         if(preservePanel){
30585             document.body.appendChild(panel.getEl().dom);
30586         }
30587         if(this.tabs){
30588             this.tabs.removeTab(panel.getEl().id);
30589         }else if (!preservePanel){
30590             this.bodyEl.dom.removeChild(panel.getEl().dom);
30591         }
30592         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30593             var p = this.panels.first();
30594             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30595             tempEl.appendChild(p.getEl().dom);
30596             this.bodyEl.update("");
30597             this.bodyEl.dom.appendChild(p.getEl().dom);
30598             tempEl = null;
30599             this.updateTitle(p.getTitle());
30600             this.tabs = null;
30601             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30602             this.setActivePanel(p);
30603         }
30604         panel.setRegion(null);
30605         if(this.activePanel == panel){
30606             this.activePanel = null;
30607         }
30608         if(this.config.autoDestroy !== false && preservePanel !== true){
30609             try{panel.destroy();}catch(e){}
30610         }
30611         this.fireEvent("panelremoved", this, panel);
30612         return panel;
30613     },
30614
30615     /**
30616      * Returns the TabPanel component used by this region
30617      * @return {Roo.TabPanel}
30618      */
30619     getTabs : function(){
30620         return this.tabs;
30621     },
30622
30623     createTool : function(parentEl, className){
30624         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30625             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30626         btn.addClassOnOver("x-layout-tools-button-over");
30627         return btn;
30628     }
30629 });/*
30630  * Based on:
30631  * Ext JS Library 1.1.1
30632  * Copyright(c) 2006-2007, Ext JS, LLC.
30633  *
30634  * Originally Released Under LGPL - original licence link has changed is not relivant.
30635  *
30636  * Fork - LGPL
30637  * <script type="text/javascript">
30638  */
30639  
30640
30641
30642 /**
30643  * @class Roo.SplitLayoutRegion
30644  * @extends Roo.LayoutRegion
30645  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30646  */
30647 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30648     this.cursor = cursor;
30649     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30650 };
30651
30652 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30653     splitTip : "Drag to resize.",
30654     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30655     useSplitTips : false,
30656
30657     applyConfig : function(config){
30658         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30659         if(config.split){
30660             if(!this.split){
30661                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30662                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30663                 /** The SplitBar for this region 
30664                 * @type Roo.SplitBar */
30665                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30666                 this.split.on("moved", this.onSplitMove, this);
30667                 this.split.useShim = config.useShim === true;
30668                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30669                 if(this.useSplitTips){
30670                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30671                 }
30672                 if(config.collapsible){
30673                     this.split.el.on("dblclick", this.collapse,  this);
30674                 }
30675             }
30676             if(typeof config.minSize != "undefined"){
30677                 this.split.minSize = config.minSize;
30678             }
30679             if(typeof config.maxSize != "undefined"){
30680                 this.split.maxSize = config.maxSize;
30681             }
30682             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30683                 this.hideSplitter();
30684             }
30685         }
30686     },
30687
30688     getHMaxSize : function(){
30689          var cmax = this.config.maxSize || 10000;
30690          var center = this.mgr.getRegion("center");
30691          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30692     },
30693
30694     getVMaxSize : function(){
30695          var cmax = this.config.maxSize || 10000;
30696          var center = this.mgr.getRegion("center");
30697          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30698     },
30699
30700     onSplitMove : function(split, newSize){
30701         this.fireEvent("resized", this, newSize);
30702     },
30703     
30704     /** 
30705      * Returns the {@link Roo.SplitBar} for this region.
30706      * @return {Roo.SplitBar}
30707      */
30708     getSplitBar : function(){
30709         return this.split;
30710     },
30711     
30712     hide : function(){
30713         this.hideSplitter();
30714         Roo.SplitLayoutRegion.superclass.hide.call(this);
30715     },
30716
30717     hideSplitter : function(){
30718         if(this.split){
30719             this.split.el.setLocation(-2000,-2000);
30720             this.split.el.hide();
30721         }
30722     },
30723
30724     show : function(){
30725         if(this.split){
30726             this.split.el.show();
30727         }
30728         Roo.SplitLayoutRegion.superclass.show.call(this);
30729     },
30730     
30731     beforeSlide: function(){
30732         if(Roo.isGecko){// firefox overflow auto bug workaround
30733             this.bodyEl.clip();
30734             if(this.tabs) {
30735                 this.tabs.bodyEl.clip();
30736             }
30737             if(this.activePanel){
30738                 this.activePanel.getEl().clip();
30739                 
30740                 if(this.activePanel.beforeSlide){
30741                     this.activePanel.beforeSlide();
30742                 }
30743             }
30744         }
30745     },
30746     
30747     afterSlide : function(){
30748         if(Roo.isGecko){// firefox overflow auto bug workaround
30749             this.bodyEl.unclip();
30750             if(this.tabs) {
30751                 this.tabs.bodyEl.unclip();
30752             }
30753             if(this.activePanel){
30754                 this.activePanel.getEl().unclip();
30755                 if(this.activePanel.afterSlide){
30756                     this.activePanel.afterSlide();
30757                 }
30758             }
30759         }
30760     },
30761
30762     initAutoHide : function(){
30763         if(this.autoHide !== false){
30764             if(!this.autoHideHd){
30765                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30766                 this.autoHideHd = {
30767                     "mouseout": function(e){
30768                         if(!e.within(this.el, true)){
30769                             st.delay(500);
30770                         }
30771                     },
30772                     "mouseover" : function(e){
30773                         st.cancel();
30774                     },
30775                     scope : this
30776                 };
30777             }
30778             this.el.on(this.autoHideHd);
30779         }
30780     },
30781
30782     clearAutoHide : function(){
30783         if(this.autoHide !== false){
30784             this.el.un("mouseout", this.autoHideHd.mouseout);
30785             this.el.un("mouseover", this.autoHideHd.mouseover);
30786         }
30787     },
30788
30789     clearMonitor : function(){
30790         Roo.get(document).un("click", this.slideInIf, this);
30791     },
30792
30793     // these names are backwards but not changed for compat
30794     slideOut : function(){
30795         if(this.isSlid || this.el.hasActiveFx()){
30796             return;
30797         }
30798         this.isSlid = true;
30799         if(this.collapseBtn){
30800             this.collapseBtn.hide();
30801         }
30802         this.closeBtnState = this.closeBtn.getStyle('display');
30803         this.closeBtn.hide();
30804         if(this.stickBtn){
30805             this.stickBtn.show();
30806         }
30807         this.el.show();
30808         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30809         this.beforeSlide();
30810         this.el.setStyle("z-index", 10001);
30811         this.el.slideIn(this.getSlideAnchor(), {
30812             callback: function(){
30813                 this.afterSlide();
30814                 this.initAutoHide();
30815                 Roo.get(document).on("click", this.slideInIf, this);
30816                 this.fireEvent("slideshow", this);
30817             },
30818             scope: this,
30819             block: true
30820         });
30821     },
30822
30823     afterSlideIn : function(){
30824         this.clearAutoHide();
30825         this.isSlid = false;
30826         this.clearMonitor();
30827         this.el.setStyle("z-index", "");
30828         if(this.collapseBtn){
30829             this.collapseBtn.show();
30830         }
30831         this.closeBtn.setStyle('display', this.closeBtnState);
30832         if(this.stickBtn){
30833             this.stickBtn.hide();
30834         }
30835         this.fireEvent("slidehide", this);
30836     },
30837
30838     slideIn : function(cb){
30839         if(!this.isSlid || this.el.hasActiveFx()){
30840             Roo.callback(cb);
30841             return;
30842         }
30843         this.isSlid = false;
30844         this.beforeSlide();
30845         this.el.slideOut(this.getSlideAnchor(), {
30846             callback: function(){
30847                 this.el.setLeftTop(-10000, -10000);
30848                 this.afterSlide();
30849                 this.afterSlideIn();
30850                 Roo.callback(cb);
30851             },
30852             scope: this,
30853             block: true
30854         });
30855     },
30856     
30857     slideInIf : function(e){
30858         if(!e.within(this.el)){
30859             this.slideIn();
30860         }
30861     },
30862
30863     animateCollapse : function(){
30864         this.beforeSlide();
30865         this.el.setStyle("z-index", 20000);
30866         var anchor = this.getSlideAnchor();
30867         this.el.slideOut(anchor, {
30868             callback : function(){
30869                 this.el.setStyle("z-index", "");
30870                 this.collapsedEl.slideIn(anchor, {duration:.3});
30871                 this.afterSlide();
30872                 this.el.setLocation(-10000,-10000);
30873                 this.el.hide();
30874                 this.fireEvent("collapsed", this);
30875             },
30876             scope: this,
30877             block: true
30878         });
30879     },
30880
30881     animateExpand : function(){
30882         this.beforeSlide();
30883         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30884         this.el.setStyle("z-index", 20000);
30885         this.collapsedEl.hide({
30886             duration:.1
30887         });
30888         this.el.slideIn(this.getSlideAnchor(), {
30889             callback : function(){
30890                 this.el.setStyle("z-index", "");
30891                 this.afterSlide();
30892                 if(this.split){
30893                     this.split.el.show();
30894                 }
30895                 this.fireEvent("invalidated", this);
30896                 this.fireEvent("expanded", this);
30897             },
30898             scope: this,
30899             block: true
30900         });
30901     },
30902
30903     anchors : {
30904         "west" : "left",
30905         "east" : "right",
30906         "north" : "top",
30907         "south" : "bottom"
30908     },
30909
30910     sanchors : {
30911         "west" : "l",
30912         "east" : "r",
30913         "north" : "t",
30914         "south" : "b"
30915     },
30916
30917     canchors : {
30918         "west" : "tl-tr",
30919         "east" : "tr-tl",
30920         "north" : "tl-bl",
30921         "south" : "bl-tl"
30922     },
30923
30924     getAnchor : function(){
30925         return this.anchors[this.position];
30926     },
30927
30928     getCollapseAnchor : function(){
30929         return this.canchors[this.position];
30930     },
30931
30932     getSlideAnchor : function(){
30933         return this.sanchors[this.position];
30934     },
30935
30936     getAlignAdj : function(){
30937         var cm = this.cmargins;
30938         switch(this.position){
30939             case "west":
30940                 return [0, 0];
30941             break;
30942             case "east":
30943                 return [0, 0];
30944             break;
30945             case "north":
30946                 return [0, 0];
30947             break;
30948             case "south":
30949                 return [0, 0];
30950             break;
30951         }
30952     },
30953
30954     getExpandAdj : function(){
30955         var c = this.collapsedEl, cm = this.cmargins;
30956         switch(this.position){
30957             case "west":
30958                 return [-(cm.right+c.getWidth()+cm.left), 0];
30959             break;
30960             case "east":
30961                 return [cm.right+c.getWidth()+cm.left, 0];
30962             break;
30963             case "north":
30964                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30965             break;
30966             case "south":
30967                 return [0, cm.top+cm.bottom+c.getHeight()];
30968             break;
30969         }
30970     }
30971 });/*
30972  * Based on:
30973  * Ext JS Library 1.1.1
30974  * Copyright(c) 2006-2007, Ext JS, LLC.
30975  *
30976  * Originally Released Under LGPL - original licence link has changed is not relivant.
30977  *
30978  * Fork - LGPL
30979  * <script type="text/javascript">
30980  */
30981 /*
30982  * These classes are private internal classes
30983  */
30984 Roo.CenterLayoutRegion = function(mgr, config){
30985     Roo.LayoutRegion.call(this, mgr, config, "center");
30986     this.visible = true;
30987     this.minWidth = config.minWidth || 20;
30988     this.minHeight = config.minHeight || 20;
30989 };
30990
30991 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30992     hide : function(){
30993         // center panel can't be hidden
30994     },
30995     
30996     show : function(){
30997         // center panel can't be hidden
30998     },
30999     
31000     getMinWidth: function(){
31001         return this.minWidth;
31002     },
31003     
31004     getMinHeight: function(){
31005         return this.minHeight;
31006     }
31007 });
31008
31009
31010 Roo.NorthLayoutRegion = function(mgr, config){
31011     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31012     if(this.split){
31013         this.split.placement = Roo.SplitBar.TOP;
31014         this.split.orientation = Roo.SplitBar.VERTICAL;
31015         this.split.el.addClass("x-layout-split-v");
31016     }
31017     var size = config.initialSize || config.height;
31018     if(typeof size != "undefined"){
31019         this.el.setHeight(size);
31020     }
31021 };
31022 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31023     orientation: Roo.SplitBar.VERTICAL,
31024     getBox : function(){
31025         if(this.collapsed){
31026             return this.collapsedEl.getBox();
31027         }
31028         var box = this.el.getBox();
31029         if(this.split){
31030             box.height += this.split.el.getHeight();
31031         }
31032         return box;
31033     },
31034     
31035     updateBox : function(box){
31036         if(this.split && !this.collapsed){
31037             box.height -= this.split.el.getHeight();
31038             this.split.el.setLeft(box.x);
31039             this.split.el.setTop(box.y+box.height);
31040             this.split.el.setWidth(box.width);
31041         }
31042         if(this.collapsed){
31043             this.updateBody(box.width, null);
31044         }
31045         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31046     }
31047 });
31048
31049 Roo.SouthLayoutRegion = function(mgr, config){
31050     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31051     if(this.split){
31052         this.split.placement = Roo.SplitBar.BOTTOM;
31053         this.split.orientation = Roo.SplitBar.VERTICAL;
31054         this.split.el.addClass("x-layout-split-v");
31055     }
31056     var size = config.initialSize || config.height;
31057     if(typeof size != "undefined"){
31058         this.el.setHeight(size);
31059     }
31060 };
31061 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31062     orientation: Roo.SplitBar.VERTICAL,
31063     getBox : function(){
31064         if(this.collapsed){
31065             return this.collapsedEl.getBox();
31066         }
31067         var box = this.el.getBox();
31068         if(this.split){
31069             var sh = this.split.el.getHeight();
31070             box.height += sh;
31071             box.y -= sh;
31072         }
31073         return box;
31074     },
31075     
31076     updateBox : function(box){
31077         if(this.split && !this.collapsed){
31078             var sh = this.split.el.getHeight();
31079             box.height -= sh;
31080             box.y += sh;
31081             this.split.el.setLeft(box.x);
31082             this.split.el.setTop(box.y-sh);
31083             this.split.el.setWidth(box.width);
31084         }
31085         if(this.collapsed){
31086             this.updateBody(box.width, null);
31087         }
31088         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31089     }
31090 });
31091
31092 Roo.EastLayoutRegion = function(mgr, config){
31093     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31094     if(this.split){
31095         this.split.placement = Roo.SplitBar.RIGHT;
31096         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31097         this.split.el.addClass("x-layout-split-h");
31098     }
31099     var size = config.initialSize || config.width;
31100     if(typeof size != "undefined"){
31101         this.el.setWidth(size);
31102     }
31103 };
31104 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31105     orientation: Roo.SplitBar.HORIZONTAL,
31106     getBox : function(){
31107         if(this.collapsed){
31108             return this.collapsedEl.getBox();
31109         }
31110         var box = this.el.getBox();
31111         if(this.split){
31112             var sw = this.split.el.getWidth();
31113             box.width += sw;
31114             box.x -= sw;
31115         }
31116         return box;
31117     },
31118
31119     updateBox : function(box){
31120         if(this.split && !this.collapsed){
31121             var sw = this.split.el.getWidth();
31122             box.width -= sw;
31123             this.split.el.setLeft(box.x);
31124             this.split.el.setTop(box.y);
31125             this.split.el.setHeight(box.height);
31126             box.x += sw;
31127         }
31128         if(this.collapsed){
31129             this.updateBody(null, box.height);
31130         }
31131         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31132     }
31133 });
31134
31135 Roo.WestLayoutRegion = function(mgr, config){
31136     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31137     if(this.split){
31138         this.split.placement = Roo.SplitBar.LEFT;
31139         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31140         this.split.el.addClass("x-layout-split-h");
31141     }
31142     var size = config.initialSize || config.width;
31143     if(typeof size != "undefined"){
31144         this.el.setWidth(size);
31145     }
31146 };
31147 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31148     orientation: Roo.SplitBar.HORIZONTAL,
31149     getBox : function(){
31150         if(this.collapsed){
31151             return this.collapsedEl.getBox();
31152         }
31153         var box = this.el.getBox();
31154         if(this.split){
31155             box.width += this.split.el.getWidth();
31156         }
31157         return box;
31158     },
31159     
31160     updateBox : function(box){
31161         if(this.split && !this.collapsed){
31162             var sw = this.split.el.getWidth();
31163             box.width -= sw;
31164             this.split.el.setLeft(box.x+box.width);
31165             this.split.el.setTop(box.y);
31166             this.split.el.setHeight(box.height);
31167         }
31168         if(this.collapsed){
31169             this.updateBody(null, box.height);
31170         }
31171         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31172     }
31173 });
31174 /*
31175  * Based on:
31176  * Ext JS Library 1.1.1
31177  * Copyright(c) 2006-2007, Ext JS, LLC.
31178  *
31179  * Originally Released Under LGPL - original licence link has changed is not relivant.
31180  *
31181  * Fork - LGPL
31182  * <script type="text/javascript">
31183  */
31184  
31185  
31186 /*
31187  * Private internal class for reading and applying state
31188  */
31189 Roo.LayoutStateManager = function(layout){
31190      // default empty state
31191      this.state = {
31192         north: {},
31193         south: {},
31194         east: {},
31195         west: {}       
31196     };
31197 };
31198
31199 Roo.LayoutStateManager.prototype = {
31200     init : function(layout, provider){
31201         this.provider = provider;
31202         var state = provider.get(layout.id+"-layout-state");
31203         if(state){
31204             var wasUpdating = layout.isUpdating();
31205             if(!wasUpdating){
31206                 layout.beginUpdate();
31207             }
31208             for(var key in state){
31209                 if(typeof state[key] != "function"){
31210                     var rstate = state[key];
31211                     var r = layout.getRegion(key);
31212                     if(r && rstate){
31213                         if(rstate.size){
31214                             r.resizeTo(rstate.size);
31215                         }
31216                         if(rstate.collapsed == true){
31217                             r.collapse(true);
31218                         }else{
31219                             r.expand(null, true);
31220                         }
31221                     }
31222                 }
31223             }
31224             if(!wasUpdating){
31225                 layout.endUpdate();
31226             }
31227             this.state = state; 
31228         }
31229         this.layout = layout;
31230         layout.on("regionresized", this.onRegionResized, this);
31231         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31232         layout.on("regionexpanded", this.onRegionExpanded, this);
31233     },
31234     
31235     storeState : function(){
31236         this.provider.set(this.layout.id+"-layout-state", this.state);
31237     },
31238     
31239     onRegionResized : function(region, newSize){
31240         this.state[region.getPosition()].size = newSize;
31241         this.storeState();
31242     },
31243     
31244     onRegionCollapsed : function(region){
31245         this.state[region.getPosition()].collapsed = true;
31246         this.storeState();
31247     },
31248     
31249     onRegionExpanded : function(region){
31250         this.state[region.getPosition()].collapsed = false;
31251         this.storeState();
31252     }
31253 };/*
31254  * Based on:
31255  * Ext JS Library 1.1.1
31256  * Copyright(c) 2006-2007, Ext JS, LLC.
31257  *
31258  * Originally Released Under LGPL - original licence link has changed is not relivant.
31259  *
31260  * Fork - LGPL
31261  * <script type="text/javascript">
31262  */
31263 /**
31264  * @class Roo.ContentPanel
31265  * @extends Roo.util.Observable
31266  * @children Roo.form.Form Roo.JsonView Roo.View
31267  * A basic ContentPanel element.
31268  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31269  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31270  * @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
31271  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31272  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31273  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31274  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31275  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31276  * @cfg {String} title          The title for this panel
31277  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31278  * @cfg {String} url            Calls {@link #setUrl} with this value
31279  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31280  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31281  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31282  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31283  * @cfg {String}    style  Extra style to add to the content panel 
31284
31285  * @constructor
31286  * Create a new ContentPanel.
31287  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31288  * @param {String/Object} config A string to set only the title or a config object
31289  * @param {String} content (optional) Set the HTML content for this panel
31290  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31291  */
31292 Roo.ContentPanel = function(el, config, content){
31293     
31294      
31295     /*
31296     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31297         config = el;
31298         el = Roo.id();
31299     }
31300     if (config && config.parentLayout) { 
31301         el = config.parentLayout.el.createChild(); 
31302     }
31303     */
31304     if(el.autoCreate){ // xtype is available if this is called from factory
31305         config = el;
31306         el = Roo.id();
31307     }
31308     this.el = Roo.get(el);
31309     if(!this.el && config && config.autoCreate){
31310         if(typeof config.autoCreate == "object"){
31311             if(!config.autoCreate.id){
31312                 config.autoCreate.id = config.id||el;
31313             }
31314             this.el = Roo.DomHelper.append(document.body,
31315                         config.autoCreate, true);
31316         }else{
31317             this.el = Roo.DomHelper.append(document.body,
31318                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31319         }
31320     }
31321     
31322     
31323     this.closable = false;
31324     this.loaded = false;
31325     this.active = false;
31326     if(typeof config == "string"){
31327         this.title = config;
31328     }else{
31329         Roo.apply(this, config);
31330     }
31331     
31332     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31333         this.wrapEl = this.el.wrap();
31334         this.toolbar.container = this.el.insertSibling(false, 'before');
31335         this.toolbar = new Roo.Toolbar(this.toolbar);
31336     }
31337     
31338     // xtype created footer. - not sure if will work as we normally have to render first..
31339     if (this.footer && !this.footer.el && this.footer.xtype) {
31340         if (!this.wrapEl) {
31341             this.wrapEl = this.el.wrap();
31342         }
31343     
31344         this.footer.container = this.wrapEl.createChild();
31345          
31346         this.footer = Roo.factory(this.footer, Roo);
31347         
31348     }
31349     
31350     if(this.resizeEl){
31351         this.resizeEl = Roo.get(this.resizeEl, true);
31352     }else{
31353         this.resizeEl = this.el;
31354     }
31355     // handle view.xtype
31356     
31357  
31358     
31359     
31360     this.addEvents({
31361         /**
31362          * @event activate
31363          * Fires when this panel is activated. 
31364          * @param {Roo.ContentPanel} this
31365          */
31366         "activate" : true,
31367         /**
31368          * @event deactivate
31369          * Fires when this panel is activated. 
31370          * @param {Roo.ContentPanel} this
31371          */
31372         "deactivate" : true,
31373
31374         /**
31375          * @event resize
31376          * Fires when this panel is resized if fitToFrame is true.
31377          * @param {Roo.ContentPanel} this
31378          * @param {Number} width The width after any component adjustments
31379          * @param {Number} height The height after any component adjustments
31380          */
31381         "resize" : true,
31382         
31383          /**
31384          * @event render
31385          * Fires when this tab is created
31386          * @param {Roo.ContentPanel} this
31387          */
31388         "render" : true
31389          
31390         
31391     });
31392     
31393
31394     
31395     
31396     if(this.autoScroll){
31397         this.resizeEl.setStyle("overflow", "auto");
31398     } else {
31399         // fix randome scrolling
31400         this.el.on('scroll', function() {
31401             Roo.log('fix random scolling');
31402             this.scrollTo('top',0); 
31403         });
31404     }
31405     content = content || this.content;
31406     if(content){
31407         this.setContent(content);
31408     }
31409     if(config && config.url){
31410         this.setUrl(this.url, this.params, this.loadOnce);
31411     }
31412     
31413     
31414     
31415     Roo.ContentPanel.superclass.constructor.call(this);
31416     
31417     if (this.view && typeof(this.view.xtype) != 'undefined') {
31418         this.view.el = this.el.appendChild(document.createElement("div"));
31419         this.view = Roo.factory(this.view); 
31420         this.view.render  &&  this.view.render(false, '');  
31421     }
31422     
31423     
31424     this.fireEvent('render', this);
31425 };
31426
31427 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31428     tabTip:'',
31429     setRegion : function(region){
31430         this.region = region;
31431         if(region){
31432            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31433         }else{
31434            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31435         } 
31436     },
31437     
31438     /**
31439      * Returns the toolbar for this Panel if one was configured. 
31440      * @return {Roo.Toolbar} 
31441      */
31442     getToolbar : function(){
31443         return this.toolbar;
31444     },
31445     
31446     setActiveState : function(active){
31447         this.active = active;
31448         if(!active){
31449             this.fireEvent("deactivate", this);
31450         }else{
31451             this.fireEvent("activate", this);
31452         }
31453     },
31454     /**
31455      * Updates this panel's element
31456      * @param {String} content The new content
31457      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31458     */
31459     setContent : function(content, loadScripts){
31460         this.el.update(content, loadScripts);
31461     },
31462
31463     ignoreResize : function(w, h){
31464         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31465             return true;
31466         }else{
31467             this.lastSize = {width: w, height: h};
31468             return false;
31469         }
31470     },
31471     /**
31472      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31473      * @return {Roo.UpdateManager} The UpdateManager
31474      */
31475     getUpdateManager : function(){
31476         return this.el.getUpdateManager();
31477     },
31478      /**
31479      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31480      * @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:
31481 <pre><code>
31482 panel.load({
31483     url: "your-url.php",
31484     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31485     callback: yourFunction,
31486     scope: yourObject, //(optional scope)
31487     discardUrl: false,
31488     nocache: false,
31489     text: "Loading...",
31490     timeout: 30,
31491     scripts: false
31492 });
31493 </code></pre>
31494      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31495      * 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.
31496      * @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}
31497      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31498      * @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.
31499      * @return {Roo.ContentPanel} this
31500      */
31501     load : function(){
31502         var um = this.el.getUpdateManager();
31503         um.update.apply(um, arguments);
31504         return this;
31505     },
31506
31507
31508     /**
31509      * 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.
31510      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31511      * @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)
31512      * @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)
31513      * @return {Roo.UpdateManager} The UpdateManager
31514      */
31515     setUrl : function(url, params, loadOnce){
31516         if(this.refreshDelegate){
31517             this.removeListener("activate", this.refreshDelegate);
31518         }
31519         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31520         this.on("activate", this.refreshDelegate);
31521         return this.el.getUpdateManager();
31522     },
31523     
31524     _handleRefresh : function(url, params, loadOnce){
31525         if(!loadOnce || !this.loaded){
31526             var updater = this.el.getUpdateManager();
31527             updater.update(url, params, this._setLoaded.createDelegate(this));
31528         }
31529     },
31530     
31531     _setLoaded : function(){
31532         this.loaded = true;
31533     }, 
31534     
31535     /**
31536      * Returns this panel's id
31537      * @return {String} 
31538      */
31539     getId : function(){
31540         return this.el.id;
31541     },
31542     
31543     /** 
31544      * Returns this panel's element - used by regiosn to add.
31545      * @return {Roo.Element} 
31546      */
31547     getEl : function(){
31548         return this.wrapEl || this.el;
31549     },
31550     
31551     adjustForComponents : function(width, height)
31552     {
31553         //Roo.log('adjustForComponents ');
31554         if(this.resizeEl != this.el){
31555             width -= this.el.getFrameWidth('lr');
31556             height -= this.el.getFrameWidth('tb');
31557         }
31558         if(this.toolbar){
31559             var te = this.toolbar.getEl();
31560             height -= te.getHeight();
31561             te.setWidth(width);
31562         }
31563         if(this.footer){
31564             var te = this.footer.getEl();
31565             //Roo.log("footer:" + te.getHeight());
31566             
31567             height -= te.getHeight();
31568             te.setWidth(width);
31569         }
31570         
31571         
31572         if(this.adjustments){
31573             width += this.adjustments[0];
31574             height += this.adjustments[1];
31575         }
31576         return {"width": width, "height": height};
31577     },
31578     
31579     setSize : function(width, height){
31580         if(this.fitToFrame && !this.ignoreResize(width, height)){
31581             if(this.fitContainer && this.resizeEl != this.el){
31582                 this.el.setSize(width, height);
31583             }
31584             var size = this.adjustForComponents(width, height);
31585             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31586             this.fireEvent('resize', this, size.width, size.height);
31587         }
31588     },
31589     
31590     /**
31591      * Returns this panel's title
31592      * @return {String} 
31593      */
31594     getTitle : function(){
31595         return this.title;
31596     },
31597     
31598     /**
31599      * Set this panel's title
31600      * @param {String} title
31601      */
31602     setTitle : function(title){
31603         this.title = title;
31604         if(this.region){
31605             this.region.updatePanelTitle(this, title);
31606         }
31607     },
31608     
31609     /**
31610      * Returns true is this panel was configured to be closable
31611      * @return {Boolean} 
31612      */
31613     isClosable : function(){
31614         return this.closable;
31615     },
31616     
31617     beforeSlide : function(){
31618         this.el.clip();
31619         this.resizeEl.clip();
31620     },
31621     
31622     afterSlide : function(){
31623         this.el.unclip();
31624         this.resizeEl.unclip();
31625     },
31626     
31627     /**
31628      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31629      *   Will fail silently if the {@link #setUrl} method has not been called.
31630      *   This does not activate the panel, just updates its content.
31631      */
31632     refresh : function(){
31633         if(this.refreshDelegate){
31634            this.loaded = false;
31635            this.refreshDelegate();
31636         }
31637     },
31638     
31639     /**
31640      * Destroys this panel
31641      */
31642     destroy : function(){
31643         this.el.removeAllListeners();
31644         var tempEl = document.createElement("span");
31645         tempEl.appendChild(this.el.dom);
31646         tempEl.innerHTML = "";
31647         this.el.remove();
31648         this.el = null;
31649     },
31650     
31651     /**
31652      * form - if the content panel contains a form - this is a reference to it.
31653      * @type {Roo.form.Form}
31654      */
31655     form : false,
31656     /**
31657      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31658      *    This contains a reference to it.
31659      * @type {Roo.View}
31660      */
31661     view : false,
31662     
31663       /**
31664      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31665      * <pre><code>
31666
31667 layout.addxtype({
31668        xtype : 'Form',
31669        items: [ .... ]
31670    }
31671 );
31672
31673 </code></pre>
31674      * @param {Object} cfg Xtype definition of item to add.
31675      */
31676     
31677     addxtype : function(cfg) {
31678         // add form..
31679         if (cfg.xtype.match(/^Form$/)) {
31680             
31681             var el;
31682             //if (this.footer) {
31683             //    el = this.footer.container.insertSibling(false, 'before');
31684             //} else {
31685                 el = this.el.createChild();
31686             //}
31687
31688             this.form = new  Roo.form.Form(cfg);
31689             
31690             
31691             if ( this.form.allItems.length) {
31692                 this.form.render(el.dom);
31693             }
31694             return this.form;
31695         }
31696         // should only have one of theses..
31697         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31698             // views.. should not be just added - used named prop 'view''
31699             
31700             cfg.el = this.el.appendChild(document.createElement("div"));
31701             // factory?
31702             
31703             var ret = new Roo.factory(cfg);
31704              
31705              ret.render && ret.render(false, ''); // render blank..
31706             this.view = ret;
31707             return ret;
31708         }
31709         return false;
31710     }
31711 });
31712
31713 /**
31714  * @class Roo.GridPanel
31715  * @extends Roo.ContentPanel
31716  * @constructor
31717  * Create a new GridPanel.
31718  * @param {Roo.grid.Grid} grid The grid for this panel
31719  * @param {String/Object} config A string to set only the panel's title, or a config object
31720  */
31721 Roo.GridPanel = function(grid, config){
31722     
31723   
31724     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31725         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31726         
31727     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31728     
31729     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31730     
31731     if(this.toolbar){
31732         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31733     }
31734     // xtype created footer. - not sure if will work as we normally have to render first..
31735     if (this.footer && !this.footer.el && this.footer.xtype) {
31736         
31737         this.footer.container = this.grid.getView().getFooterPanel(true);
31738         this.footer.dataSource = this.grid.dataSource;
31739         this.footer = Roo.factory(this.footer, Roo);
31740         
31741     }
31742     
31743     grid.monitorWindowResize = false; // turn off autosizing
31744     grid.autoHeight = false;
31745     grid.autoWidth = false;
31746     this.grid = grid;
31747     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31748 };
31749
31750 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31751     getId : function(){
31752         return this.grid.id;
31753     },
31754     
31755     /**
31756      * Returns the grid for this panel
31757      * @return {Roo.grid.Grid} 
31758      */
31759     getGrid : function(){
31760         return this.grid;    
31761     },
31762     
31763     setSize : function(width, height){
31764         if(!this.ignoreResize(width, height)){
31765             var grid = this.grid;
31766             var size = this.adjustForComponents(width, height);
31767             grid.getGridEl().setSize(size.width, size.height);
31768             grid.autoSize();
31769         }
31770     },
31771     
31772     beforeSlide : function(){
31773         this.grid.getView().scroller.clip();
31774     },
31775     
31776     afterSlide : function(){
31777         this.grid.getView().scroller.unclip();
31778     },
31779     
31780     destroy : function(){
31781         this.grid.destroy();
31782         delete this.grid;
31783         Roo.GridPanel.superclass.destroy.call(this); 
31784     }
31785 });
31786
31787
31788 /**
31789  * @class Roo.NestedLayoutPanel
31790  * @extends Roo.ContentPanel
31791  * @constructor
31792  * Create a new NestedLayoutPanel.
31793  * 
31794  * 
31795  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31796  * @param {String/Object} config A string to set only the title or a config object
31797  */
31798 Roo.NestedLayoutPanel = function(layout, config)
31799 {
31800     // construct with only one argument..
31801     /* FIXME - implement nicer consturctors
31802     if (layout.layout) {
31803         config = layout;
31804         layout = config.layout;
31805         delete config.layout;
31806     }
31807     if (layout.xtype && !layout.getEl) {
31808         // then layout needs constructing..
31809         layout = Roo.factory(layout, Roo);
31810     }
31811     */
31812     
31813     
31814     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31815     
31816     layout.monitorWindowResize = false; // turn off autosizing
31817     this.layout = layout;
31818     this.layout.getEl().addClass("x-layout-nested-layout");
31819     
31820     
31821     
31822     
31823 };
31824
31825 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31826
31827     setSize : function(width, height){
31828         if(!this.ignoreResize(width, height)){
31829             var size = this.adjustForComponents(width, height);
31830             var el = this.layout.getEl();
31831             el.setSize(size.width, size.height);
31832             var touch = el.dom.offsetWidth;
31833             this.layout.layout();
31834             // ie requires a double layout on the first pass
31835             if(Roo.isIE && !this.initialized){
31836                 this.initialized = true;
31837                 this.layout.layout();
31838             }
31839         }
31840     },
31841     
31842     // activate all subpanels if not currently active..
31843     
31844     setActiveState : function(active){
31845         this.active = active;
31846         if(!active){
31847             this.fireEvent("deactivate", this);
31848             return;
31849         }
31850         
31851         this.fireEvent("activate", this);
31852         // not sure if this should happen before or after..
31853         if (!this.layout) {
31854             return; // should not happen..
31855         }
31856         var reg = false;
31857         for (var r in this.layout.regions) {
31858             reg = this.layout.getRegion(r);
31859             if (reg.getActivePanel()) {
31860                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31861                 reg.setActivePanel(reg.getActivePanel());
31862                 continue;
31863             }
31864             if (!reg.panels.length) {
31865                 continue;
31866             }
31867             reg.showPanel(reg.getPanel(0));
31868         }
31869         
31870         
31871         
31872         
31873     },
31874     
31875     /**
31876      * Returns the nested BorderLayout for this panel
31877      * @return {Roo.BorderLayout} 
31878      */
31879     getLayout : function(){
31880         return this.layout;
31881     },
31882     
31883      /**
31884      * Adds a xtype elements to the layout of the nested panel
31885      * <pre><code>
31886
31887 panel.addxtype({
31888        xtype : 'ContentPanel',
31889        region: 'west',
31890        items: [ .... ]
31891    }
31892 );
31893
31894 panel.addxtype({
31895         xtype : 'NestedLayoutPanel',
31896         region: 'west',
31897         layout: {
31898            center: { },
31899            west: { }   
31900         },
31901         items : [ ... list of content panels or nested layout panels.. ]
31902    }
31903 );
31904 </code></pre>
31905      * @param {Object} cfg Xtype definition of item to add.
31906      */
31907     addxtype : function(cfg) {
31908         return this.layout.addxtype(cfg);
31909     
31910     }
31911 });
31912
31913 Roo.ScrollPanel = function(el, config, content){
31914     config = config || {};
31915     config.fitToFrame = true;
31916     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31917     
31918     this.el.dom.style.overflow = "hidden";
31919     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31920     this.el.removeClass("x-layout-inactive-content");
31921     this.el.on("mousewheel", this.onWheel, this);
31922
31923     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31924     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31925     up.unselectable(); down.unselectable();
31926     up.on("click", this.scrollUp, this);
31927     down.on("click", this.scrollDown, this);
31928     up.addClassOnOver("x-scroller-btn-over");
31929     down.addClassOnOver("x-scroller-btn-over");
31930     up.addClassOnClick("x-scroller-btn-click");
31931     down.addClassOnClick("x-scroller-btn-click");
31932     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31933
31934     this.resizeEl = this.el;
31935     this.el = wrap; this.up = up; this.down = down;
31936 };
31937
31938 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31939     increment : 100,
31940     wheelIncrement : 5,
31941     scrollUp : function(){
31942         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31943     },
31944
31945     scrollDown : function(){
31946         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31947     },
31948
31949     afterScroll : function(){
31950         var el = this.resizeEl;
31951         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31952         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31953         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31954     },
31955
31956     setSize : function(){
31957         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31958         this.afterScroll();
31959     },
31960
31961     onWheel : function(e){
31962         var d = e.getWheelDelta();
31963         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31964         this.afterScroll();
31965         e.stopEvent();
31966     },
31967
31968     setContent : function(content, loadScripts){
31969         this.resizeEl.update(content, loadScripts);
31970     }
31971
31972 });
31973
31974
31975
31976 /**
31977  * @class Roo.TreePanel
31978  * @extends Roo.ContentPanel
31979  * Treepanel component
31980  * 
31981  * @constructor
31982  * Create a new TreePanel. - defaults to fit/scoll contents.
31983  * @param {String/Object} config A string to set only the panel's title, or a config object
31984  */
31985 Roo.TreePanel = function(config){
31986     var el = config.el;
31987     var tree = config.tree;
31988     delete config.tree; 
31989     delete config.el; // hopefull!
31990     
31991     // wrapper for IE7 strict & safari scroll issue
31992     
31993     var treeEl = el.createChild();
31994     config.resizeEl = treeEl;
31995     
31996     
31997     
31998     Roo.TreePanel.superclass.constructor.call(this, el, config);
31999  
32000  
32001     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32002     //console.log(tree);
32003     this.on('activate', function()
32004     {
32005         if (this.tree.rendered) {
32006             return;
32007         }
32008         //console.log('render tree');
32009         this.tree.render();
32010     });
32011     // this should not be needed.. - it's actually the 'el' that resizes?
32012     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32013     
32014     //this.on('resize',  function (cp, w, h) {
32015     //        this.tree.innerCt.setWidth(w);
32016     //        this.tree.innerCt.setHeight(h);
32017     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32018     //});
32019
32020         
32021     
32022 };
32023
32024 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32025     fitToFrame : true,
32026     autoScroll : true,
32027     /*
32028      * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32029      */
32030     tree : false
32031
32032 });
32033
32034
32035
32036
32037
32038
32039
32040
32041
32042
32043
32044 /*
32045  * Based on:
32046  * Ext JS Library 1.1.1
32047  * Copyright(c) 2006-2007, Ext JS, LLC.
32048  *
32049  * Originally Released Under LGPL - original licence link has changed is not relivant.
32050  *
32051  * Fork - LGPL
32052  * <script type="text/javascript">
32053  */
32054  
32055
32056 /**
32057  * @class Roo.ReaderLayout
32058  * @extends Roo.BorderLayout
32059  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32060  * center region containing two nested regions (a top one for a list view and one for item preview below),
32061  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32062  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32063  * expedites the setup of the overall layout and regions for this common application style.
32064  * Example:
32065  <pre><code>
32066 var reader = new Roo.ReaderLayout();
32067 var CP = Roo.ContentPanel;  // shortcut for adding
32068
32069 reader.beginUpdate();
32070 reader.add("north", new CP("north", "North"));
32071 reader.add("west", new CP("west", {title: "West"}));
32072 reader.add("east", new CP("east", {title: "East"}));
32073
32074 reader.regions.listView.add(new CP("listView", "List"));
32075 reader.regions.preview.add(new CP("preview", "Preview"));
32076 reader.endUpdate();
32077 </code></pre>
32078 * @constructor
32079 * Create a new ReaderLayout
32080 * @param {Object} config Configuration options
32081 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32082 * document.body if omitted)
32083 */
32084 Roo.ReaderLayout = function(config, renderTo){
32085     var c = config || {size:{}};
32086     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32087         north: c.north !== false ? Roo.apply({
32088             split:false,
32089             initialSize: 32,
32090             titlebar: false
32091         }, c.north) : false,
32092         west: c.west !== false ? Roo.apply({
32093             split:true,
32094             initialSize: 200,
32095             minSize: 175,
32096             maxSize: 400,
32097             titlebar: true,
32098             collapsible: true,
32099             animate: true,
32100             margins:{left:5,right:0,bottom:5,top:5},
32101             cmargins:{left:5,right:5,bottom:5,top:5}
32102         }, c.west) : false,
32103         east: c.east !== false ? Roo.apply({
32104             split:true,
32105             initialSize: 200,
32106             minSize: 175,
32107             maxSize: 400,
32108             titlebar: true,
32109             collapsible: true,
32110             animate: true,
32111             margins:{left:0,right:5,bottom:5,top:5},
32112             cmargins:{left:5,right:5,bottom:5,top:5}
32113         }, c.east) : false,
32114         center: Roo.apply({
32115             tabPosition: 'top',
32116             autoScroll:false,
32117             closeOnTab: true,
32118             titlebar:false,
32119             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32120         }, c.center)
32121     });
32122
32123     this.el.addClass('x-reader');
32124
32125     this.beginUpdate();
32126
32127     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32128         south: c.preview !== false ? Roo.apply({
32129             split:true,
32130             initialSize: 200,
32131             minSize: 100,
32132             autoScroll:true,
32133             collapsible:true,
32134             titlebar: true,
32135             cmargins:{top:5,left:0, right:0, bottom:0}
32136         }, c.preview) : false,
32137         center: Roo.apply({
32138             autoScroll:false,
32139             titlebar:false,
32140             minHeight:200
32141         }, c.listView)
32142     });
32143     this.add('center', new Roo.NestedLayoutPanel(inner,
32144             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32145
32146     this.endUpdate();
32147
32148     this.regions.preview = inner.getRegion('south');
32149     this.regions.listView = inner.getRegion('center');
32150 };
32151
32152 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32153  * Based on:
32154  * Ext JS Library 1.1.1
32155  * Copyright(c) 2006-2007, Ext JS, LLC.
32156  *
32157  * Originally Released Under LGPL - original licence link has changed is not relivant.
32158  *
32159  * Fork - LGPL
32160  * <script type="text/javascript">
32161  */
32162  
32163 /**
32164  * @class Roo.grid.Grid
32165  * @extends Roo.util.Observable
32166  * This class represents the primary interface of a component based grid control.
32167  * <br><br>Usage:<pre><code>
32168  var grid = new Roo.grid.Grid("my-container-id", {
32169      ds: myDataStore,
32170      cm: myColModel,
32171      selModel: mySelectionModel,
32172      autoSizeColumns: true,
32173      monitorWindowResize: false,
32174      trackMouseOver: true
32175  });
32176  // set any options
32177  grid.render();
32178  * </code></pre>
32179  * <b>Common Problems:</b><br/>
32180  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32181  * element will correct this<br/>
32182  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32183  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32184  * are unpredictable.<br/>
32185  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32186  * grid to calculate dimensions/offsets.<br/>
32187   * @constructor
32188  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32189  * The container MUST have some type of size defined for the grid to fill. The container will be
32190  * automatically set to position relative if it isn't already.
32191  * @param {Object} config A config object that sets properties on this grid.
32192  */
32193 Roo.grid.Grid = function(container, config){
32194         // initialize the container
32195         this.container = Roo.get(container);
32196         this.container.update("");
32197         this.container.setStyle("overflow", "hidden");
32198     this.container.addClass('x-grid-container');
32199
32200     this.id = this.container.id;
32201
32202     Roo.apply(this, config);
32203     // check and correct shorthanded configs
32204     if(this.ds){
32205         this.dataSource = this.ds;
32206         delete this.ds;
32207     }
32208     if(this.cm){
32209         this.colModel = this.cm;
32210         delete this.cm;
32211     }
32212     if(this.sm){
32213         this.selModel = this.sm;
32214         delete this.sm;
32215     }
32216
32217     if (this.selModel) {
32218         this.selModel = Roo.factory(this.selModel, Roo.grid);
32219         this.sm = this.selModel;
32220         this.sm.xmodule = this.xmodule || false;
32221     }
32222     if (typeof(this.colModel.config) == 'undefined') {
32223         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32224         this.cm = this.colModel;
32225         this.cm.xmodule = this.xmodule || false;
32226     }
32227     if (this.dataSource) {
32228         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32229         this.ds = this.dataSource;
32230         this.ds.xmodule = this.xmodule || false;
32231          
32232     }
32233     
32234     
32235     
32236     if(this.width){
32237         this.container.setWidth(this.width);
32238     }
32239
32240     if(this.height){
32241         this.container.setHeight(this.height);
32242     }
32243     /** @private */
32244         this.addEvents({
32245         // raw events
32246         /**
32247          * @event click
32248          * The raw click event for the entire grid.
32249          * @param {Roo.EventObject} e
32250          */
32251         "click" : true,
32252         /**
32253          * @event dblclick
32254          * The raw dblclick event for the entire grid.
32255          * @param {Roo.EventObject} e
32256          */
32257         "dblclick" : true,
32258         /**
32259          * @event contextmenu
32260          * The raw contextmenu event for the entire grid.
32261          * @param {Roo.EventObject} e
32262          */
32263         "contextmenu" : true,
32264         /**
32265          * @event mousedown
32266          * The raw mousedown event for the entire grid.
32267          * @param {Roo.EventObject} e
32268          */
32269         "mousedown" : true,
32270         /**
32271          * @event mouseup
32272          * The raw mouseup event for the entire grid.
32273          * @param {Roo.EventObject} e
32274          */
32275         "mouseup" : true,
32276         /**
32277          * @event mouseover
32278          * The raw mouseover event for the entire grid.
32279          * @param {Roo.EventObject} e
32280          */
32281         "mouseover" : true,
32282         /**
32283          * @event mouseout
32284          * The raw mouseout event for the entire grid.
32285          * @param {Roo.EventObject} e
32286          */
32287         "mouseout" : true,
32288         /**
32289          * @event keypress
32290          * The raw keypress event for the entire grid.
32291          * @param {Roo.EventObject} e
32292          */
32293         "keypress" : true,
32294         /**
32295          * @event keydown
32296          * The raw keydown event for the entire grid.
32297          * @param {Roo.EventObject} e
32298          */
32299         "keydown" : true,
32300
32301         // custom events
32302
32303         /**
32304          * @event cellclick
32305          * Fires when a cell is clicked
32306          * @param {Grid} this
32307          * @param {Number} rowIndex
32308          * @param {Number} columnIndex
32309          * @param {Roo.EventObject} e
32310          */
32311         "cellclick" : true,
32312         /**
32313          * @event celldblclick
32314          * Fires when a cell is double clicked
32315          * @param {Grid} this
32316          * @param {Number} rowIndex
32317          * @param {Number} columnIndex
32318          * @param {Roo.EventObject} e
32319          */
32320         "celldblclick" : true,
32321         /**
32322          * @event rowclick
32323          * Fires when a row is clicked
32324          * @param {Grid} this
32325          * @param {Number} rowIndex
32326          * @param {Roo.EventObject} e
32327          */
32328         "rowclick" : true,
32329         /**
32330          * @event rowdblclick
32331          * Fires when a row is double clicked
32332          * @param {Grid} this
32333          * @param {Number} rowIndex
32334          * @param {Roo.EventObject} e
32335          */
32336         "rowdblclick" : true,
32337         /**
32338          * @event headerclick
32339          * Fires when a header is clicked
32340          * @param {Grid} this
32341          * @param {Number} columnIndex
32342          * @param {Roo.EventObject} e
32343          */
32344         "headerclick" : true,
32345         /**
32346          * @event headerdblclick
32347          * Fires when a header cell is double clicked
32348          * @param {Grid} this
32349          * @param {Number} columnIndex
32350          * @param {Roo.EventObject} e
32351          */
32352         "headerdblclick" : true,
32353         /**
32354          * @event rowcontextmenu
32355          * Fires when a row is right clicked
32356          * @param {Grid} this
32357          * @param {Number} rowIndex
32358          * @param {Roo.EventObject} e
32359          */
32360         "rowcontextmenu" : true,
32361         /**
32362          * @event cellcontextmenu
32363          * Fires when a cell is right clicked
32364          * @param {Grid} this
32365          * @param {Number} rowIndex
32366          * @param {Number} cellIndex
32367          * @param {Roo.EventObject} e
32368          */
32369          "cellcontextmenu" : true,
32370         /**
32371          * @event headercontextmenu
32372          * Fires when a header is right clicked
32373          * @param {Grid} this
32374          * @param {Number} columnIndex
32375          * @param {Roo.EventObject} e
32376          */
32377         "headercontextmenu" : true,
32378         /**
32379          * @event bodyscroll
32380          * Fires when the body element is scrolled
32381          * @param {Number} scrollLeft
32382          * @param {Number} scrollTop
32383          */
32384         "bodyscroll" : true,
32385         /**
32386          * @event columnresize
32387          * Fires when the user resizes a column
32388          * @param {Number} columnIndex
32389          * @param {Number} newSize
32390          */
32391         "columnresize" : true,
32392         /**
32393          * @event columnmove
32394          * Fires when the user moves a column
32395          * @param {Number} oldIndex
32396          * @param {Number} newIndex
32397          */
32398         "columnmove" : true,
32399         /**
32400          * @event startdrag
32401          * Fires when row(s) start being dragged
32402          * @param {Grid} this
32403          * @param {Roo.GridDD} dd The drag drop object
32404          * @param {event} e The raw browser event
32405          */
32406         "startdrag" : true,
32407         /**
32408          * @event enddrag
32409          * Fires when a drag operation is complete
32410          * @param {Grid} this
32411          * @param {Roo.GridDD} dd The drag drop object
32412          * @param {event} e The raw browser event
32413          */
32414         "enddrag" : true,
32415         /**
32416          * @event dragdrop
32417          * Fires when dragged row(s) are dropped on a valid DD target
32418          * @param {Grid} this
32419          * @param {Roo.GridDD} dd The drag drop object
32420          * @param {String} targetId The target drag drop object
32421          * @param {event} e The raw browser event
32422          */
32423         "dragdrop" : true,
32424         /**
32425          * @event dragover
32426          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32427          * @param {Grid} this
32428          * @param {Roo.GridDD} dd The drag drop object
32429          * @param {String} targetId The target drag drop object
32430          * @param {event} e The raw browser event
32431          */
32432         "dragover" : true,
32433         /**
32434          * @event dragenter
32435          *  Fires when the dragged row(s) first cross another DD target while being dragged
32436          * @param {Grid} this
32437          * @param {Roo.GridDD} dd The drag drop object
32438          * @param {String} targetId The target drag drop object
32439          * @param {event} e The raw browser event
32440          */
32441         "dragenter" : true,
32442         /**
32443          * @event dragout
32444          * Fires when the dragged row(s) leave another DD target while being dragged
32445          * @param {Grid} this
32446          * @param {Roo.GridDD} dd The drag drop object
32447          * @param {String} targetId The target drag drop object
32448          * @param {event} e The raw browser event
32449          */
32450         "dragout" : true,
32451         /**
32452          * @event rowclass
32453          * Fires when a row is rendered, so you can change add a style to it.
32454          * @param {GridView} gridview   The grid view
32455          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32456          */
32457         'rowclass' : true,
32458
32459         /**
32460          * @event render
32461          * Fires when the grid is rendered
32462          * @param {Grid} grid
32463          */
32464         'render' : true
32465     });
32466
32467     Roo.grid.Grid.superclass.constructor.call(this);
32468 };
32469 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32470     
32471     /**
32472      * @cfg {String} ddGroup - drag drop group.
32473      */
32474       /**
32475      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32476      */
32477
32478     /**
32479      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32480      */
32481     minColumnWidth : 25,
32482
32483     /**
32484      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32485      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32486      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32487      */
32488     autoSizeColumns : false,
32489
32490     /**
32491      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32492      */
32493     autoSizeHeaders : true,
32494
32495     /**
32496      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32497      */
32498     monitorWindowResize : true,
32499
32500     /**
32501      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32502      * rows measured to get a columns size. Default is 0 (all rows).
32503      */
32504     maxRowsToMeasure : 0,
32505
32506     /**
32507      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32508      */
32509     trackMouseOver : true,
32510
32511     /**
32512     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32513     */
32514       /**
32515     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32516     */
32517     
32518     /**
32519     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32520     */
32521     enableDragDrop : false,
32522     
32523     /**
32524     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32525     */
32526     enableColumnMove : true,
32527     
32528     /**
32529     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32530     */
32531     enableColumnHide : true,
32532     
32533     /**
32534     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32535     */
32536     enableRowHeightSync : false,
32537     
32538     /**
32539     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32540     */
32541     stripeRows : true,
32542     
32543     /**
32544     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32545     */
32546     autoHeight : false,
32547
32548     /**
32549      * @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.
32550      */
32551     autoExpandColumn : false,
32552
32553     /**
32554     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32555     * Default is 50.
32556     */
32557     autoExpandMin : 50,
32558
32559     /**
32560     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32561     */
32562     autoExpandMax : 1000,
32563
32564     /**
32565     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32566     */
32567     view : null,
32568
32569     /**
32570     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32571     */
32572     loadMask : false,
32573     /**
32574     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32575     */
32576     dropTarget: false,
32577     
32578    
32579     
32580     // private
32581     rendered : false,
32582
32583     /**
32584     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32585     * of a fixed width. Default is false.
32586     */
32587     /**
32588     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32589     */
32590     
32591     
32592     /**
32593     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32594     * %0 is replaced with the number of selected rows.
32595     */
32596     ddText : "{0} selected row{1}",
32597     
32598     
32599     /**
32600      * Called once after all setup has been completed and the grid is ready to be rendered.
32601      * @return {Roo.grid.Grid} this
32602      */
32603     render : function()
32604     {
32605         var c = this.container;
32606         // try to detect autoHeight/width mode
32607         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32608             this.autoHeight = true;
32609         }
32610         var view = this.getView();
32611         view.init(this);
32612
32613         c.on("click", this.onClick, this);
32614         c.on("dblclick", this.onDblClick, this);
32615         c.on("contextmenu", this.onContextMenu, this);
32616         c.on("keydown", this.onKeyDown, this);
32617         if (Roo.isTouch) {
32618             c.on("touchstart", this.onTouchStart, this);
32619         }
32620
32621         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32622
32623         this.getSelectionModel().init(this);
32624
32625         view.render();
32626
32627         if(this.loadMask){
32628             this.loadMask = new Roo.LoadMask(this.container,
32629                     Roo.apply({store:this.dataSource}, this.loadMask));
32630         }
32631         
32632         
32633         if (this.toolbar && this.toolbar.xtype) {
32634             this.toolbar.container = this.getView().getHeaderPanel(true);
32635             this.toolbar = new Roo.Toolbar(this.toolbar);
32636         }
32637         if (this.footer && this.footer.xtype) {
32638             this.footer.dataSource = this.getDataSource();
32639             this.footer.container = this.getView().getFooterPanel(true);
32640             this.footer = Roo.factory(this.footer, Roo);
32641         }
32642         if (this.dropTarget && this.dropTarget.xtype) {
32643             delete this.dropTarget.xtype;
32644             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32645         }
32646         
32647         
32648         this.rendered = true;
32649         this.fireEvent('render', this);
32650         return this;
32651     },
32652
32653     /**
32654      * Reconfigures the grid to use a different Store and Column Model.
32655      * The View will be bound to the new objects and refreshed.
32656      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32657      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32658      */
32659     reconfigure : function(dataSource, colModel){
32660         if(this.loadMask){
32661             this.loadMask.destroy();
32662             this.loadMask = new Roo.LoadMask(this.container,
32663                     Roo.apply({store:dataSource}, this.loadMask));
32664         }
32665         this.view.bind(dataSource, colModel);
32666         this.dataSource = dataSource;
32667         this.colModel = colModel;
32668         this.view.refresh(true);
32669     },
32670     /**
32671      * addColumns
32672      * Add's a column, default at the end..
32673      
32674      * @param {int} position to add (default end)
32675      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32676      */
32677     addColumns : function(pos, ar)
32678     {
32679         
32680         for (var i =0;i< ar.length;i++) {
32681             var cfg = ar[i];
32682             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32683             this.cm.lookup[cfg.id] = cfg;
32684         }
32685         
32686         
32687         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32688             pos = this.cm.config.length; //this.cm.config.push(cfg);
32689         } 
32690         pos = Math.max(0,pos);
32691         ar.unshift(0);
32692         ar.unshift(pos);
32693         this.cm.config.splice.apply(this.cm.config, ar);
32694         
32695         
32696         
32697         this.view.generateRules(this.cm);
32698         this.view.refresh(true);
32699         
32700     },
32701     
32702     
32703     
32704     
32705     // private
32706     onKeyDown : function(e){
32707         this.fireEvent("keydown", e);
32708     },
32709
32710     /**
32711      * Destroy this grid.
32712      * @param {Boolean} removeEl True to remove the element
32713      */
32714     destroy : function(removeEl, keepListeners){
32715         if(this.loadMask){
32716             this.loadMask.destroy();
32717         }
32718         var c = this.container;
32719         c.removeAllListeners();
32720         this.view.destroy();
32721         this.colModel.purgeListeners();
32722         if(!keepListeners){
32723             this.purgeListeners();
32724         }
32725         c.update("");
32726         if(removeEl === true){
32727             c.remove();
32728         }
32729     },
32730
32731     // private
32732     processEvent : function(name, e){
32733         // does this fire select???
32734         //Roo.log('grid:processEvent '  + name);
32735         
32736         if (name != 'touchstart' ) {
32737             this.fireEvent(name, e);    
32738         }
32739         
32740         var t = e.getTarget();
32741         var v = this.view;
32742         var header = v.findHeaderIndex(t);
32743         if(header !== false){
32744             var ename = name == 'touchstart' ? 'click' : name;
32745              
32746             this.fireEvent("header" + ename, this, header, e);
32747         }else{
32748             var row = v.findRowIndex(t);
32749             var cell = v.findCellIndex(t);
32750             if (name == 'touchstart') {
32751                 // first touch is always a click.
32752                 // hopefull this happens after selection is updated.?
32753                 name = false;
32754                 
32755                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32756                     var cs = this.selModel.getSelectedCell();
32757                     if (row == cs[0] && cell == cs[1]){
32758                         name = 'dblclick';
32759                     }
32760                 }
32761                 if (typeof(this.selModel.getSelections) != 'undefined') {
32762                     var cs = this.selModel.getSelections();
32763                     var ds = this.dataSource;
32764                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32765                         name = 'dblclick';
32766                     }
32767                 }
32768                 if (!name) {
32769                     return;
32770                 }
32771             }
32772             
32773             
32774             if(row !== false){
32775                 this.fireEvent("row" + name, this, row, e);
32776                 if(cell !== false){
32777                     this.fireEvent("cell" + name, this, row, cell, e);
32778                 }
32779             }
32780         }
32781     },
32782
32783     // private
32784     onClick : function(e){
32785         this.processEvent("click", e);
32786     },
32787    // private
32788     onTouchStart : function(e){
32789         this.processEvent("touchstart", e);
32790     },
32791
32792     // private
32793     onContextMenu : function(e, t){
32794         this.processEvent("contextmenu", e);
32795     },
32796
32797     // private
32798     onDblClick : function(e){
32799         this.processEvent("dblclick", e);
32800     },
32801
32802     // private
32803     walkCells : function(row, col, step, fn, scope){
32804         var cm = this.colModel, clen = cm.getColumnCount();
32805         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32806         if(step < 0){
32807             if(col < 0){
32808                 row--;
32809                 first = false;
32810             }
32811             while(row >= 0){
32812                 if(!first){
32813                     col = clen-1;
32814                 }
32815                 first = false;
32816                 while(col >= 0){
32817                     if(fn.call(scope || this, row, col, cm) === true){
32818                         return [row, col];
32819                     }
32820                     col--;
32821                 }
32822                 row--;
32823             }
32824         } else {
32825             if(col >= clen){
32826                 row++;
32827                 first = false;
32828             }
32829             while(row < rlen){
32830                 if(!first){
32831                     col = 0;
32832                 }
32833                 first = false;
32834                 while(col < clen){
32835                     if(fn.call(scope || this, row, col, cm) === true){
32836                         return [row, col];
32837                     }
32838                     col++;
32839                 }
32840                 row++;
32841             }
32842         }
32843         return null;
32844     },
32845
32846     // private
32847     getSelections : function(){
32848         return this.selModel.getSelections();
32849     },
32850
32851     /**
32852      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32853      * but if manual update is required this method will initiate it.
32854      */
32855     autoSize : function(){
32856         if(this.rendered){
32857             this.view.layout();
32858             if(this.view.adjustForScroll){
32859                 this.view.adjustForScroll();
32860             }
32861         }
32862     },
32863
32864     /**
32865      * Returns the grid's underlying element.
32866      * @return {Element} The element
32867      */
32868     getGridEl : function(){
32869         return this.container;
32870     },
32871
32872     // private for compatibility, overridden by editor grid
32873     stopEditing : function(){},
32874
32875     /**
32876      * Returns the grid's SelectionModel.
32877      * @return {SelectionModel}
32878      */
32879     getSelectionModel : function(){
32880         if(!this.selModel){
32881             this.selModel = new Roo.grid.RowSelectionModel();
32882         }
32883         return this.selModel;
32884     },
32885
32886     /**
32887      * Returns the grid's DataSource.
32888      * @return {DataSource}
32889      */
32890     getDataSource : function(){
32891         return this.dataSource;
32892     },
32893
32894     /**
32895      * Returns the grid's ColumnModel.
32896      * @return {ColumnModel}
32897      */
32898     getColumnModel : function(){
32899         return this.colModel;
32900     },
32901
32902     /**
32903      * Returns the grid's GridView object.
32904      * @return {GridView}
32905      */
32906     getView : function(){
32907         if(!this.view){
32908             this.view = new Roo.grid.GridView(this.viewConfig);
32909             this.relayEvents(this.view, [
32910                 "beforerowremoved", "beforerowsinserted",
32911                 "beforerefresh", "rowremoved",
32912                 "rowsinserted", "rowupdated" ,"refresh"
32913             ]);
32914         }
32915         return this.view;
32916     },
32917     /**
32918      * Called to get grid's drag proxy text, by default returns this.ddText.
32919      * Override this to put something different in the dragged text.
32920      * @return {String}
32921      */
32922     getDragDropText : function(){
32923         var count = this.selModel.getCount();
32924         return String.format(this.ddText, count, count == 1 ? '' : 's');
32925     }
32926 });
32927 /*
32928  * Based on:
32929  * Ext JS Library 1.1.1
32930  * Copyright(c) 2006-2007, Ext JS, LLC.
32931  *
32932  * Originally Released Under LGPL - original licence link has changed is not relivant.
32933  *
32934  * Fork - LGPL
32935  * <script type="text/javascript">
32936  */
32937  
32938 Roo.grid.AbstractGridView = function(){
32939         this.grid = null;
32940         
32941         this.events = {
32942             "beforerowremoved" : true,
32943             "beforerowsinserted" : true,
32944             "beforerefresh" : true,
32945             "rowremoved" : true,
32946             "rowsinserted" : true,
32947             "rowupdated" : true,
32948             "refresh" : true
32949         };
32950     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32951 };
32952
32953 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32954     rowClass : "x-grid-row",
32955     cellClass : "x-grid-cell",
32956     tdClass : "x-grid-td",
32957     hdClass : "x-grid-hd",
32958     splitClass : "x-grid-hd-split",
32959     
32960     init: function(grid){
32961         this.grid = grid;
32962                 var cid = this.grid.getGridEl().id;
32963         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32964         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32965         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32966         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32967         },
32968         
32969     getColumnRenderers : function(){
32970         var renderers = [];
32971         var cm = this.grid.colModel;
32972         var colCount = cm.getColumnCount();
32973         for(var i = 0; i < colCount; i++){
32974             renderers[i] = cm.getRenderer(i);
32975         }
32976         return renderers;
32977     },
32978     
32979     getColumnIds : function(){
32980         var ids = [];
32981         var cm = this.grid.colModel;
32982         var colCount = cm.getColumnCount();
32983         for(var i = 0; i < colCount; i++){
32984             ids[i] = cm.getColumnId(i);
32985         }
32986         return ids;
32987     },
32988     
32989     getDataIndexes : function(){
32990         if(!this.indexMap){
32991             this.indexMap = this.buildIndexMap();
32992         }
32993         return this.indexMap.colToData;
32994     },
32995     
32996     getColumnIndexByDataIndex : function(dataIndex){
32997         if(!this.indexMap){
32998             this.indexMap = this.buildIndexMap();
32999         }
33000         return this.indexMap.dataToCol[dataIndex];
33001     },
33002     
33003     /**
33004      * Set a css style for a column dynamically. 
33005      * @param {Number} colIndex The index of the column
33006      * @param {String} name The css property name
33007      * @param {String} value The css value
33008      */
33009     setCSSStyle : function(colIndex, name, value){
33010         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33011         Roo.util.CSS.updateRule(selector, name, value);
33012     },
33013     
33014     generateRules : function(cm){
33015         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33016         Roo.util.CSS.removeStyleSheet(rulesId);
33017         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33018             var cid = cm.getColumnId(i);
33019             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33020                          this.tdSelector, cid, " {\n}\n",
33021                          this.hdSelector, cid, " {\n}\n",
33022                          this.splitSelector, cid, " {\n}\n");
33023         }
33024         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33025     }
33026 });/*
33027  * Based on:
33028  * Ext JS Library 1.1.1
33029  * Copyright(c) 2006-2007, Ext JS, LLC.
33030  *
33031  * Originally Released Under LGPL - original licence link has changed is not relivant.
33032  *
33033  * Fork - LGPL
33034  * <script type="text/javascript">
33035  */
33036
33037 // private
33038 // This is a support class used internally by the Grid components
33039 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33040     this.grid = grid;
33041     this.view = grid.getView();
33042     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33043     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33044     if(hd2){
33045         this.setHandleElId(Roo.id(hd));
33046         this.setOuterHandleElId(Roo.id(hd2));
33047     }
33048     this.scroll = false;
33049 };
33050 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33051     maxDragWidth: 120,
33052     getDragData : function(e){
33053         var t = Roo.lib.Event.getTarget(e);
33054         var h = this.view.findHeaderCell(t);
33055         if(h){
33056             return {ddel: h.firstChild, header:h};
33057         }
33058         return false;
33059     },
33060
33061     onInitDrag : function(e){
33062         this.view.headersDisabled = true;
33063         var clone = this.dragData.ddel.cloneNode(true);
33064         clone.id = Roo.id();
33065         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33066         this.proxy.update(clone);
33067         return true;
33068     },
33069
33070     afterValidDrop : function(){
33071         var v = this.view;
33072         setTimeout(function(){
33073             v.headersDisabled = false;
33074         }, 50);
33075     },
33076
33077     afterInvalidDrop : function(){
33078         var v = this.view;
33079         setTimeout(function(){
33080             v.headersDisabled = false;
33081         }, 50);
33082     }
33083 });
33084 /*
33085  * Based on:
33086  * Ext JS Library 1.1.1
33087  * Copyright(c) 2006-2007, Ext JS, LLC.
33088  *
33089  * Originally Released Under LGPL - original licence link has changed is not relivant.
33090  *
33091  * Fork - LGPL
33092  * <script type="text/javascript">
33093  */
33094 // private
33095 // This is a support class used internally by the Grid components
33096 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33097     this.grid = grid;
33098     this.view = grid.getView();
33099     // split the proxies so they don't interfere with mouse events
33100     this.proxyTop = Roo.DomHelper.append(document.body, {
33101         cls:"col-move-top", html:"&#160;"
33102     }, true);
33103     this.proxyBottom = Roo.DomHelper.append(document.body, {
33104         cls:"col-move-bottom", html:"&#160;"
33105     }, true);
33106     this.proxyTop.hide = this.proxyBottom.hide = function(){
33107         this.setLeftTop(-100,-100);
33108         this.setStyle("visibility", "hidden");
33109     };
33110     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33111     // temporarily disabled
33112     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33113     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33114 };
33115 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33116     proxyOffsets : [-4, -9],
33117     fly: Roo.Element.fly,
33118
33119     getTargetFromEvent : function(e){
33120         var t = Roo.lib.Event.getTarget(e);
33121         var cindex = this.view.findCellIndex(t);
33122         if(cindex !== false){
33123             return this.view.getHeaderCell(cindex);
33124         }
33125         return null;
33126     },
33127
33128     nextVisible : function(h){
33129         var v = this.view, cm = this.grid.colModel;
33130         h = h.nextSibling;
33131         while(h){
33132             if(!cm.isHidden(v.getCellIndex(h))){
33133                 return h;
33134             }
33135             h = h.nextSibling;
33136         }
33137         return null;
33138     },
33139
33140     prevVisible : function(h){
33141         var v = this.view, cm = this.grid.colModel;
33142         h = h.prevSibling;
33143         while(h){
33144             if(!cm.isHidden(v.getCellIndex(h))){
33145                 return h;
33146             }
33147             h = h.prevSibling;
33148         }
33149         return null;
33150     },
33151
33152     positionIndicator : function(h, n, e){
33153         var x = Roo.lib.Event.getPageX(e);
33154         var r = Roo.lib.Dom.getRegion(n.firstChild);
33155         var px, pt, py = r.top + this.proxyOffsets[1];
33156         if((r.right - x) <= (r.right-r.left)/2){
33157             px = r.right+this.view.borderWidth;
33158             pt = "after";
33159         }else{
33160             px = r.left;
33161             pt = "before";
33162         }
33163         var oldIndex = this.view.getCellIndex(h);
33164         var newIndex = this.view.getCellIndex(n);
33165
33166         if(this.grid.colModel.isFixed(newIndex)){
33167             return false;
33168         }
33169
33170         var locked = this.grid.colModel.isLocked(newIndex);
33171
33172         if(pt == "after"){
33173             newIndex++;
33174         }
33175         if(oldIndex < newIndex){
33176             newIndex--;
33177         }
33178         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33179             return false;
33180         }
33181         px +=  this.proxyOffsets[0];
33182         this.proxyTop.setLeftTop(px, py);
33183         this.proxyTop.show();
33184         if(!this.bottomOffset){
33185             this.bottomOffset = this.view.mainHd.getHeight();
33186         }
33187         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33188         this.proxyBottom.show();
33189         return pt;
33190     },
33191
33192     onNodeEnter : function(n, dd, e, data){
33193         if(data.header != n){
33194             this.positionIndicator(data.header, n, e);
33195         }
33196     },
33197
33198     onNodeOver : function(n, dd, e, data){
33199         var result = false;
33200         if(data.header != n){
33201             result = this.positionIndicator(data.header, n, e);
33202         }
33203         if(!result){
33204             this.proxyTop.hide();
33205             this.proxyBottom.hide();
33206         }
33207         return result ? this.dropAllowed : this.dropNotAllowed;
33208     },
33209
33210     onNodeOut : function(n, dd, e, data){
33211         this.proxyTop.hide();
33212         this.proxyBottom.hide();
33213     },
33214
33215     onNodeDrop : function(n, dd, e, data){
33216         var h = data.header;
33217         if(h != n){
33218             var cm = this.grid.colModel;
33219             var x = Roo.lib.Event.getPageX(e);
33220             var r = Roo.lib.Dom.getRegion(n.firstChild);
33221             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33222             var oldIndex = this.view.getCellIndex(h);
33223             var newIndex = this.view.getCellIndex(n);
33224             var locked = cm.isLocked(newIndex);
33225             if(pt == "after"){
33226                 newIndex++;
33227             }
33228             if(oldIndex < newIndex){
33229                 newIndex--;
33230             }
33231             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33232                 return false;
33233             }
33234             cm.setLocked(oldIndex, locked, true);
33235             cm.moveColumn(oldIndex, newIndex);
33236             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33237             return true;
33238         }
33239         return false;
33240     }
33241 });
33242 /*
33243  * Based on:
33244  * Ext JS Library 1.1.1
33245  * Copyright(c) 2006-2007, Ext JS, LLC.
33246  *
33247  * Originally Released Under LGPL - original licence link has changed is not relivant.
33248  *
33249  * Fork - LGPL
33250  * <script type="text/javascript">
33251  */
33252   
33253 /**
33254  * @class Roo.grid.GridView
33255  * @extends Roo.util.Observable
33256  *
33257  * @constructor
33258  * @param {Object} config
33259  */
33260 Roo.grid.GridView = function(config){
33261     Roo.grid.GridView.superclass.constructor.call(this);
33262     this.el = null;
33263
33264     Roo.apply(this, config);
33265 };
33266
33267 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33268
33269     unselectable :  'unselectable="on"',
33270     unselectableCls :  'x-unselectable',
33271     
33272     
33273     rowClass : "x-grid-row",
33274
33275     cellClass : "x-grid-col",
33276
33277     tdClass : "x-grid-td",
33278
33279     hdClass : "x-grid-hd",
33280
33281     splitClass : "x-grid-split",
33282
33283     sortClasses : ["sort-asc", "sort-desc"],
33284
33285     enableMoveAnim : false,
33286
33287     hlColor: "C3DAF9",
33288
33289     dh : Roo.DomHelper,
33290
33291     fly : Roo.Element.fly,
33292
33293     css : Roo.util.CSS,
33294
33295     borderWidth: 1,
33296
33297     splitOffset: 3,
33298
33299     scrollIncrement : 22,
33300
33301     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33302
33303     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33304
33305     bind : function(ds, cm){
33306         if(this.ds){
33307             this.ds.un("load", this.onLoad, this);
33308             this.ds.un("datachanged", this.onDataChange, this);
33309             this.ds.un("add", this.onAdd, this);
33310             this.ds.un("remove", this.onRemove, this);
33311             this.ds.un("update", this.onUpdate, this);
33312             this.ds.un("clear", this.onClear, this);
33313         }
33314         if(ds){
33315             ds.on("load", this.onLoad, this);
33316             ds.on("datachanged", this.onDataChange, this);
33317             ds.on("add", this.onAdd, this);
33318             ds.on("remove", this.onRemove, this);
33319             ds.on("update", this.onUpdate, this);
33320             ds.on("clear", this.onClear, this);
33321         }
33322         this.ds = ds;
33323
33324         if(this.cm){
33325             this.cm.un("widthchange", this.onColWidthChange, this);
33326             this.cm.un("headerchange", this.onHeaderChange, this);
33327             this.cm.un("hiddenchange", this.onHiddenChange, this);
33328             this.cm.un("columnmoved", this.onColumnMove, this);
33329             this.cm.un("columnlockchange", this.onColumnLock, this);
33330         }
33331         if(cm){
33332             this.generateRules(cm);
33333             cm.on("widthchange", this.onColWidthChange, this);
33334             cm.on("headerchange", this.onHeaderChange, this);
33335             cm.on("hiddenchange", this.onHiddenChange, this);
33336             cm.on("columnmoved", this.onColumnMove, this);
33337             cm.on("columnlockchange", this.onColumnLock, this);
33338         }
33339         this.cm = cm;
33340     },
33341
33342     init: function(grid){
33343         Roo.grid.GridView.superclass.init.call(this, grid);
33344
33345         this.bind(grid.dataSource, grid.colModel);
33346
33347         grid.on("headerclick", this.handleHeaderClick, this);
33348
33349         if(grid.trackMouseOver){
33350             grid.on("mouseover", this.onRowOver, this);
33351             grid.on("mouseout", this.onRowOut, this);
33352         }
33353         grid.cancelTextSelection = function(){};
33354         this.gridId = grid.id;
33355
33356         var tpls = this.templates || {};
33357
33358         if(!tpls.master){
33359             tpls.master = new Roo.Template(
33360                '<div class="x-grid" hidefocus="true">',
33361                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33362                   '<div class="x-grid-topbar"></div>',
33363                   '<div class="x-grid-scroller"><div></div></div>',
33364                   '<div class="x-grid-locked">',
33365                       '<div class="x-grid-header">{lockedHeader}</div>',
33366                       '<div class="x-grid-body">{lockedBody}</div>',
33367                   "</div>",
33368                   '<div class="x-grid-viewport">',
33369                       '<div class="x-grid-header">{header}</div>',
33370                       '<div class="x-grid-body">{body}</div>',
33371                   "</div>",
33372                   '<div class="x-grid-bottombar"></div>',
33373                  
33374                   '<div class="x-grid-resize-proxy">&#160;</div>',
33375                "</div>"
33376             );
33377             tpls.master.disableformats = true;
33378         }
33379
33380         if(!tpls.header){
33381             tpls.header = new Roo.Template(
33382                '<table border="0" cellspacing="0" cellpadding="0">',
33383                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33384                "</table>{splits}"
33385             );
33386             tpls.header.disableformats = true;
33387         }
33388         tpls.header.compile();
33389
33390         if(!tpls.hcell){
33391             tpls.hcell = new Roo.Template(
33392                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33393                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33394                 "</div></td>"
33395              );
33396              tpls.hcell.disableFormats = true;
33397         }
33398         tpls.hcell.compile();
33399
33400         if(!tpls.hsplit){
33401             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33402                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33403             tpls.hsplit.disableFormats = true;
33404         }
33405         tpls.hsplit.compile();
33406
33407         if(!tpls.body){
33408             tpls.body = new Roo.Template(
33409                '<table border="0" cellspacing="0" cellpadding="0">',
33410                "<tbody>{rows}</tbody>",
33411                "</table>"
33412             );
33413             tpls.body.disableFormats = true;
33414         }
33415         tpls.body.compile();
33416
33417         if(!tpls.row){
33418             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33419             tpls.row.disableFormats = true;
33420         }
33421         tpls.row.compile();
33422
33423         if(!tpls.cell){
33424             tpls.cell = new Roo.Template(
33425                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33426                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33427                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33428                 "</td>"
33429             );
33430             tpls.cell.disableFormats = true;
33431         }
33432         tpls.cell.compile();
33433
33434         this.templates = tpls;
33435     },
33436
33437     // remap these for backwards compat
33438     onColWidthChange : function(){
33439         this.updateColumns.apply(this, arguments);
33440     },
33441     onHeaderChange : function(){
33442         this.updateHeaders.apply(this, arguments);
33443     }, 
33444     onHiddenChange : function(){
33445         this.handleHiddenChange.apply(this, arguments);
33446     },
33447     onColumnMove : function(){
33448         this.handleColumnMove.apply(this, arguments);
33449     },
33450     onColumnLock : function(){
33451         this.handleLockChange.apply(this, arguments);
33452     },
33453
33454     onDataChange : function(){
33455         this.refresh();
33456         this.updateHeaderSortState();
33457     },
33458
33459     onClear : function(){
33460         this.refresh();
33461     },
33462
33463     onUpdate : function(ds, record){
33464         this.refreshRow(record);
33465     },
33466
33467     refreshRow : function(record){
33468         var ds = this.ds, index;
33469         if(typeof record == 'number'){
33470             index = record;
33471             record = ds.getAt(index);
33472         }else{
33473             index = ds.indexOf(record);
33474         }
33475         this.insertRows(ds, index, index, true);
33476         this.onRemove(ds, record, index+1, true);
33477         this.syncRowHeights(index, index);
33478         this.layout();
33479         this.fireEvent("rowupdated", this, index, record);
33480     },
33481
33482     onAdd : function(ds, records, index){
33483         this.insertRows(ds, index, index + (records.length-1));
33484     },
33485
33486     onRemove : function(ds, record, index, isUpdate){
33487         if(isUpdate !== true){
33488             this.fireEvent("beforerowremoved", this, index, record);
33489         }
33490         var bt = this.getBodyTable(), lt = this.getLockedTable();
33491         if(bt.rows[index]){
33492             bt.firstChild.removeChild(bt.rows[index]);
33493         }
33494         if(lt.rows[index]){
33495             lt.firstChild.removeChild(lt.rows[index]);
33496         }
33497         if(isUpdate !== true){
33498             this.stripeRows(index);
33499             this.syncRowHeights(index, index);
33500             this.layout();
33501             this.fireEvent("rowremoved", this, index, record);
33502         }
33503     },
33504
33505     onLoad : function(){
33506         this.scrollToTop();
33507     },
33508
33509     /**
33510      * Scrolls the grid to the top
33511      */
33512     scrollToTop : function(){
33513         if(this.scroller){
33514             this.scroller.dom.scrollTop = 0;
33515             this.syncScroll();
33516         }
33517     },
33518
33519     /**
33520      * Gets a panel in the header of the grid that can be used for toolbars etc.
33521      * After modifying the contents of this panel a call to grid.autoSize() may be
33522      * required to register any changes in size.
33523      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33524      * @return Roo.Element
33525      */
33526     getHeaderPanel : function(doShow){
33527         if(doShow){
33528             this.headerPanel.show();
33529         }
33530         return this.headerPanel;
33531     },
33532
33533     /**
33534      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33535      * After modifying the contents of this panel a call to grid.autoSize() may be
33536      * required to register any changes in size.
33537      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33538      * @return Roo.Element
33539      */
33540     getFooterPanel : function(doShow){
33541         if(doShow){
33542             this.footerPanel.show();
33543         }
33544         return this.footerPanel;
33545     },
33546
33547     initElements : function(){
33548         var E = Roo.Element;
33549         var el = this.grid.getGridEl().dom.firstChild;
33550         var cs = el.childNodes;
33551
33552         this.el = new E(el);
33553         
33554          this.focusEl = new E(el.firstChild);
33555         this.focusEl.swallowEvent("click", true);
33556         
33557         this.headerPanel = new E(cs[1]);
33558         this.headerPanel.enableDisplayMode("block");
33559
33560         this.scroller = new E(cs[2]);
33561         this.scrollSizer = new E(this.scroller.dom.firstChild);
33562
33563         this.lockedWrap = new E(cs[3]);
33564         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33565         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33566
33567         this.mainWrap = new E(cs[4]);
33568         this.mainHd = new E(this.mainWrap.dom.firstChild);
33569         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33570
33571         this.footerPanel = new E(cs[5]);
33572         this.footerPanel.enableDisplayMode("block");
33573
33574         this.resizeProxy = new E(cs[6]);
33575
33576         this.headerSelector = String.format(
33577            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33578            this.lockedHd.id, this.mainHd.id
33579         );
33580
33581         this.splitterSelector = String.format(
33582            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33583            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33584         );
33585     },
33586     idToCssName : function(s)
33587     {
33588         return s.replace(/[^a-z0-9]+/ig, '-');
33589     },
33590
33591     getHeaderCell : function(index){
33592         return Roo.DomQuery.select(this.headerSelector)[index];
33593     },
33594
33595     getHeaderCellMeasure : function(index){
33596         return this.getHeaderCell(index).firstChild;
33597     },
33598
33599     getHeaderCellText : function(index){
33600         return this.getHeaderCell(index).firstChild.firstChild;
33601     },
33602
33603     getLockedTable : function(){
33604         return this.lockedBody.dom.firstChild;
33605     },
33606
33607     getBodyTable : function(){
33608         return this.mainBody.dom.firstChild;
33609     },
33610
33611     getLockedRow : function(index){
33612         return this.getLockedTable().rows[index];
33613     },
33614
33615     getRow : function(index){
33616         return this.getBodyTable().rows[index];
33617     },
33618
33619     getRowComposite : function(index){
33620         if(!this.rowEl){
33621             this.rowEl = new Roo.CompositeElementLite();
33622         }
33623         var els = [], lrow, mrow;
33624         if(lrow = this.getLockedRow(index)){
33625             els.push(lrow);
33626         }
33627         if(mrow = this.getRow(index)){
33628             els.push(mrow);
33629         }
33630         this.rowEl.elements = els;
33631         return this.rowEl;
33632     },
33633     /**
33634      * Gets the 'td' of the cell
33635      * 
33636      * @param {Integer} rowIndex row to select
33637      * @param {Integer} colIndex column to select
33638      * 
33639      * @return {Object} 
33640      */
33641     getCell : function(rowIndex, colIndex){
33642         var locked = this.cm.getLockedCount();
33643         var source;
33644         if(colIndex < locked){
33645             source = this.lockedBody.dom.firstChild;
33646         }else{
33647             source = this.mainBody.dom.firstChild;
33648             colIndex -= locked;
33649         }
33650         return source.rows[rowIndex].childNodes[colIndex];
33651     },
33652
33653     getCellText : function(rowIndex, colIndex){
33654         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33655     },
33656
33657     getCellBox : function(cell){
33658         var b = this.fly(cell).getBox();
33659         if(Roo.isOpera){ // opera fails to report the Y
33660             b.y = cell.offsetTop + this.mainBody.getY();
33661         }
33662         return b;
33663     },
33664
33665     getCellIndex : function(cell){
33666         var id = String(cell.className).match(this.cellRE);
33667         if(id){
33668             return parseInt(id[1], 10);
33669         }
33670         return 0;
33671     },
33672
33673     findHeaderIndex : function(n){
33674         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33675         return r ? this.getCellIndex(r) : false;
33676     },
33677
33678     findHeaderCell : function(n){
33679         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33680         return r ? r : false;
33681     },
33682
33683     findRowIndex : function(n){
33684         if(!n){
33685             return false;
33686         }
33687         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33688         return r ? r.rowIndex : false;
33689     },
33690
33691     findCellIndex : function(node){
33692         var stop = this.el.dom;
33693         while(node && node != stop){
33694             if(this.findRE.test(node.className)){
33695                 return this.getCellIndex(node);
33696             }
33697             node = node.parentNode;
33698         }
33699         return false;
33700     },
33701
33702     getColumnId : function(index){
33703         return this.cm.getColumnId(index);
33704     },
33705
33706     getSplitters : function()
33707     {
33708         if(this.splitterSelector){
33709            return Roo.DomQuery.select(this.splitterSelector);
33710         }else{
33711             return null;
33712       }
33713     },
33714
33715     getSplitter : function(index){
33716         return this.getSplitters()[index];
33717     },
33718
33719     onRowOver : function(e, t){
33720         var row;
33721         if((row = this.findRowIndex(t)) !== false){
33722             this.getRowComposite(row).addClass("x-grid-row-over");
33723         }
33724     },
33725
33726     onRowOut : function(e, t){
33727         var row;
33728         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33729             this.getRowComposite(row).removeClass("x-grid-row-over");
33730         }
33731     },
33732
33733     renderHeaders : function(){
33734         var cm = this.cm;
33735         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33736         var cb = [], lb = [], sb = [], lsb = [], p = {};
33737         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33738             p.cellId = "x-grid-hd-0-" + i;
33739             p.splitId = "x-grid-csplit-0-" + i;
33740             p.id = cm.getColumnId(i);
33741             p.value = cm.getColumnHeader(i) || "";
33742             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33743             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33744             if(!cm.isLocked(i)){
33745                 cb[cb.length] = ct.apply(p);
33746                 sb[sb.length] = st.apply(p);
33747             }else{
33748                 lb[lb.length] = ct.apply(p);
33749                 lsb[lsb.length] = st.apply(p);
33750             }
33751         }
33752         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33753                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33754     },
33755
33756     updateHeaders : function(){
33757         var html = this.renderHeaders();
33758         this.lockedHd.update(html[0]);
33759         this.mainHd.update(html[1]);
33760     },
33761
33762     /**
33763      * Focuses the specified row.
33764      * @param {Number} row The row index
33765      */
33766     focusRow : function(row)
33767     {
33768         //Roo.log('GridView.focusRow');
33769         var x = this.scroller.dom.scrollLeft;
33770         this.focusCell(row, 0, false);
33771         this.scroller.dom.scrollLeft = x;
33772     },
33773
33774     /**
33775      * Focuses the specified cell.
33776      * @param {Number} row The row index
33777      * @param {Number} col The column index
33778      * @param {Boolean} hscroll false to disable horizontal scrolling
33779      */
33780     focusCell : function(row, col, hscroll)
33781     {
33782         //Roo.log('GridView.focusCell');
33783         var el = this.ensureVisible(row, col, hscroll);
33784         this.focusEl.alignTo(el, "tl-tl");
33785         if(Roo.isGecko){
33786             this.focusEl.focus();
33787         }else{
33788             this.focusEl.focus.defer(1, this.focusEl);
33789         }
33790     },
33791
33792     /**
33793      * Scrolls the specified cell into view
33794      * @param {Number} row The row index
33795      * @param {Number} col The column index
33796      * @param {Boolean} hscroll false to disable horizontal scrolling
33797      */
33798     ensureVisible : function(row, col, hscroll)
33799     {
33800         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33801         //return null; //disable for testing.
33802         if(typeof row != "number"){
33803             row = row.rowIndex;
33804         }
33805         if(row < 0 && row >= this.ds.getCount()){
33806             return  null;
33807         }
33808         col = (col !== undefined ? col : 0);
33809         var cm = this.grid.colModel;
33810         while(cm.isHidden(col)){
33811             col++;
33812         }
33813
33814         var el = this.getCell(row, col);
33815         if(!el){
33816             return null;
33817         }
33818         var c = this.scroller.dom;
33819
33820         var ctop = parseInt(el.offsetTop, 10);
33821         var cleft = parseInt(el.offsetLeft, 10);
33822         var cbot = ctop + el.offsetHeight;
33823         var cright = cleft + el.offsetWidth;
33824         
33825         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33826         var stop = parseInt(c.scrollTop, 10);
33827         var sleft = parseInt(c.scrollLeft, 10);
33828         var sbot = stop + ch;
33829         var sright = sleft + c.clientWidth;
33830         /*
33831         Roo.log('GridView.ensureVisible:' +
33832                 ' ctop:' + ctop +
33833                 ' c.clientHeight:' + c.clientHeight +
33834                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33835                 ' stop:' + stop +
33836                 ' cbot:' + cbot +
33837                 ' sbot:' + sbot +
33838                 ' ch:' + ch  
33839                 );
33840         */
33841         if(ctop < stop){
33842             c.scrollTop = ctop;
33843             //Roo.log("set scrolltop to ctop DISABLE?");
33844         }else if(cbot > sbot){
33845             //Roo.log("set scrolltop to cbot-ch");
33846             c.scrollTop = cbot-ch;
33847         }
33848         
33849         if(hscroll !== false){
33850             if(cleft < sleft){
33851                 c.scrollLeft = cleft;
33852             }else if(cright > sright){
33853                 c.scrollLeft = cright-c.clientWidth;
33854             }
33855         }
33856          
33857         return el;
33858     },
33859
33860     updateColumns : function(){
33861         this.grid.stopEditing();
33862         var cm = this.grid.colModel, colIds = this.getColumnIds();
33863         //var totalWidth = cm.getTotalWidth();
33864         var pos = 0;
33865         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33866             //if(cm.isHidden(i)) continue;
33867             var w = cm.getColumnWidth(i);
33868             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33869             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33870         }
33871         this.updateSplitters();
33872     },
33873
33874     generateRules : function(cm){
33875         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33876         Roo.util.CSS.removeStyleSheet(rulesId);
33877         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33878             var cid = cm.getColumnId(i);
33879             var align = '';
33880             if(cm.config[i].align){
33881                 align = 'text-align:'+cm.config[i].align+';';
33882             }
33883             var hidden = '';
33884             if(cm.isHidden(i)){
33885                 hidden = 'display:none;';
33886             }
33887             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33888             ruleBuf.push(
33889                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33890                     this.hdSelector, cid, " {\n", align, width, "}\n",
33891                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33892                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33893         }
33894         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33895     },
33896
33897     updateSplitters : function(){
33898         var cm = this.cm, s = this.getSplitters();
33899         if(s){ // splitters not created yet
33900             var pos = 0, locked = true;
33901             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33902                 if(cm.isHidden(i)) {
33903                     continue;
33904                 }
33905                 var w = cm.getColumnWidth(i); // make sure it's a number
33906                 if(!cm.isLocked(i) && locked){
33907                     pos = 0;
33908                     locked = false;
33909                 }
33910                 pos += w;
33911                 s[i].style.left = (pos-this.splitOffset) + "px";
33912             }
33913         }
33914     },
33915
33916     handleHiddenChange : function(colModel, colIndex, hidden){
33917         if(hidden){
33918             this.hideColumn(colIndex);
33919         }else{
33920             this.unhideColumn(colIndex);
33921         }
33922     },
33923
33924     hideColumn : function(colIndex){
33925         var cid = this.getColumnId(colIndex);
33926         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33927         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33928         if(Roo.isSafari){
33929             this.updateHeaders();
33930         }
33931         this.updateSplitters();
33932         this.layout();
33933     },
33934
33935     unhideColumn : function(colIndex){
33936         var cid = this.getColumnId(colIndex);
33937         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33938         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33939
33940         if(Roo.isSafari){
33941             this.updateHeaders();
33942         }
33943         this.updateSplitters();
33944         this.layout();
33945     },
33946
33947     insertRows : function(dm, firstRow, lastRow, isUpdate){
33948         if(firstRow == 0 && lastRow == dm.getCount()-1){
33949             this.refresh();
33950         }else{
33951             if(!isUpdate){
33952                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33953             }
33954             var s = this.getScrollState();
33955             var markup = this.renderRows(firstRow, lastRow);
33956             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33957             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33958             this.restoreScroll(s);
33959             if(!isUpdate){
33960                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33961                 this.syncRowHeights(firstRow, lastRow);
33962                 this.stripeRows(firstRow);
33963                 this.layout();
33964             }
33965         }
33966     },
33967
33968     bufferRows : function(markup, target, index){
33969         var before = null, trows = target.rows, tbody = target.tBodies[0];
33970         if(index < trows.length){
33971             before = trows[index];
33972         }
33973         var b = document.createElement("div");
33974         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33975         var rows = b.firstChild.rows;
33976         for(var i = 0, len = rows.length; i < len; i++){
33977             if(before){
33978                 tbody.insertBefore(rows[0], before);
33979             }else{
33980                 tbody.appendChild(rows[0]);
33981             }
33982         }
33983         b.innerHTML = "";
33984         b = null;
33985     },
33986
33987     deleteRows : function(dm, firstRow, lastRow){
33988         if(dm.getRowCount()<1){
33989             this.fireEvent("beforerefresh", this);
33990             this.mainBody.update("");
33991             this.lockedBody.update("");
33992             this.fireEvent("refresh", this);
33993         }else{
33994             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33995             var bt = this.getBodyTable();
33996             var tbody = bt.firstChild;
33997             var rows = bt.rows;
33998             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33999                 tbody.removeChild(rows[firstRow]);
34000             }
34001             this.stripeRows(firstRow);
34002             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34003         }
34004     },
34005
34006     updateRows : function(dataSource, firstRow, lastRow){
34007         var s = this.getScrollState();
34008         this.refresh();
34009         this.restoreScroll(s);
34010     },
34011
34012     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34013         if(!noRefresh){
34014            this.refresh();
34015         }
34016         this.updateHeaderSortState();
34017     },
34018
34019     getScrollState : function(){
34020         
34021         var sb = this.scroller.dom;
34022         return {left: sb.scrollLeft, top: sb.scrollTop};
34023     },
34024
34025     stripeRows : function(startRow){
34026         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34027             return;
34028         }
34029         startRow = startRow || 0;
34030         var rows = this.getBodyTable().rows;
34031         var lrows = this.getLockedTable().rows;
34032         var cls = ' x-grid-row-alt ';
34033         for(var i = startRow, len = rows.length; i < len; i++){
34034             var row = rows[i], lrow = lrows[i];
34035             var isAlt = ((i+1) % 2 == 0);
34036             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34037             if(isAlt == hasAlt){
34038                 continue;
34039             }
34040             if(isAlt){
34041                 row.className += " x-grid-row-alt";
34042             }else{
34043                 row.className = row.className.replace("x-grid-row-alt", "");
34044             }
34045             if(lrow){
34046                 lrow.className = row.className;
34047             }
34048         }
34049     },
34050
34051     restoreScroll : function(state){
34052         //Roo.log('GridView.restoreScroll');
34053         var sb = this.scroller.dom;
34054         sb.scrollLeft = state.left;
34055         sb.scrollTop = state.top;
34056         this.syncScroll();
34057     },
34058
34059     syncScroll : function(){
34060         //Roo.log('GridView.syncScroll');
34061         var sb = this.scroller.dom;
34062         var sh = this.mainHd.dom;
34063         var bs = this.mainBody.dom;
34064         var lv = this.lockedBody.dom;
34065         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34066         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34067     },
34068
34069     handleScroll : function(e){
34070         this.syncScroll();
34071         var sb = this.scroller.dom;
34072         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34073         e.stopEvent();
34074     },
34075
34076     handleWheel : function(e){
34077         var d = e.getWheelDelta();
34078         this.scroller.dom.scrollTop -= d*22;
34079         // set this here to prevent jumpy scrolling on large tables
34080         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34081         e.stopEvent();
34082     },
34083
34084     renderRows : function(startRow, endRow){
34085         // pull in all the crap needed to render rows
34086         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34087         var colCount = cm.getColumnCount();
34088
34089         if(ds.getCount() < 1){
34090             return ["", ""];
34091         }
34092
34093         // build a map for all the columns
34094         var cs = [];
34095         for(var i = 0; i < colCount; i++){
34096             var name = cm.getDataIndex(i);
34097             cs[i] = {
34098                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34099                 renderer : cm.getRenderer(i),
34100                 id : cm.getColumnId(i),
34101                 locked : cm.isLocked(i),
34102                 has_editor : cm.isCellEditable(i)
34103             };
34104         }
34105
34106         startRow = startRow || 0;
34107         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34108
34109         // records to render
34110         var rs = ds.getRange(startRow, endRow);
34111
34112         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34113     },
34114
34115     // As much as I hate to duplicate code, this was branched because FireFox really hates
34116     // [].join("") on strings. The performance difference was substantial enough to
34117     // branch this function
34118     doRender : Roo.isGecko ?
34119             function(cs, rs, ds, startRow, colCount, stripe){
34120                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34121                 // buffers
34122                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34123                 
34124                 var hasListener = this.grid.hasListener('rowclass');
34125                 var rowcfg = {};
34126                 for(var j = 0, len = rs.length; j < len; j++){
34127                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34128                     for(var i = 0; i < colCount; i++){
34129                         c = cs[i];
34130                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34131                         p.id = c.id;
34132                         p.css = p.attr = "";
34133                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34134                         if(p.value == undefined || p.value === "") {
34135                             p.value = "&#160;";
34136                         }
34137                         if(c.has_editor){
34138                             p.css += ' x-grid-editable-cell';
34139                         }
34140                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34141                             p.css +=  ' x-grid-dirty-cell';
34142                         }
34143                         var markup = ct.apply(p);
34144                         if(!c.locked){
34145                             cb+= markup;
34146                         }else{
34147                             lcb+= markup;
34148                         }
34149                     }
34150                     var alt = [];
34151                     if(stripe && ((rowIndex+1) % 2 == 0)){
34152                         alt.push("x-grid-row-alt")
34153                     }
34154                     if(r.dirty){
34155                         alt.push(  " x-grid-dirty-row");
34156                     }
34157                     rp.cells = lcb;
34158                     if(this.getRowClass){
34159                         alt.push(this.getRowClass(r, rowIndex));
34160                     }
34161                     if (hasListener) {
34162                         rowcfg = {
34163                              
34164                             record: r,
34165                             rowIndex : rowIndex,
34166                             rowClass : ''
34167                         };
34168                         this.grid.fireEvent('rowclass', this, rowcfg);
34169                         alt.push(rowcfg.rowClass);
34170                     }
34171                     rp.alt = alt.join(" ");
34172                     lbuf+= rt.apply(rp);
34173                     rp.cells = cb;
34174                     buf+=  rt.apply(rp);
34175                 }
34176                 return [lbuf, buf];
34177             } :
34178             function(cs, rs, ds, startRow, colCount, stripe){
34179                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34180                 // buffers
34181                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34182                 var hasListener = this.grid.hasListener('rowclass');
34183  
34184                 var rowcfg = {};
34185                 for(var j = 0, len = rs.length; j < len; j++){
34186                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34187                     for(var i = 0; i < colCount; i++){
34188                         c = cs[i];
34189                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34190                         p.id = c.id;
34191                         p.css = p.attr = "";
34192                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34193                         if(p.value == undefined || p.value === "") {
34194                             p.value = "&#160;";
34195                         }
34196                         //Roo.log(c);
34197                          if(c.has_editor){
34198                             p.css += ' x-grid-editable-cell';
34199                         }
34200                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34201                             p.css += ' x-grid-dirty-cell' 
34202                         }
34203                         
34204                         var markup = ct.apply(p);
34205                         if(!c.locked){
34206                             cb[cb.length] = markup;
34207                         }else{
34208                             lcb[lcb.length] = markup;
34209                         }
34210                     }
34211                     var alt = [];
34212                     if(stripe && ((rowIndex+1) % 2 == 0)){
34213                         alt.push( "x-grid-row-alt");
34214                     }
34215                     if(r.dirty){
34216                         alt.push(" x-grid-dirty-row");
34217                     }
34218                     rp.cells = lcb;
34219                     if(this.getRowClass){
34220                         alt.push( this.getRowClass(r, rowIndex));
34221                     }
34222                     if (hasListener) {
34223                         rowcfg = {
34224                              
34225                             record: r,
34226                             rowIndex : rowIndex,
34227                             rowClass : ''
34228                         };
34229                         this.grid.fireEvent('rowclass', this, rowcfg);
34230                         alt.push(rowcfg.rowClass);
34231                     }
34232                     
34233                     rp.alt = alt.join(" ");
34234                     rp.cells = lcb.join("");
34235                     lbuf[lbuf.length] = rt.apply(rp);
34236                     rp.cells = cb.join("");
34237                     buf[buf.length] =  rt.apply(rp);
34238                 }
34239                 return [lbuf.join(""), buf.join("")];
34240             },
34241
34242     renderBody : function(){
34243         var markup = this.renderRows();
34244         var bt = this.templates.body;
34245         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34246     },
34247
34248     /**
34249      * Refreshes the grid
34250      * @param {Boolean} headersToo
34251      */
34252     refresh : function(headersToo){
34253         this.fireEvent("beforerefresh", this);
34254         this.grid.stopEditing();
34255         var result = this.renderBody();
34256         this.lockedBody.update(result[0]);
34257         this.mainBody.update(result[1]);
34258         if(headersToo === true){
34259             this.updateHeaders();
34260             this.updateColumns();
34261             this.updateSplitters();
34262             this.updateHeaderSortState();
34263         }
34264         this.syncRowHeights();
34265         this.layout();
34266         this.fireEvent("refresh", this);
34267     },
34268
34269     handleColumnMove : function(cm, oldIndex, newIndex){
34270         this.indexMap = null;
34271         var s = this.getScrollState();
34272         this.refresh(true);
34273         this.restoreScroll(s);
34274         this.afterMove(newIndex);
34275     },
34276
34277     afterMove : function(colIndex){
34278         if(this.enableMoveAnim && Roo.enableFx){
34279             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34280         }
34281         // if multisort - fix sortOrder, and reload..
34282         if (this.grid.dataSource.multiSort) {
34283             // the we can call sort again..
34284             var dm = this.grid.dataSource;
34285             var cm = this.grid.colModel;
34286             var so = [];
34287             for(var i = 0; i < cm.config.length; i++ ) {
34288                 
34289                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34290                     continue; // dont' bother, it's not in sort list or being set.
34291                 }
34292                 
34293                 so.push(cm.config[i].dataIndex);
34294             };
34295             dm.sortOrder = so;
34296             dm.load(dm.lastOptions);
34297             
34298             
34299         }
34300         
34301     },
34302
34303     updateCell : function(dm, rowIndex, dataIndex){
34304         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34305         if(typeof colIndex == "undefined"){ // not present in grid
34306             return;
34307         }
34308         var cm = this.grid.colModel;
34309         var cell = this.getCell(rowIndex, colIndex);
34310         var cellText = this.getCellText(rowIndex, colIndex);
34311
34312         var p = {
34313             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34314             id : cm.getColumnId(colIndex),
34315             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34316         };
34317         var renderer = cm.getRenderer(colIndex);
34318         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34319         if(typeof val == "undefined" || val === "") {
34320             val = "&#160;";
34321         }
34322         cellText.innerHTML = val;
34323         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34324         this.syncRowHeights(rowIndex, rowIndex);
34325     },
34326
34327     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34328         var maxWidth = 0;
34329         if(this.grid.autoSizeHeaders){
34330             var h = this.getHeaderCellMeasure(colIndex);
34331             maxWidth = Math.max(maxWidth, h.scrollWidth);
34332         }
34333         var tb, index;
34334         if(this.cm.isLocked(colIndex)){
34335             tb = this.getLockedTable();
34336             index = colIndex;
34337         }else{
34338             tb = this.getBodyTable();
34339             index = colIndex - this.cm.getLockedCount();
34340         }
34341         if(tb && tb.rows){
34342             var rows = tb.rows;
34343             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34344             for(var i = 0; i < stopIndex; i++){
34345                 var cell = rows[i].childNodes[index].firstChild;
34346                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34347             }
34348         }
34349         return maxWidth + /*margin for error in IE*/ 5;
34350     },
34351     /**
34352      * Autofit a column to its content.
34353      * @param {Number} colIndex
34354      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34355      */
34356      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34357          if(this.cm.isHidden(colIndex)){
34358              return; // can't calc a hidden column
34359          }
34360         if(forceMinSize){
34361             var cid = this.cm.getColumnId(colIndex);
34362             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34363            if(this.grid.autoSizeHeaders){
34364                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34365            }
34366         }
34367         var newWidth = this.calcColumnWidth(colIndex);
34368         this.cm.setColumnWidth(colIndex,
34369             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34370         if(!suppressEvent){
34371             this.grid.fireEvent("columnresize", colIndex, newWidth);
34372         }
34373     },
34374
34375     /**
34376      * Autofits all columns to their content and then expands to fit any extra space in the grid
34377      */
34378      autoSizeColumns : function(){
34379         var cm = this.grid.colModel;
34380         var colCount = cm.getColumnCount();
34381         for(var i = 0; i < colCount; i++){
34382             this.autoSizeColumn(i, true, true);
34383         }
34384         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34385             this.fitColumns();
34386         }else{
34387             this.updateColumns();
34388             this.layout();
34389         }
34390     },
34391
34392     /**
34393      * Autofits all columns to the grid's width proportionate with their current size
34394      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34395      */
34396     fitColumns : function(reserveScrollSpace){
34397         var cm = this.grid.colModel;
34398         var colCount = cm.getColumnCount();
34399         var cols = [];
34400         var width = 0;
34401         var i, w;
34402         for (i = 0; i < colCount; i++){
34403             if(!cm.isHidden(i) && !cm.isFixed(i)){
34404                 w = cm.getColumnWidth(i);
34405                 cols.push(i);
34406                 cols.push(w);
34407                 width += w;
34408             }
34409         }
34410         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34411         if(reserveScrollSpace){
34412             avail -= 17;
34413         }
34414         var frac = (avail - cm.getTotalWidth())/width;
34415         while (cols.length){
34416             w = cols.pop();
34417             i = cols.pop();
34418             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34419         }
34420         this.updateColumns();
34421         this.layout();
34422     },
34423
34424     onRowSelect : function(rowIndex){
34425         var row = this.getRowComposite(rowIndex);
34426         row.addClass("x-grid-row-selected");
34427     },
34428
34429     onRowDeselect : function(rowIndex){
34430         var row = this.getRowComposite(rowIndex);
34431         row.removeClass("x-grid-row-selected");
34432     },
34433
34434     onCellSelect : function(row, col){
34435         var cell = this.getCell(row, col);
34436         if(cell){
34437             Roo.fly(cell).addClass("x-grid-cell-selected");
34438         }
34439     },
34440
34441     onCellDeselect : function(row, col){
34442         var cell = this.getCell(row, col);
34443         if(cell){
34444             Roo.fly(cell).removeClass("x-grid-cell-selected");
34445         }
34446     },
34447
34448     updateHeaderSortState : function(){
34449         
34450         // sort state can be single { field: xxx, direction : yyy}
34451         // or   { xxx=>ASC , yyy : DESC ..... }
34452         
34453         var mstate = {};
34454         if (!this.ds.multiSort) { 
34455             var state = this.ds.getSortState();
34456             if(!state){
34457                 return;
34458             }
34459             mstate[state.field] = state.direction;
34460             // FIXME... - this is not used here.. but might be elsewhere..
34461             this.sortState = state;
34462             
34463         } else {
34464             mstate = this.ds.sortToggle;
34465         }
34466         //remove existing sort classes..
34467         
34468         var sc = this.sortClasses;
34469         var hds = this.el.select(this.headerSelector).removeClass(sc);
34470         
34471         for(var f in mstate) {
34472         
34473             var sortColumn = this.cm.findColumnIndex(f);
34474             
34475             if(sortColumn != -1){
34476                 var sortDir = mstate[f];        
34477                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34478             }
34479         }
34480         
34481          
34482         
34483     },
34484
34485
34486     handleHeaderClick : function(g, index,e){
34487         
34488         Roo.log("header click");
34489         
34490         if (Roo.isTouch) {
34491             // touch events on header are handled by context
34492             this.handleHdCtx(g,index,e);
34493             return;
34494         }
34495         
34496         
34497         if(this.headersDisabled){
34498             return;
34499         }
34500         var dm = g.dataSource, cm = g.colModel;
34501         if(!cm.isSortable(index)){
34502             return;
34503         }
34504         g.stopEditing();
34505         
34506         if (dm.multiSort) {
34507             // update the sortOrder
34508             var so = [];
34509             for(var i = 0; i < cm.config.length; i++ ) {
34510                 
34511                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34512                     continue; // dont' bother, it's not in sort list or being set.
34513                 }
34514                 
34515                 so.push(cm.config[i].dataIndex);
34516             };
34517             dm.sortOrder = so;
34518         }
34519         
34520         
34521         dm.sort(cm.getDataIndex(index));
34522     },
34523
34524
34525     destroy : function(){
34526         if(this.colMenu){
34527             this.colMenu.removeAll();
34528             Roo.menu.MenuMgr.unregister(this.colMenu);
34529             this.colMenu.getEl().remove();
34530             delete this.colMenu;
34531         }
34532         if(this.hmenu){
34533             this.hmenu.removeAll();
34534             Roo.menu.MenuMgr.unregister(this.hmenu);
34535             this.hmenu.getEl().remove();
34536             delete this.hmenu;
34537         }
34538         if(this.grid.enableColumnMove){
34539             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34540             if(dds){
34541                 for(var dd in dds){
34542                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34543                         var elid = dds[dd].dragElId;
34544                         dds[dd].unreg();
34545                         Roo.get(elid).remove();
34546                     } else if(dds[dd].config.isTarget){
34547                         dds[dd].proxyTop.remove();
34548                         dds[dd].proxyBottom.remove();
34549                         dds[dd].unreg();
34550                     }
34551                     if(Roo.dd.DDM.locationCache[dd]){
34552                         delete Roo.dd.DDM.locationCache[dd];
34553                     }
34554                 }
34555                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34556             }
34557         }
34558         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34559         this.bind(null, null);
34560         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34561     },
34562
34563     handleLockChange : function(){
34564         this.refresh(true);
34565     },
34566
34567     onDenyColumnLock : function(){
34568
34569     },
34570
34571     onDenyColumnHide : function(){
34572
34573     },
34574
34575     handleHdMenuClick : function(item){
34576         var index = this.hdCtxIndex;
34577         var cm = this.cm, ds = this.ds;
34578         switch(item.id){
34579             case "asc":
34580                 ds.sort(cm.getDataIndex(index), "ASC");
34581                 break;
34582             case "desc":
34583                 ds.sort(cm.getDataIndex(index), "DESC");
34584                 break;
34585             case "lock":
34586                 var lc = cm.getLockedCount();
34587                 if(cm.getColumnCount(true) <= lc+1){
34588                     this.onDenyColumnLock();
34589                     return;
34590                 }
34591                 if(lc != index){
34592                     cm.setLocked(index, true, true);
34593                     cm.moveColumn(index, lc);
34594                     this.grid.fireEvent("columnmove", index, lc);
34595                 }else{
34596                     cm.setLocked(index, true);
34597                 }
34598             break;
34599             case "unlock":
34600                 var lc = cm.getLockedCount();
34601                 if((lc-1) != index){
34602                     cm.setLocked(index, false, true);
34603                     cm.moveColumn(index, lc-1);
34604                     this.grid.fireEvent("columnmove", index, lc-1);
34605                 }else{
34606                     cm.setLocked(index, false);
34607                 }
34608             break;
34609             case 'wider': // used to expand cols on touch..
34610             case 'narrow':
34611                 var cw = cm.getColumnWidth(index);
34612                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34613                 cw = Math.max(0, cw);
34614                 cw = Math.min(cw,4000);
34615                 cm.setColumnWidth(index, cw);
34616                 break;
34617                 
34618             default:
34619                 index = cm.getIndexById(item.id.substr(4));
34620                 if(index != -1){
34621                     if(item.checked && cm.getColumnCount(true) <= 1){
34622                         this.onDenyColumnHide();
34623                         return false;
34624                     }
34625                     cm.setHidden(index, item.checked);
34626                 }
34627         }
34628         return true;
34629     },
34630
34631     beforeColMenuShow : function(){
34632         var cm = this.cm,  colCount = cm.getColumnCount();
34633         this.colMenu.removeAll();
34634         for(var i = 0; i < colCount; i++){
34635             this.colMenu.add(new Roo.menu.CheckItem({
34636                 id: "col-"+cm.getColumnId(i),
34637                 text: cm.getColumnHeader(i),
34638                 checked: !cm.isHidden(i),
34639                 hideOnClick:false
34640             }));
34641         }
34642     },
34643
34644     handleHdCtx : function(g, index, e){
34645         e.stopEvent();
34646         var hd = this.getHeaderCell(index);
34647         this.hdCtxIndex = index;
34648         var ms = this.hmenu.items, cm = this.cm;
34649         ms.get("asc").setDisabled(!cm.isSortable(index));
34650         ms.get("desc").setDisabled(!cm.isSortable(index));
34651         if(this.grid.enableColLock !== false){
34652             ms.get("lock").setDisabled(cm.isLocked(index));
34653             ms.get("unlock").setDisabled(!cm.isLocked(index));
34654         }
34655         this.hmenu.show(hd, "tl-bl");
34656     },
34657
34658     handleHdOver : function(e){
34659         var hd = this.findHeaderCell(e.getTarget());
34660         if(hd && !this.headersDisabled){
34661             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34662                this.fly(hd).addClass("x-grid-hd-over");
34663             }
34664         }
34665     },
34666
34667     handleHdOut : function(e){
34668         var hd = this.findHeaderCell(e.getTarget());
34669         if(hd){
34670             this.fly(hd).removeClass("x-grid-hd-over");
34671         }
34672     },
34673
34674     handleSplitDblClick : function(e, t){
34675         var i = this.getCellIndex(t);
34676         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34677             this.autoSizeColumn(i, true);
34678             this.layout();
34679         }
34680     },
34681
34682     render : function(){
34683
34684         var cm = this.cm;
34685         var colCount = cm.getColumnCount();
34686
34687         if(this.grid.monitorWindowResize === true){
34688             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34689         }
34690         var header = this.renderHeaders();
34691         var body = this.templates.body.apply({rows:""});
34692         var html = this.templates.master.apply({
34693             lockedBody: body,
34694             body: body,
34695             lockedHeader: header[0],
34696             header: header[1]
34697         });
34698
34699         //this.updateColumns();
34700
34701         this.grid.getGridEl().dom.innerHTML = html;
34702
34703         this.initElements();
34704         
34705         // a kludge to fix the random scolling effect in webkit
34706         this.el.on("scroll", function() {
34707             this.el.dom.scrollTop=0; // hopefully not recursive..
34708         },this);
34709
34710         this.scroller.on("scroll", this.handleScroll, this);
34711         this.lockedBody.on("mousewheel", this.handleWheel, this);
34712         this.mainBody.on("mousewheel", this.handleWheel, this);
34713
34714         this.mainHd.on("mouseover", this.handleHdOver, this);
34715         this.mainHd.on("mouseout", this.handleHdOut, this);
34716         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34717                 {delegate: "."+this.splitClass});
34718
34719         this.lockedHd.on("mouseover", this.handleHdOver, this);
34720         this.lockedHd.on("mouseout", this.handleHdOut, this);
34721         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34722                 {delegate: "."+this.splitClass});
34723
34724         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34725             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34726         }
34727
34728         this.updateSplitters();
34729
34730         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34731             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34732             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34733         }
34734
34735         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34736             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34737             this.hmenu.add(
34738                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34739                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34740             );
34741             if(this.grid.enableColLock !== false){
34742                 this.hmenu.add('-',
34743                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34744                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34745                 );
34746             }
34747             if (Roo.isTouch) {
34748                  this.hmenu.add('-',
34749                     {id:"wider", text: this.columnsWiderText},
34750                     {id:"narrow", text: this.columnsNarrowText }
34751                 );
34752                 
34753                  
34754             }
34755             
34756             if(this.grid.enableColumnHide !== false){
34757
34758                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34759                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34760                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34761
34762                 this.hmenu.add('-',
34763                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34764                 );
34765             }
34766             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34767
34768             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34769         }
34770
34771         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34772             this.dd = new Roo.grid.GridDragZone(this.grid, {
34773                 ddGroup : this.grid.ddGroup || 'GridDD'
34774             });
34775             
34776         }
34777
34778         /*
34779         for(var i = 0; i < colCount; i++){
34780             if(cm.isHidden(i)){
34781                 this.hideColumn(i);
34782             }
34783             if(cm.config[i].align){
34784                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34785                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34786             }
34787         }*/
34788         
34789         this.updateHeaderSortState();
34790
34791         this.beforeInitialResize();
34792         this.layout(true);
34793
34794         // two part rendering gives faster view to the user
34795         this.renderPhase2.defer(1, this);
34796     },
34797
34798     renderPhase2 : function(){
34799         // render the rows now
34800         this.refresh();
34801         if(this.grid.autoSizeColumns){
34802             this.autoSizeColumns();
34803         }
34804     },
34805
34806     beforeInitialResize : function(){
34807
34808     },
34809
34810     onColumnSplitterMoved : function(i, w){
34811         this.userResized = true;
34812         var cm = this.grid.colModel;
34813         cm.setColumnWidth(i, w, true);
34814         var cid = cm.getColumnId(i);
34815         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34816         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34817         this.updateSplitters();
34818         this.layout();
34819         this.grid.fireEvent("columnresize", i, w);
34820     },
34821
34822     syncRowHeights : function(startIndex, endIndex){
34823         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34824             startIndex = startIndex || 0;
34825             var mrows = this.getBodyTable().rows;
34826             var lrows = this.getLockedTable().rows;
34827             var len = mrows.length-1;
34828             endIndex = Math.min(endIndex || len, len);
34829             for(var i = startIndex; i <= endIndex; i++){
34830                 var m = mrows[i], l = lrows[i];
34831                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34832                 m.style.height = l.style.height = h + "px";
34833             }
34834         }
34835     },
34836
34837     layout : function(initialRender, is2ndPass)
34838     {
34839         var g = this.grid;
34840         var auto = g.autoHeight;
34841         var scrollOffset = 16;
34842         var c = g.getGridEl(), cm = this.cm,
34843                 expandCol = g.autoExpandColumn,
34844                 gv = this;
34845         //c.beginMeasure();
34846
34847         if(!c.dom.offsetWidth){ // display:none?
34848             if(initialRender){
34849                 this.lockedWrap.show();
34850                 this.mainWrap.show();
34851             }
34852             return;
34853         }
34854
34855         var hasLock = this.cm.isLocked(0);
34856
34857         var tbh = this.headerPanel.getHeight();
34858         var bbh = this.footerPanel.getHeight();
34859
34860         if(auto){
34861             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34862             var newHeight = ch + c.getBorderWidth("tb");
34863             if(g.maxHeight){
34864                 newHeight = Math.min(g.maxHeight, newHeight);
34865             }
34866             c.setHeight(newHeight);
34867         }
34868
34869         if(g.autoWidth){
34870             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34871         }
34872
34873         var s = this.scroller;
34874
34875         var csize = c.getSize(true);
34876
34877         this.el.setSize(csize.width, csize.height);
34878
34879         this.headerPanel.setWidth(csize.width);
34880         this.footerPanel.setWidth(csize.width);
34881
34882         var hdHeight = this.mainHd.getHeight();
34883         var vw = csize.width;
34884         var vh = csize.height - (tbh + bbh);
34885
34886         s.setSize(vw, vh);
34887
34888         var bt = this.getBodyTable();
34889         
34890         if(cm.getLockedCount() == cm.config.length){
34891             bt = this.getLockedTable();
34892         }
34893         
34894         var ltWidth = hasLock ?
34895                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34896
34897         var scrollHeight = bt.offsetHeight;
34898         var scrollWidth = ltWidth + bt.offsetWidth;
34899         var vscroll = false, hscroll = false;
34900
34901         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34902
34903         var lw = this.lockedWrap, mw = this.mainWrap;
34904         var lb = this.lockedBody, mb = this.mainBody;
34905
34906         setTimeout(function(){
34907             var t = s.dom.offsetTop;
34908             var w = s.dom.clientWidth,
34909                 h = s.dom.clientHeight;
34910
34911             lw.setTop(t);
34912             lw.setSize(ltWidth, h);
34913
34914             mw.setLeftTop(ltWidth, t);
34915             mw.setSize(w-ltWidth, h);
34916
34917             lb.setHeight(h-hdHeight);
34918             mb.setHeight(h-hdHeight);
34919
34920             if(is2ndPass !== true && !gv.userResized && expandCol){
34921                 // high speed resize without full column calculation
34922                 
34923                 var ci = cm.getIndexById(expandCol);
34924                 if (ci < 0) {
34925                     ci = cm.findColumnIndex(expandCol);
34926                 }
34927                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34928                 var expandId = cm.getColumnId(ci);
34929                 var  tw = cm.getTotalWidth(false);
34930                 var currentWidth = cm.getColumnWidth(ci);
34931                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34932                 if(currentWidth != cw){
34933                     cm.setColumnWidth(ci, cw, true);
34934                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34935                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34936                     gv.updateSplitters();
34937                     gv.layout(false, true);
34938                 }
34939             }
34940
34941             if(initialRender){
34942                 lw.show();
34943                 mw.show();
34944             }
34945             //c.endMeasure();
34946         }, 10);
34947     },
34948
34949     onWindowResize : function(){
34950         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34951             return;
34952         }
34953         this.layout();
34954     },
34955
34956     appendFooter : function(parentEl){
34957         return null;
34958     },
34959
34960     sortAscText : "Sort Ascending",
34961     sortDescText : "Sort Descending",
34962     lockText : "Lock Column",
34963     unlockText : "Unlock Column",
34964     columnsText : "Columns",
34965  
34966     columnsWiderText : "Wider",
34967     columnsNarrowText : "Thinner"
34968 });
34969
34970
34971 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34972     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34973     this.proxy.el.addClass('x-grid3-col-dd');
34974 };
34975
34976 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34977     handleMouseDown : function(e){
34978
34979     },
34980
34981     callHandleMouseDown : function(e){
34982         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34983     }
34984 });
34985 /*
34986  * Based on:
34987  * Ext JS Library 1.1.1
34988  * Copyright(c) 2006-2007, Ext JS, LLC.
34989  *
34990  * Originally Released Under LGPL - original licence link has changed is not relivant.
34991  *
34992  * Fork - LGPL
34993  * <script type="text/javascript">
34994  */
34995  /**
34996  * @extends Roo.dd.DDProxy
34997  * @class Roo.grid.SplitDragZone
34998  * Support for Column Header resizing
34999  * @constructor
35000  * @param {Object} config
35001  */
35002 // private
35003 // This is a support class used internally by the Grid components
35004 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35005     this.grid = grid;
35006     this.view = grid.getView();
35007     this.proxy = this.view.resizeProxy;
35008     Roo.grid.SplitDragZone.superclass.constructor.call(
35009         this,
35010         hd, // ID
35011         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35012         {  // CONFIG
35013             dragElId : Roo.id(this.proxy.dom),
35014             resizeFrame:false
35015         }
35016     );
35017     
35018     this.setHandleElId(Roo.id(hd));
35019     if (hd2 !== false) {
35020         this.setOuterHandleElId(Roo.id(hd2));
35021     }
35022     
35023     this.scroll = false;
35024 };
35025 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35026     fly: Roo.Element.fly,
35027
35028     b4StartDrag : function(x, y){
35029         this.view.headersDisabled = true;
35030         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35031                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35032         );
35033         this.proxy.setHeight(h);
35034         
35035         // for old system colWidth really stored the actual width?
35036         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35037         // which in reality did not work.. - it worked only for fixed sizes
35038         // for resizable we need to use actual sizes.
35039         var w = this.cm.getColumnWidth(this.cellIndex);
35040         if (!this.view.mainWrap) {
35041             // bootstrap.
35042             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35043         }
35044         
35045         
35046         
35047         // this was w-this.grid.minColumnWidth;
35048         // doesnt really make sense? - w = thie curren width or the rendered one?
35049         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35050         this.resetConstraints();
35051         this.setXConstraint(minw, 1000);
35052         this.setYConstraint(0, 0);
35053         this.minX = x - minw;
35054         this.maxX = x + 1000;
35055         this.startPos = x;
35056         if (!this.view.mainWrap) { // this is Bootstrap code..
35057             this.getDragEl().style.display='block';
35058         }
35059         
35060         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35061     },
35062
35063
35064     handleMouseDown : function(e){
35065         ev = Roo.EventObject.setEvent(e);
35066         var t = this.fly(ev.getTarget());
35067         if(t.hasClass("x-grid-split")){
35068             this.cellIndex = this.view.getCellIndex(t.dom);
35069             this.split = t.dom;
35070             this.cm = this.grid.colModel;
35071             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35072                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35073             }
35074         }
35075     },
35076
35077     endDrag : function(e){
35078         this.view.headersDisabled = false;
35079         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35080         var diff = endX - this.startPos;
35081         // 
35082         var w = this.cm.getColumnWidth(this.cellIndex);
35083         if (!this.view.mainWrap) {
35084             w = 0;
35085         }
35086         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35087     },
35088
35089     autoOffset : function(){
35090         this.setDelta(0,0);
35091     }
35092 });/*
35093  * Based on:
35094  * Ext JS Library 1.1.1
35095  * Copyright(c) 2006-2007, Ext JS, LLC.
35096  *
35097  * Originally Released Under LGPL - original licence link has changed is not relivant.
35098  *
35099  * Fork - LGPL
35100  * <script type="text/javascript">
35101  */
35102  
35103 // private
35104 // This is a support class used internally by the Grid components
35105 Roo.grid.GridDragZone = function(grid, config){
35106     this.view = grid.getView();
35107     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35108     if(this.view.lockedBody){
35109         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35110         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35111     }
35112     this.scroll = false;
35113     this.grid = grid;
35114     this.ddel = document.createElement('div');
35115     this.ddel.className = 'x-grid-dd-wrap';
35116 };
35117
35118 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35119     ddGroup : "GridDD",
35120
35121     getDragData : function(e){
35122         var t = Roo.lib.Event.getTarget(e);
35123         var rowIndex = this.view.findRowIndex(t);
35124         var sm = this.grid.selModel;
35125             
35126         //Roo.log(rowIndex);
35127         
35128         if (sm.getSelectedCell) {
35129             // cell selection..
35130             if (!sm.getSelectedCell()) {
35131                 return false;
35132             }
35133             if (rowIndex != sm.getSelectedCell()[0]) {
35134                 return false;
35135             }
35136         
35137         }
35138         if (sm.getSelections && sm.getSelections().length < 1) {
35139             return false;
35140         }
35141         
35142         
35143         // before it used to all dragging of unseleted... - now we dont do that.
35144         if(rowIndex !== false){
35145             
35146             // if editorgrid.. 
35147             
35148             
35149             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35150                
35151             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35152               //  
35153             //}
35154             if (e.hasModifier()){
35155                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35156             }
35157             
35158             Roo.log("getDragData");
35159             
35160             return {
35161                 grid: this.grid,
35162                 ddel: this.ddel,
35163                 rowIndex: rowIndex,
35164                 selections: sm.getSelections ? sm.getSelections() : (
35165                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35166             };
35167         }
35168         return false;
35169     },
35170     
35171     
35172     onInitDrag : function(e){
35173         var data = this.dragData;
35174         this.ddel.innerHTML = this.grid.getDragDropText();
35175         this.proxy.update(this.ddel);
35176         // fire start drag?
35177     },
35178
35179     afterRepair : function(){
35180         this.dragging = false;
35181     },
35182
35183     getRepairXY : function(e, data){
35184         return false;
35185     },
35186
35187     onEndDrag : function(data, e){
35188         // fire end drag?
35189     },
35190
35191     onValidDrop : function(dd, e, id){
35192         // fire drag drop?
35193         this.hideProxy();
35194     },
35195
35196     beforeInvalidDrop : function(e, id){
35197
35198     }
35199 });/*
35200  * Based on:
35201  * Ext JS Library 1.1.1
35202  * Copyright(c) 2006-2007, Ext JS, LLC.
35203  *
35204  * Originally Released Under LGPL - original licence link has changed is not relivant.
35205  *
35206  * Fork - LGPL
35207  * <script type="text/javascript">
35208  */
35209  
35210
35211 /**
35212  * @class Roo.grid.ColumnModel
35213  * @extends Roo.util.Observable
35214  * This is the default implementation of a ColumnModel used by the Grid. It defines
35215  * the columns in the grid.
35216  * <br>Usage:<br>
35217  <pre><code>
35218  var colModel = new Roo.grid.ColumnModel([
35219         {header: "Ticker", width: 60, sortable: true, locked: true},
35220         {header: "Company Name", width: 150, sortable: true},
35221         {header: "Market Cap.", width: 100, sortable: true},
35222         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35223         {header: "Employees", width: 100, sortable: true, resizable: false}
35224  ]);
35225  </code></pre>
35226  * <p>
35227  
35228  * The config options listed for this class are options which may appear in each
35229  * individual column definition.
35230  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35231  * @constructor
35232  * @param {Object} config An Array of column config objects. See this class's
35233  * config objects for details.
35234 */
35235 Roo.grid.ColumnModel = function(config){
35236         /**
35237      * The config passed into the constructor
35238      */
35239     this.config = []; //config;
35240     this.lookup = {};
35241
35242     // if no id, create one
35243     // if the column does not have a dataIndex mapping,
35244     // map it to the order it is in the config
35245     for(var i = 0, len = config.length; i < len; i++){
35246         this.addColumn(config[i]);
35247         
35248     }
35249
35250     /**
35251      * The width of columns which have no width specified (defaults to 100)
35252      * @type Number
35253      */
35254     this.defaultWidth = 100;
35255
35256     /**
35257      * Default sortable of columns which have no sortable specified (defaults to false)
35258      * @type Boolean
35259      */
35260     this.defaultSortable = false;
35261
35262     this.addEvents({
35263         /**
35264              * @event widthchange
35265              * Fires when the width of a column changes.
35266              * @param {ColumnModel} this
35267              * @param {Number} columnIndex The column index
35268              * @param {Number} newWidth The new width
35269              */
35270             "widthchange": true,
35271         /**
35272              * @event headerchange
35273              * Fires when the text of a header changes.
35274              * @param {ColumnModel} this
35275              * @param {Number} columnIndex The column index
35276              * @param {Number} newText The new header text
35277              */
35278             "headerchange": true,
35279         /**
35280              * @event hiddenchange
35281              * Fires when a column is hidden or "unhidden".
35282              * @param {ColumnModel} this
35283              * @param {Number} columnIndex The column index
35284              * @param {Boolean} hidden true if hidden, false otherwise
35285              */
35286             "hiddenchange": true,
35287             /**
35288          * @event columnmoved
35289          * Fires when a column is moved.
35290          * @param {ColumnModel} this
35291          * @param {Number} oldIndex
35292          * @param {Number} newIndex
35293          */
35294         "columnmoved" : true,
35295         /**
35296          * @event columlockchange
35297          * Fires when a column's locked state is changed
35298          * @param {ColumnModel} this
35299          * @param {Number} colIndex
35300          * @param {Boolean} locked true if locked
35301          */
35302         "columnlockchange" : true
35303     });
35304     Roo.grid.ColumnModel.superclass.constructor.call(this);
35305 };
35306 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35307     /**
35308      * @cfg {String} header The header text to display in the Grid view.
35309      */
35310         /**
35311      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35312      */
35313         /**
35314      * @cfg {String} smHeader Header at Bootsrap Small width
35315      */
35316         /**
35317      * @cfg {String} mdHeader Header at Bootsrap Medium width
35318      */
35319         /**
35320      * @cfg {String} lgHeader Header at Bootsrap Large width
35321      */
35322         /**
35323      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35324      */
35325     /**
35326      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35327      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35328      * specified, the column's index is used as an index into the Record's data Array.
35329      */
35330     /**
35331      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35332      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35333      */
35334     /**
35335      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35336      * Defaults to the value of the {@link #defaultSortable} property.
35337      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35338      */
35339     /**
35340      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35341      */
35342     /**
35343      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35344      */
35345     /**
35346      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35347      */
35348     /**
35349      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35350      */
35351     /**
35352      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35353      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35354      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35355      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35356      */
35357        /**
35358      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35359      */
35360     /**
35361      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35362      */
35363     /**
35364      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35365      */
35366     /**
35367      * @cfg {String} cursor (Optional)
35368      */
35369     /**
35370      * @cfg {String} tooltip (Optional)
35371      */
35372     /**
35373      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35374      */
35375     /**
35376      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35377      */
35378     /**
35379      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35380      */
35381     /**
35382      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35383      */
35384         /**
35385      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35386      */
35387     /**
35388      * Returns the id of the column at the specified index.
35389      * @param {Number} index The column index
35390      * @return {String} the id
35391      */
35392     getColumnId : function(index){
35393         return this.config[index].id;
35394     },
35395
35396     /**
35397      * Returns the column for a specified id.
35398      * @param {String} id The column id
35399      * @return {Object} the column
35400      */
35401     getColumnById : function(id){
35402         return this.lookup[id];
35403     },
35404
35405     
35406     /**
35407      * Returns the column Object for a specified dataIndex.
35408      * @param {String} dataIndex The column dataIndex
35409      * @return {Object|Boolean} the column or false if not found
35410      */
35411     getColumnByDataIndex: function(dataIndex){
35412         var index = this.findColumnIndex(dataIndex);
35413         return index > -1 ? this.config[index] : false;
35414     },
35415     
35416     /**
35417      * Returns the index for a specified column id.
35418      * @param {String} id The column id
35419      * @return {Number} the index, or -1 if not found
35420      */
35421     getIndexById : function(id){
35422         for(var i = 0, len = this.config.length; i < len; i++){
35423             if(this.config[i].id == id){
35424                 return i;
35425             }
35426         }
35427         return -1;
35428     },
35429     
35430     /**
35431      * Returns the index for a specified column dataIndex.
35432      * @param {String} dataIndex The column dataIndex
35433      * @return {Number} the index, or -1 if not found
35434      */
35435     
35436     findColumnIndex : function(dataIndex){
35437         for(var i = 0, len = this.config.length; i < len; i++){
35438             if(this.config[i].dataIndex == dataIndex){
35439                 return i;
35440             }
35441         }
35442         return -1;
35443     },
35444     
35445     
35446     moveColumn : function(oldIndex, newIndex){
35447         var c = this.config[oldIndex];
35448         this.config.splice(oldIndex, 1);
35449         this.config.splice(newIndex, 0, c);
35450         this.dataMap = null;
35451         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35452     },
35453
35454     isLocked : function(colIndex){
35455         return this.config[colIndex].locked === true;
35456     },
35457
35458     setLocked : function(colIndex, value, suppressEvent){
35459         if(this.isLocked(colIndex) == value){
35460             return;
35461         }
35462         this.config[colIndex].locked = value;
35463         if(!suppressEvent){
35464             this.fireEvent("columnlockchange", this, colIndex, value);
35465         }
35466     },
35467
35468     getTotalLockedWidth : function(){
35469         var totalWidth = 0;
35470         for(var i = 0; i < this.config.length; i++){
35471             if(this.isLocked(i) && !this.isHidden(i)){
35472                 this.totalWidth += this.getColumnWidth(i);
35473             }
35474         }
35475         return totalWidth;
35476     },
35477
35478     getLockedCount : function(){
35479         for(var i = 0, len = this.config.length; i < len; i++){
35480             if(!this.isLocked(i)){
35481                 return i;
35482             }
35483         }
35484         
35485         return this.config.length;
35486     },
35487
35488     /**
35489      * Returns the number of columns.
35490      * @return {Number}
35491      */
35492     getColumnCount : function(visibleOnly){
35493         if(visibleOnly === true){
35494             var c = 0;
35495             for(var i = 0, len = this.config.length; i < len; i++){
35496                 if(!this.isHidden(i)){
35497                     c++;
35498                 }
35499             }
35500             return c;
35501         }
35502         return this.config.length;
35503     },
35504
35505     /**
35506      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35507      * @param {Function} fn
35508      * @param {Object} scope (optional)
35509      * @return {Array} result
35510      */
35511     getColumnsBy : function(fn, scope){
35512         var r = [];
35513         for(var i = 0, len = this.config.length; i < len; i++){
35514             var c = this.config[i];
35515             if(fn.call(scope||this, c, i) === true){
35516                 r[r.length] = c;
35517             }
35518         }
35519         return r;
35520     },
35521
35522     /**
35523      * Returns true if the specified column is sortable.
35524      * @param {Number} col The column index
35525      * @return {Boolean}
35526      */
35527     isSortable : function(col){
35528         if(typeof this.config[col].sortable == "undefined"){
35529             return this.defaultSortable;
35530         }
35531         return this.config[col].sortable;
35532     },
35533
35534     /**
35535      * Returns the rendering (formatting) function defined for the column.
35536      * @param {Number} col The column index.
35537      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35538      */
35539     getRenderer : function(col){
35540         if(!this.config[col].renderer){
35541             return Roo.grid.ColumnModel.defaultRenderer;
35542         }
35543         return this.config[col].renderer;
35544     },
35545
35546     /**
35547      * Sets the rendering (formatting) function for a column.
35548      * @param {Number} col The column index
35549      * @param {Function} fn The function to use to process the cell's raw data
35550      * to return HTML markup for the grid view. The render function is called with
35551      * the following parameters:<ul>
35552      * <li>Data value.</li>
35553      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35554      * <li>css A CSS style string to apply to the table cell.</li>
35555      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35556      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35557      * <li>Row index</li>
35558      * <li>Column index</li>
35559      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35560      */
35561     setRenderer : function(col, fn){
35562         this.config[col].renderer = fn;
35563     },
35564
35565     /**
35566      * Returns the width for the specified column.
35567      * @param {Number} col The column index
35568      * @param (optional) {String} gridSize bootstrap width size.
35569      * @return {Number}
35570      */
35571     getColumnWidth : function(col, gridSize)
35572         {
35573                 var cfg = this.config[col];
35574                 
35575                 if (typeof(gridSize) == 'undefined') {
35576                         return cfg.width * 1 || this.defaultWidth;
35577                 }
35578                 if (gridSize === false) { // if we set it..
35579                         return cfg.width || false;
35580                 }
35581                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35582                 
35583                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35584                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35585                                 continue;
35586                         }
35587                         return cfg[ sizes[i] ];
35588                 }
35589                 return 1;
35590                 
35591     },
35592
35593     /**
35594      * Sets the width for a column.
35595      * @param {Number} col The column index
35596      * @param {Number} width The new width
35597      */
35598     setColumnWidth : function(col, width, suppressEvent){
35599         this.config[col].width = width;
35600         this.totalWidth = null;
35601         if(!suppressEvent){
35602              this.fireEvent("widthchange", this, col, width);
35603         }
35604     },
35605
35606     /**
35607      * Returns the total width of all columns.
35608      * @param {Boolean} includeHidden True to include hidden column widths
35609      * @return {Number}
35610      */
35611     getTotalWidth : function(includeHidden){
35612         if(!this.totalWidth){
35613             this.totalWidth = 0;
35614             for(var i = 0, len = this.config.length; i < len; i++){
35615                 if(includeHidden || !this.isHidden(i)){
35616                     this.totalWidth += this.getColumnWidth(i);
35617                 }
35618             }
35619         }
35620         return this.totalWidth;
35621     },
35622
35623     /**
35624      * Returns the header for the specified column.
35625      * @param {Number} col The column index
35626      * @return {String}
35627      */
35628     getColumnHeader : function(col){
35629         return this.config[col].header;
35630     },
35631
35632     /**
35633      * Sets the header for a column.
35634      * @param {Number} col The column index
35635      * @param {String} header The new header
35636      */
35637     setColumnHeader : function(col, header){
35638         this.config[col].header = header;
35639         this.fireEvent("headerchange", this, col, header);
35640     },
35641
35642     /**
35643      * Returns the tooltip for the specified column.
35644      * @param {Number} col The column index
35645      * @return {String}
35646      */
35647     getColumnTooltip : function(col){
35648             return this.config[col].tooltip;
35649     },
35650     /**
35651      * Sets the tooltip for a column.
35652      * @param {Number} col The column index
35653      * @param {String} tooltip The new tooltip
35654      */
35655     setColumnTooltip : function(col, tooltip){
35656             this.config[col].tooltip = tooltip;
35657     },
35658
35659     /**
35660      * Returns the dataIndex for the specified column.
35661      * @param {Number} col The column index
35662      * @return {Number}
35663      */
35664     getDataIndex : function(col){
35665         return this.config[col].dataIndex;
35666     },
35667
35668     /**
35669      * Sets the dataIndex for a column.
35670      * @param {Number} col The column index
35671      * @param {Number} dataIndex The new dataIndex
35672      */
35673     setDataIndex : function(col, dataIndex){
35674         this.config[col].dataIndex = dataIndex;
35675     },
35676
35677     
35678     
35679     /**
35680      * Returns true if the cell is editable.
35681      * @param {Number} colIndex The column index
35682      * @param {Number} rowIndex The row index - this is nto actually used..?
35683      * @return {Boolean}
35684      */
35685     isCellEditable : function(colIndex, rowIndex){
35686         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35687     },
35688
35689     /**
35690      * Returns the editor defined for the cell/column.
35691      * return false or null to disable editing.
35692      * @param {Number} colIndex The column index
35693      * @param {Number} rowIndex The row index
35694      * @return {Object}
35695      */
35696     getCellEditor : function(colIndex, rowIndex){
35697         return this.config[colIndex].editor;
35698     },
35699
35700     /**
35701      * Sets if a column is editable.
35702      * @param {Number} col The column index
35703      * @param {Boolean} editable True if the column is editable
35704      */
35705     setEditable : function(col, editable){
35706         this.config[col].editable = editable;
35707     },
35708
35709
35710     /**
35711      * Returns true if the column is hidden.
35712      * @param {Number} colIndex The column index
35713      * @return {Boolean}
35714      */
35715     isHidden : function(colIndex){
35716         return this.config[colIndex].hidden;
35717     },
35718
35719
35720     /**
35721      * Returns true if the column width cannot be changed
35722      */
35723     isFixed : function(colIndex){
35724         return this.config[colIndex].fixed;
35725     },
35726
35727     /**
35728      * Returns true if the column can be resized
35729      * @return {Boolean}
35730      */
35731     isResizable : function(colIndex){
35732         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35733     },
35734     /**
35735      * Sets if a column is hidden.
35736      * @param {Number} colIndex The column index
35737      * @param {Boolean} hidden True if the column is hidden
35738      */
35739     setHidden : function(colIndex, hidden){
35740         this.config[colIndex].hidden = hidden;
35741         this.totalWidth = null;
35742         this.fireEvent("hiddenchange", this, colIndex, hidden);
35743     },
35744
35745     /**
35746      * Sets the editor for a column.
35747      * @param {Number} col The column index
35748      * @param {Object} editor The editor object
35749      */
35750     setEditor : function(col, editor){
35751         this.config[col].editor = editor;
35752     },
35753     /**
35754      * Add a column (experimental...) - defaults to adding to the end..
35755      * @param {Object} config 
35756     */
35757     addColumn : function(c)
35758     {
35759     
35760         var i = this.config.length;
35761         this.config[i] = c;
35762         
35763         if(typeof c.dataIndex == "undefined"){
35764             c.dataIndex = i;
35765         }
35766         if(typeof c.renderer == "string"){
35767             c.renderer = Roo.util.Format[c.renderer];
35768         }
35769         if(typeof c.id == "undefined"){
35770             c.id = Roo.id();
35771         }
35772         if(c.editor && c.editor.xtype){
35773             c.editor  = Roo.factory(c.editor, Roo.grid);
35774         }
35775         if(c.editor && c.editor.isFormField){
35776             c.editor = new Roo.grid.GridEditor(c.editor);
35777         }
35778         this.lookup[c.id] = c;
35779     }
35780     
35781 });
35782
35783 Roo.grid.ColumnModel.defaultRenderer = function(value)
35784 {
35785     if(typeof value == "object") {
35786         return value;
35787     }
35788         if(typeof value == "string" && value.length < 1){
35789             return "&#160;";
35790         }
35791     
35792         return String.format("{0}", value);
35793 };
35794
35795 // Alias for backwards compatibility
35796 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35797 /*
35798  * Based on:
35799  * Ext JS Library 1.1.1
35800  * Copyright(c) 2006-2007, Ext JS, LLC.
35801  *
35802  * Originally Released Under LGPL - original licence link has changed is not relivant.
35803  *
35804  * Fork - LGPL
35805  * <script type="text/javascript">
35806  */
35807
35808 /**
35809  * @class Roo.grid.AbstractSelectionModel
35810  * @extends Roo.util.Observable
35811  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35812  * implemented by descendant classes.  This class should not be directly instantiated.
35813  * @constructor
35814  */
35815 Roo.grid.AbstractSelectionModel = function(){
35816     this.locked = false;
35817     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35818 };
35819
35820 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35821     /** @ignore Called by the grid automatically. Do not call directly. */
35822     init : function(grid){
35823         this.grid = grid;
35824         this.initEvents();
35825     },
35826
35827     /**
35828      * Locks the selections.
35829      */
35830     lock : function(){
35831         this.locked = true;
35832     },
35833
35834     /**
35835      * Unlocks the selections.
35836      */
35837     unlock : function(){
35838         this.locked = false;
35839     },
35840
35841     /**
35842      * Returns true if the selections are locked.
35843      * @return {Boolean}
35844      */
35845     isLocked : function(){
35846         return this.locked;
35847     }
35848 });/*
35849  * Based on:
35850  * Ext JS Library 1.1.1
35851  * Copyright(c) 2006-2007, Ext JS, LLC.
35852  *
35853  * Originally Released Under LGPL - original licence link has changed is not relivant.
35854  *
35855  * Fork - LGPL
35856  * <script type="text/javascript">
35857  */
35858 /**
35859  * @extends Roo.grid.AbstractSelectionModel
35860  * @class Roo.grid.RowSelectionModel
35861  * The default SelectionModel used by {@link Roo.grid.Grid}.
35862  * It supports multiple selections and keyboard selection/navigation. 
35863  * @constructor
35864  * @param {Object} config
35865  */
35866 Roo.grid.RowSelectionModel = function(config){
35867     Roo.apply(this, config);
35868     this.selections = new Roo.util.MixedCollection(false, function(o){
35869         return o.id;
35870     });
35871
35872     this.last = false;
35873     this.lastActive = false;
35874
35875     this.addEvents({
35876         /**
35877         * @event selectionchange
35878         * Fires when the selection changes
35879         * @param {SelectionModel} this
35880         */
35881        "selectionchange" : true,
35882        /**
35883         * @event afterselectionchange
35884         * Fires after the selection changes (eg. by key press or clicking)
35885         * @param {SelectionModel} this
35886         */
35887        "afterselectionchange" : true,
35888        /**
35889         * @event beforerowselect
35890         * Fires when a row is selected being selected, return false to cancel.
35891         * @param {SelectionModel} this
35892         * @param {Number} rowIndex The selected index
35893         * @param {Boolean} keepExisting False if other selections will be cleared
35894         */
35895        "beforerowselect" : true,
35896        /**
35897         * @event rowselect
35898         * Fires when a row is selected.
35899         * @param {SelectionModel} this
35900         * @param {Number} rowIndex The selected index
35901         * @param {Roo.data.Record} r The record
35902         */
35903        "rowselect" : true,
35904        /**
35905         * @event rowdeselect
35906         * Fires when a row is deselected.
35907         * @param {SelectionModel} this
35908         * @param {Number} rowIndex The selected index
35909         */
35910         "rowdeselect" : true
35911     });
35912     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35913     this.locked = false;
35914 };
35915
35916 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35917     /**
35918      * @cfg {Boolean} singleSelect
35919      * True to allow selection of only one row at a time (defaults to false)
35920      */
35921     singleSelect : false,
35922
35923     // private
35924     initEvents : function(){
35925
35926         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35927             this.grid.on("mousedown", this.handleMouseDown, this);
35928         }else{ // allow click to work like normal
35929             this.grid.on("rowclick", this.handleDragableRowClick, this);
35930         }
35931         // bootstrap does not have a view..
35932         var view = this.grid.view ? this.grid.view : this.grid;
35933         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35934             "up" : function(e){
35935                 if(!e.shiftKey){
35936                     this.selectPrevious(e.shiftKey);
35937                 }else if(this.last !== false && this.lastActive !== false){
35938                     var last = this.last;
35939                     this.selectRange(this.last,  this.lastActive-1);
35940                     view.focusRow(this.lastActive);
35941                     if(last !== false){
35942                         this.last = last;
35943                     }
35944                 }else{
35945                     this.selectFirstRow();
35946                 }
35947                 this.fireEvent("afterselectionchange", this);
35948             },
35949             "down" : function(e){
35950                 if(!e.shiftKey){
35951                     this.selectNext(e.shiftKey);
35952                 }else if(this.last !== false && this.lastActive !== false){
35953                     var last = this.last;
35954                     this.selectRange(this.last,  this.lastActive+1);
35955                     view.focusRow(this.lastActive);
35956                     if(last !== false){
35957                         this.last = last;
35958                     }
35959                 }else{
35960                     this.selectFirstRow();
35961                 }
35962                 this.fireEvent("afterselectionchange", this);
35963             },
35964             scope: this
35965         });
35966
35967          
35968         view.on("refresh", this.onRefresh, this);
35969         view.on("rowupdated", this.onRowUpdated, this);
35970         view.on("rowremoved", this.onRemove, this);
35971     },
35972
35973     // private
35974     onRefresh : function(){
35975         var ds = this.grid.ds, i, v = this.grid.view;
35976         var s = this.selections;
35977         s.each(function(r){
35978             if((i = ds.indexOfId(r.id)) != -1){
35979                 v.onRowSelect(i);
35980                 s.add(ds.getAt(i)); // updating the selection relate data
35981             }else{
35982                 s.remove(r);
35983             }
35984         });
35985     },
35986
35987     // private
35988     onRemove : function(v, index, r){
35989         this.selections.remove(r);
35990     },
35991
35992     // private
35993     onRowUpdated : function(v, index, r){
35994         if(this.isSelected(r)){
35995             v.onRowSelect(index);
35996         }
35997     },
35998
35999     /**
36000      * Select records.
36001      * @param {Array} records The records to select
36002      * @param {Boolean} keepExisting (optional) True to keep existing selections
36003      */
36004     selectRecords : function(records, keepExisting){
36005         if(!keepExisting){
36006             this.clearSelections();
36007         }
36008         var ds = this.grid.ds;
36009         for(var i = 0, len = records.length; i < len; i++){
36010             this.selectRow(ds.indexOf(records[i]), true);
36011         }
36012     },
36013
36014     /**
36015      * Gets the number of selected rows.
36016      * @return {Number}
36017      */
36018     getCount : function(){
36019         return this.selections.length;
36020     },
36021
36022     /**
36023      * Selects the first row in the grid.
36024      */
36025     selectFirstRow : function(){
36026         this.selectRow(0);
36027     },
36028
36029     /**
36030      * Select the last row.
36031      * @param {Boolean} keepExisting (optional) True to keep existing selections
36032      */
36033     selectLastRow : function(keepExisting){
36034         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36035     },
36036
36037     /**
36038      * Selects the row immediately following the last selected row.
36039      * @param {Boolean} keepExisting (optional) True to keep existing selections
36040      */
36041     selectNext : function(keepExisting){
36042         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36043             this.selectRow(this.last+1, keepExisting);
36044             var view = this.grid.view ? this.grid.view : this.grid;
36045             view.focusRow(this.last);
36046         }
36047     },
36048
36049     /**
36050      * Selects the row that precedes the last selected row.
36051      * @param {Boolean} keepExisting (optional) True to keep existing selections
36052      */
36053     selectPrevious : function(keepExisting){
36054         if(this.last){
36055             this.selectRow(this.last-1, keepExisting);
36056             var view = this.grid.view ? this.grid.view : this.grid;
36057             view.focusRow(this.last);
36058         }
36059     },
36060
36061     /**
36062      * Returns the selected records
36063      * @return {Array} Array of selected records
36064      */
36065     getSelections : function(){
36066         return [].concat(this.selections.items);
36067     },
36068
36069     /**
36070      * Returns the first selected record.
36071      * @return {Record}
36072      */
36073     getSelected : function(){
36074         return this.selections.itemAt(0);
36075     },
36076
36077
36078     /**
36079      * Clears all selections.
36080      */
36081     clearSelections : function(fast){
36082         if(this.locked) {
36083             return;
36084         }
36085         if(fast !== true){
36086             var ds = this.grid.ds;
36087             var s = this.selections;
36088             s.each(function(r){
36089                 this.deselectRow(ds.indexOfId(r.id));
36090             }, this);
36091             s.clear();
36092         }else{
36093             this.selections.clear();
36094         }
36095         this.last = false;
36096     },
36097
36098
36099     /**
36100      * Selects all rows.
36101      */
36102     selectAll : function(){
36103         if(this.locked) {
36104             return;
36105         }
36106         this.selections.clear();
36107         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36108             this.selectRow(i, true);
36109         }
36110     },
36111
36112     /**
36113      * Returns True if there is a selection.
36114      * @return {Boolean}
36115      */
36116     hasSelection : function(){
36117         return this.selections.length > 0;
36118     },
36119
36120     /**
36121      * Returns True if the specified row is selected.
36122      * @param {Number/Record} record The record or index of the record to check
36123      * @return {Boolean}
36124      */
36125     isSelected : function(index){
36126         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36127         return (r && this.selections.key(r.id) ? true : false);
36128     },
36129
36130     /**
36131      * Returns True if the specified record id is selected.
36132      * @param {String} id The id of record to check
36133      * @return {Boolean}
36134      */
36135     isIdSelected : function(id){
36136         return (this.selections.key(id) ? true : false);
36137     },
36138
36139     // private
36140     handleMouseDown : function(e, t)
36141     {
36142         var view = this.grid.view ? this.grid.view : this.grid;
36143         var rowIndex;
36144         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36145             return;
36146         };
36147         if(e.shiftKey && this.last !== false){
36148             var last = this.last;
36149             this.selectRange(last, rowIndex, e.ctrlKey);
36150             this.last = last; // reset the last
36151             view.focusRow(rowIndex);
36152         }else{
36153             var isSelected = this.isSelected(rowIndex);
36154             if(e.button !== 0 && isSelected){
36155                 view.focusRow(rowIndex);
36156             }else if(e.ctrlKey && isSelected){
36157                 this.deselectRow(rowIndex);
36158             }else if(!isSelected){
36159                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36160                 view.focusRow(rowIndex);
36161             }
36162         }
36163         this.fireEvent("afterselectionchange", this);
36164     },
36165     // private
36166     handleDragableRowClick :  function(grid, rowIndex, e) 
36167     {
36168         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36169             this.selectRow(rowIndex, false);
36170             var view = this.grid.view ? this.grid.view : this.grid;
36171             view.focusRow(rowIndex);
36172              this.fireEvent("afterselectionchange", this);
36173         }
36174     },
36175     
36176     /**
36177      * Selects multiple rows.
36178      * @param {Array} rows Array of the indexes of the row to select
36179      * @param {Boolean} keepExisting (optional) True to keep existing selections
36180      */
36181     selectRows : function(rows, keepExisting){
36182         if(!keepExisting){
36183             this.clearSelections();
36184         }
36185         for(var i = 0, len = rows.length; i < len; i++){
36186             this.selectRow(rows[i], true);
36187         }
36188     },
36189
36190     /**
36191      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36192      * @param {Number} startRow The index of the first row in the range
36193      * @param {Number} endRow The index of the last row in the range
36194      * @param {Boolean} keepExisting (optional) True to retain existing selections
36195      */
36196     selectRange : function(startRow, endRow, keepExisting){
36197         if(this.locked) {
36198             return;
36199         }
36200         if(!keepExisting){
36201             this.clearSelections();
36202         }
36203         if(startRow <= endRow){
36204             for(var i = startRow; i <= endRow; i++){
36205                 this.selectRow(i, true);
36206             }
36207         }else{
36208             for(var i = startRow; i >= endRow; i--){
36209                 this.selectRow(i, true);
36210             }
36211         }
36212     },
36213
36214     /**
36215      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36216      * @param {Number} startRow The index of the first row in the range
36217      * @param {Number} endRow The index of the last row in the range
36218      */
36219     deselectRange : function(startRow, endRow, preventViewNotify){
36220         if(this.locked) {
36221             return;
36222         }
36223         for(var i = startRow; i <= endRow; i++){
36224             this.deselectRow(i, preventViewNotify);
36225         }
36226     },
36227
36228     /**
36229      * Selects a row.
36230      * @param {Number} row The index of the row to select
36231      * @param {Boolean} keepExisting (optional) True to keep existing selections
36232      */
36233     selectRow : function(index, keepExisting, preventViewNotify){
36234         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36235             return;
36236         }
36237         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36238             if(!keepExisting || this.singleSelect){
36239                 this.clearSelections();
36240             }
36241             var r = this.grid.ds.getAt(index);
36242             this.selections.add(r);
36243             this.last = this.lastActive = index;
36244             if(!preventViewNotify){
36245                 var view = this.grid.view ? this.grid.view : this.grid;
36246                 view.onRowSelect(index);
36247             }
36248             this.fireEvent("rowselect", this, index, r);
36249             this.fireEvent("selectionchange", this);
36250         }
36251     },
36252
36253     /**
36254      * Deselects a row.
36255      * @param {Number} row The index of the row to deselect
36256      */
36257     deselectRow : function(index, preventViewNotify){
36258         if(this.locked) {
36259             return;
36260         }
36261         if(this.last == index){
36262             this.last = false;
36263         }
36264         if(this.lastActive == index){
36265             this.lastActive = false;
36266         }
36267         var r = this.grid.ds.getAt(index);
36268         this.selections.remove(r);
36269         if(!preventViewNotify){
36270             var view = this.grid.view ? this.grid.view : this.grid;
36271             view.onRowDeselect(index);
36272         }
36273         this.fireEvent("rowdeselect", this, index);
36274         this.fireEvent("selectionchange", this);
36275     },
36276
36277     // private
36278     restoreLast : function(){
36279         if(this._last){
36280             this.last = this._last;
36281         }
36282     },
36283
36284     // private
36285     acceptsNav : function(row, col, cm){
36286         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36287     },
36288
36289     // private
36290     onEditorKey : function(field, e){
36291         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36292         if(k == e.TAB){
36293             e.stopEvent();
36294             ed.completeEdit();
36295             if(e.shiftKey){
36296                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36297             }else{
36298                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36299             }
36300         }else if(k == e.ENTER && !e.ctrlKey){
36301             e.stopEvent();
36302             ed.completeEdit();
36303             if(e.shiftKey){
36304                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36305             }else{
36306                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36307             }
36308         }else if(k == e.ESC){
36309             ed.cancelEdit();
36310         }
36311         if(newCell){
36312             g.startEditing(newCell[0], newCell[1]);
36313         }
36314     }
36315 });/*
36316  * Based on:
36317  * Ext JS Library 1.1.1
36318  * Copyright(c) 2006-2007, Ext JS, LLC.
36319  *
36320  * Originally Released Under LGPL - original licence link has changed is not relivant.
36321  *
36322  * Fork - LGPL
36323  * <script type="text/javascript">
36324  */
36325 /**
36326  * @class Roo.grid.CellSelectionModel
36327  * @extends Roo.grid.AbstractSelectionModel
36328  * This class provides the basic implementation for cell selection in a grid.
36329  * @constructor
36330  * @param {Object} config The object containing the configuration of this model.
36331  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36332  */
36333 Roo.grid.CellSelectionModel = function(config){
36334     Roo.apply(this, config);
36335
36336     this.selection = null;
36337
36338     this.addEvents({
36339         /**
36340              * @event beforerowselect
36341              * Fires before a cell is selected.
36342              * @param {SelectionModel} this
36343              * @param {Number} rowIndex The selected row index
36344              * @param {Number} colIndex The selected cell index
36345              */
36346             "beforecellselect" : true,
36347         /**
36348              * @event cellselect
36349              * Fires when a cell is selected.
36350              * @param {SelectionModel} this
36351              * @param {Number} rowIndex The selected row index
36352              * @param {Number} colIndex The selected cell index
36353              */
36354             "cellselect" : true,
36355         /**
36356              * @event selectionchange
36357              * Fires when the active selection changes.
36358              * @param {SelectionModel} this
36359              * @param {Object} selection null for no selection or an object (o) with two properties
36360                 <ul>
36361                 <li>o.record: the record object for the row the selection is in</li>
36362                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36363                 </ul>
36364              */
36365             "selectionchange" : true,
36366         /**
36367              * @event tabend
36368              * Fires when the tab (or enter) was pressed on the last editable cell
36369              * You can use this to trigger add new row.
36370              * @param {SelectionModel} this
36371              */
36372             "tabend" : true,
36373          /**
36374              * @event beforeeditnext
36375              * Fires before the next editable sell is made active
36376              * You can use this to skip to another cell or fire the tabend
36377              *    if you set cell to false
36378              * @param {Object} eventdata object : { cell : [ row, col ] } 
36379              */
36380             "beforeeditnext" : true
36381     });
36382     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36383 };
36384
36385 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36386     
36387     enter_is_tab: false,
36388
36389     /** @ignore */
36390     initEvents : function(){
36391         this.grid.on("mousedown", this.handleMouseDown, this);
36392         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36393         var view = this.grid.view;
36394         view.on("refresh", this.onViewChange, this);
36395         view.on("rowupdated", this.onRowUpdated, this);
36396         view.on("beforerowremoved", this.clearSelections, this);
36397         view.on("beforerowsinserted", this.clearSelections, this);
36398         if(this.grid.isEditor){
36399             this.grid.on("beforeedit", this.beforeEdit,  this);
36400         }
36401     },
36402
36403         //private
36404     beforeEdit : function(e){
36405         this.select(e.row, e.column, false, true, e.record);
36406     },
36407
36408         //private
36409     onRowUpdated : function(v, index, r){
36410         if(this.selection && this.selection.record == r){
36411             v.onCellSelect(index, this.selection.cell[1]);
36412         }
36413     },
36414
36415         //private
36416     onViewChange : function(){
36417         this.clearSelections(true);
36418     },
36419
36420         /**
36421          * Returns the currently selected cell,.
36422          * @return {Array} The selected cell (row, column) or null if none selected.
36423          */
36424     getSelectedCell : function(){
36425         return this.selection ? this.selection.cell : null;
36426     },
36427
36428     /**
36429      * Clears all selections.
36430      * @param {Boolean} true to prevent the gridview from being notified about the change.
36431      */
36432     clearSelections : function(preventNotify){
36433         var s = this.selection;
36434         if(s){
36435             if(preventNotify !== true){
36436                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36437             }
36438             this.selection = null;
36439             this.fireEvent("selectionchange", this, null);
36440         }
36441     },
36442
36443     /**
36444      * Returns true if there is a selection.
36445      * @return {Boolean}
36446      */
36447     hasSelection : function(){
36448         return this.selection ? true : false;
36449     },
36450
36451     /** @ignore */
36452     handleMouseDown : function(e, t){
36453         var v = this.grid.getView();
36454         if(this.isLocked()){
36455             return;
36456         };
36457         var row = v.findRowIndex(t);
36458         var cell = v.findCellIndex(t);
36459         if(row !== false && cell !== false){
36460             this.select(row, cell);
36461         }
36462     },
36463
36464     /**
36465      * Selects a cell.
36466      * @param {Number} rowIndex
36467      * @param {Number} collIndex
36468      */
36469     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36470         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36471             this.clearSelections();
36472             r = r || this.grid.dataSource.getAt(rowIndex);
36473             this.selection = {
36474                 record : r,
36475                 cell : [rowIndex, colIndex]
36476             };
36477             if(!preventViewNotify){
36478                 var v = this.grid.getView();
36479                 v.onCellSelect(rowIndex, colIndex);
36480                 if(preventFocus !== true){
36481                     v.focusCell(rowIndex, colIndex);
36482                 }
36483             }
36484             this.fireEvent("cellselect", this, rowIndex, colIndex);
36485             this.fireEvent("selectionchange", this, this.selection);
36486         }
36487     },
36488
36489         //private
36490     isSelectable : function(rowIndex, colIndex, cm){
36491         return !cm.isHidden(colIndex);
36492     },
36493
36494     /** @ignore */
36495     handleKeyDown : function(e){
36496         //Roo.log('Cell Sel Model handleKeyDown');
36497         if(!e.isNavKeyPress()){
36498             return;
36499         }
36500         var g = this.grid, s = this.selection;
36501         if(!s){
36502             e.stopEvent();
36503             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36504             if(cell){
36505                 this.select(cell[0], cell[1]);
36506             }
36507             return;
36508         }
36509         var sm = this;
36510         var walk = function(row, col, step){
36511             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36512         };
36513         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36514         var newCell;
36515
36516       
36517
36518         switch(k){
36519             case e.TAB:
36520                 // handled by onEditorKey
36521                 if (g.isEditor && g.editing) {
36522                     return;
36523                 }
36524                 if(e.shiftKey) {
36525                     newCell = walk(r, c-1, -1);
36526                 } else {
36527                     newCell = walk(r, c+1, 1);
36528                 }
36529                 break;
36530             
36531             case e.DOWN:
36532                newCell = walk(r+1, c, 1);
36533                 break;
36534             
36535             case e.UP:
36536                 newCell = walk(r-1, c, -1);
36537                 break;
36538             
36539             case e.RIGHT:
36540                 newCell = walk(r, c+1, 1);
36541                 break;
36542             
36543             case e.LEFT:
36544                 newCell = walk(r, c-1, -1);
36545                 break;
36546             
36547             case e.ENTER:
36548                 
36549                 if(g.isEditor && !g.editing){
36550                    g.startEditing(r, c);
36551                    e.stopEvent();
36552                    return;
36553                 }
36554                 
36555                 
36556              break;
36557         };
36558         if(newCell){
36559             this.select(newCell[0], newCell[1]);
36560             e.stopEvent();
36561             
36562         }
36563     },
36564
36565     acceptsNav : function(row, col, cm){
36566         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36567     },
36568     /**
36569      * Selects a cell.
36570      * @param {Number} field (not used) - as it's normally used as a listener
36571      * @param {Number} e - event - fake it by using
36572      *
36573      * var e = Roo.EventObjectImpl.prototype;
36574      * e.keyCode = e.TAB
36575      *
36576      * 
36577      */
36578     onEditorKey : function(field, e){
36579         
36580         var k = e.getKey(),
36581             newCell,
36582             g = this.grid,
36583             ed = g.activeEditor,
36584             forward = false;
36585         ///Roo.log('onEditorKey' + k);
36586         
36587         
36588         if (this.enter_is_tab && k == e.ENTER) {
36589             k = e.TAB;
36590         }
36591         
36592         if(k == e.TAB){
36593             if(e.shiftKey){
36594                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36595             }else{
36596                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36597                 forward = true;
36598             }
36599             
36600             e.stopEvent();
36601             
36602         } else if(k == e.ENTER &&  !e.ctrlKey){
36603             ed.completeEdit();
36604             e.stopEvent();
36605             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36606         
36607                 } else if(k == e.ESC){
36608             ed.cancelEdit();
36609         }
36610                 
36611         if (newCell) {
36612             var ecall = { cell : newCell, forward : forward };
36613             this.fireEvent('beforeeditnext', ecall );
36614             newCell = ecall.cell;
36615                         forward = ecall.forward;
36616         }
36617                 
36618         if(newCell){
36619             //Roo.log('next cell after edit');
36620             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36621         } else if (forward) {
36622             // tabbed past last
36623             this.fireEvent.defer(100, this, ['tabend',this]);
36624         }
36625     }
36626 });/*
36627  * Based on:
36628  * Ext JS Library 1.1.1
36629  * Copyright(c) 2006-2007, Ext JS, LLC.
36630  *
36631  * Originally Released Under LGPL - original licence link has changed is not relivant.
36632  *
36633  * Fork - LGPL
36634  * <script type="text/javascript">
36635  */
36636  
36637 /**
36638  * @class Roo.grid.EditorGrid
36639  * @extends Roo.grid.Grid
36640  * Class for creating and editable grid.
36641  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36642  * The container MUST have some type of size defined for the grid to fill. The container will be 
36643  * automatically set to position relative if it isn't already.
36644  * @param {Object} dataSource The data model to bind to
36645  * @param {Object} colModel The column model with info about this grid's columns
36646  */
36647 Roo.grid.EditorGrid = function(container, config){
36648     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36649     this.getGridEl().addClass("xedit-grid");
36650
36651     if(!this.selModel){
36652         this.selModel = new Roo.grid.CellSelectionModel();
36653     }
36654
36655     this.activeEditor = null;
36656
36657         this.addEvents({
36658             /**
36659              * @event beforeedit
36660              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36661              * <ul style="padding:5px;padding-left:16px;">
36662              * <li>grid - This grid</li>
36663              * <li>record - The record being edited</li>
36664              * <li>field - The field name being edited</li>
36665              * <li>value - The value for the field being edited.</li>
36666              * <li>row - The grid row index</li>
36667              * <li>column - The grid column index</li>
36668              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36669              * </ul>
36670              * @param {Object} e An edit event (see above for description)
36671              */
36672             "beforeedit" : true,
36673             /**
36674              * @event afteredit
36675              * Fires after a cell is edited. <br />
36676              * <ul style="padding:5px;padding-left:16px;">
36677              * <li>grid - This grid</li>
36678              * <li>record - The record being edited</li>
36679              * <li>field - The field name being edited</li>
36680              * <li>value - The value being set</li>
36681              * <li>originalValue - The original value for the field, before the edit.</li>
36682              * <li>row - The grid row index</li>
36683              * <li>column - The grid column index</li>
36684              * </ul>
36685              * @param {Object} e An edit event (see above for description)
36686              */
36687             "afteredit" : true,
36688             /**
36689              * @event validateedit
36690              * Fires after a cell is edited, but before the value is set in the record. 
36691          * You can use this to modify the value being set in the field, Return false
36692              * to cancel the change. The edit event object has the following properties <br />
36693              * <ul style="padding:5px;padding-left:16px;">
36694          * <li>editor - This editor</li>
36695              * <li>grid - This grid</li>
36696              * <li>record - The record being edited</li>
36697              * <li>field - The field name being edited</li>
36698              * <li>value - The value being set</li>
36699              * <li>originalValue - The original value for the field, before the edit.</li>
36700              * <li>row - The grid row index</li>
36701              * <li>column - The grid column index</li>
36702              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36703              * </ul>
36704              * @param {Object} e An edit event (see above for description)
36705              */
36706             "validateedit" : true
36707         });
36708     this.on("bodyscroll", this.stopEditing,  this);
36709     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36710 };
36711
36712 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36713     /**
36714      * @cfg {Number} clicksToEdit
36715      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36716      */
36717     clicksToEdit: 2,
36718
36719     // private
36720     isEditor : true,
36721     // private
36722     trackMouseOver: false, // causes very odd FF errors
36723
36724     onCellDblClick : function(g, row, col){
36725         this.startEditing(row, col);
36726     },
36727
36728     onEditComplete : function(ed, value, startValue){
36729         this.editing = false;
36730         this.activeEditor = null;
36731         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36732         var r = ed.record;
36733         var field = this.colModel.getDataIndex(ed.col);
36734         var e = {
36735             grid: this,
36736             record: r,
36737             field: field,
36738             originalValue: startValue,
36739             value: value,
36740             row: ed.row,
36741             column: ed.col,
36742             cancel:false,
36743             editor: ed
36744         };
36745         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36746         cell.show();
36747           
36748         if(String(value) !== String(startValue)){
36749             
36750             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36751                 r.set(field, e.value);
36752                 // if we are dealing with a combo box..
36753                 // then we also set the 'name' colum to be the displayField
36754                 if (ed.field.displayField && ed.field.name) {
36755                     r.set(ed.field.name, ed.field.el.dom.value);
36756                 }
36757                 
36758                 delete e.cancel; //?? why!!!
36759                 this.fireEvent("afteredit", e);
36760             }
36761         } else {
36762             this.fireEvent("afteredit", e); // always fire it!
36763         }
36764         this.view.focusCell(ed.row, ed.col);
36765     },
36766
36767     /**
36768      * Starts editing the specified for the specified row/column
36769      * @param {Number} rowIndex
36770      * @param {Number} colIndex
36771      */
36772     startEditing : function(row, col){
36773         this.stopEditing();
36774         if(this.colModel.isCellEditable(col, row)){
36775             this.view.ensureVisible(row, col, true);
36776           
36777             var r = this.dataSource.getAt(row);
36778             var field = this.colModel.getDataIndex(col);
36779             var cell = Roo.get(this.view.getCell(row,col));
36780             var e = {
36781                 grid: this,
36782                 record: r,
36783                 field: field,
36784                 value: r.data[field],
36785                 row: row,
36786                 column: col,
36787                 cancel:false 
36788             };
36789             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36790                 this.editing = true;
36791                 var ed = this.colModel.getCellEditor(col, row);
36792                 
36793                 if (!ed) {
36794                     return;
36795                 }
36796                 if(!ed.rendered){
36797                     ed.render(ed.parentEl || document.body);
36798                 }
36799                 ed.field.reset();
36800                
36801                 cell.hide();
36802                 
36803                 (function(){ // complex but required for focus issues in safari, ie and opera
36804                     ed.row = row;
36805                     ed.col = col;
36806                     ed.record = r;
36807                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36808                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36809                     this.activeEditor = ed;
36810                     var v = r.data[field];
36811                     ed.startEdit(this.view.getCell(row, col), v);
36812                     // combo's with 'displayField and name set
36813                     if (ed.field.displayField && ed.field.name) {
36814                         ed.field.el.dom.value = r.data[ed.field.name];
36815                     }
36816                     
36817                     
36818                 }).defer(50, this);
36819             }
36820         }
36821     },
36822         
36823     /**
36824      * Stops any active editing
36825      */
36826     stopEditing : function(){
36827         if(this.activeEditor){
36828             this.activeEditor.completeEdit();
36829         }
36830         this.activeEditor = null;
36831     },
36832         
36833          /**
36834      * Called to get grid's drag proxy text, by default returns this.ddText.
36835      * @return {String}
36836      */
36837     getDragDropText : function(){
36838         var count = this.selModel.getSelectedCell() ? 1 : 0;
36839         return String.format(this.ddText, count, count == 1 ? '' : 's');
36840     }
36841         
36842 });/*
36843  * Based on:
36844  * Ext JS Library 1.1.1
36845  * Copyright(c) 2006-2007, Ext JS, LLC.
36846  *
36847  * Originally Released Under LGPL - original licence link has changed is not relivant.
36848  *
36849  * Fork - LGPL
36850  * <script type="text/javascript">
36851  */
36852
36853 // private - not really -- you end up using it !
36854 // This is a support class used internally by the Grid components
36855
36856 /**
36857  * @class Roo.grid.GridEditor
36858  * @extends Roo.Editor
36859  * Class for creating and editable grid elements.
36860  * @param {Object} config any settings (must include field)
36861  */
36862 Roo.grid.GridEditor = function(field, config){
36863     if (!config && field.field) {
36864         config = field;
36865         field = Roo.factory(config.field, Roo.form);
36866     }
36867     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36868     field.monitorTab = false;
36869 };
36870
36871 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36872     
36873     /**
36874      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36875      */
36876     
36877     alignment: "tl-tl",
36878     autoSize: "width",
36879     hideEl : false,
36880     cls: "x-small-editor x-grid-editor",
36881     shim:false,
36882     shadow:"frame"
36883 });/*
36884  * Based on:
36885  * Ext JS Library 1.1.1
36886  * Copyright(c) 2006-2007, Ext JS, LLC.
36887  *
36888  * Originally Released Under LGPL - original licence link has changed is not relivant.
36889  *
36890  * Fork - LGPL
36891  * <script type="text/javascript">
36892  */
36893   
36894
36895   
36896 Roo.grid.PropertyRecord = Roo.data.Record.create([
36897     {name:'name',type:'string'},  'value'
36898 ]);
36899
36900
36901 Roo.grid.PropertyStore = function(grid, source){
36902     this.grid = grid;
36903     this.store = new Roo.data.Store({
36904         recordType : Roo.grid.PropertyRecord
36905     });
36906     this.store.on('update', this.onUpdate,  this);
36907     if(source){
36908         this.setSource(source);
36909     }
36910     Roo.grid.PropertyStore.superclass.constructor.call(this);
36911 };
36912
36913
36914
36915 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36916     setSource : function(o){
36917         this.source = o;
36918         this.store.removeAll();
36919         var data = [];
36920         for(var k in o){
36921             if(this.isEditableValue(o[k])){
36922                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36923             }
36924         }
36925         this.store.loadRecords({records: data}, {}, true);
36926     },
36927
36928     onUpdate : function(ds, record, type){
36929         if(type == Roo.data.Record.EDIT){
36930             var v = record.data['value'];
36931             var oldValue = record.modified['value'];
36932             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36933                 this.source[record.id] = v;
36934                 record.commit();
36935                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36936             }else{
36937                 record.reject();
36938             }
36939         }
36940     },
36941
36942     getProperty : function(row){
36943        return this.store.getAt(row);
36944     },
36945
36946     isEditableValue: function(val){
36947         if(val && val instanceof Date){
36948             return true;
36949         }else if(typeof val == 'object' || typeof val == 'function'){
36950             return false;
36951         }
36952         return true;
36953     },
36954
36955     setValue : function(prop, value){
36956         this.source[prop] = value;
36957         this.store.getById(prop).set('value', value);
36958     },
36959
36960     getSource : function(){
36961         return this.source;
36962     }
36963 });
36964
36965 Roo.grid.PropertyColumnModel = function(grid, store){
36966     this.grid = grid;
36967     var g = Roo.grid;
36968     g.PropertyColumnModel.superclass.constructor.call(this, [
36969         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36970         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36971     ]);
36972     this.store = store;
36973     this.bselect = Roo.DomHelper.append(document.body, {
36974         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36975             {tag: 'option', value: 'true', html: 'true'},
36976             {tag: 'option', value: 'false', html: 'false'}
36977         ]
36978     });
36979     Roo.id(this.bselect);
36980     var f = Roo.form;
36981     this.editors = {
36982         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36983         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36984         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36985         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36986         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36987     };
36988     this.renderCellDelegate = this.renderCell.createDelegate(this);
36989     this.renderPropDelegate = this.renderProp.createDelegate(this);
36990 };
36991
36992 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36993     
36994     
36995     nameText : 'Name',
36996     valueText : 'Value',
36997     
36998     dateFormat : 'm/j/Y',
36999     
37000     
37001     renderDate : function(dateVal){
37002         return dateVal.dateFormat(this.dateFormat);
37003     },
37004
37005     renderBool : function(bVal){
37006         return bVal ? 'true' : 'false';
37007     },
37008
37009     isCellEditable : function(colIndex, rowIndex){
37010         return colIndex == 1;
37011     },
37012
37013     getRenderer : function(col){
37014         return col == 1 ?
37015             this.renderCellDelegate : this.renderPropDelegate;
37016     },
37017
37018     renderProp : function(v){
37019         return this.getPropertyName(v);
37020     },
37021
37022     renderCell : function(val){
37023         var rv = val;
37024         if(val instanceof Date){
37025             rv = this.renderDate(val);
37026         }else if(typeof val == 'boolean'){
37027             rv = this.renderBool(val);
37028         }
37029         return Roo.util.Format.htmlEncode(rv);
37030     },
37031
37032     getPropertyName : function(name){
37033         var pn = this.grid.propertyNames;
37034         return pn && pn[name] ? pn[name] : name;
37035     },
37036
37037     getCellEditor : function(colIndex, rowIndex){
37038         var p = this.store.getProperty(rowIndex);
37039         var n = p.data['name'], val = p.data['value'];
37040         
37041         if(typeof(this.grid.customEditors[n]) == 'string'){
37042             return this.editors[this.grid.customEditors[n]];
37043         }
37044         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37045             return this.grid.customEditors[n];
37046         }
37047         if(val instanceof Date){
37048             return this.editors['date'];
37049         }else if(typeof val == 'number'){
37050             return this.editors['number'];
37051         }else if(typeof val == 'boolean'){
37052             return this.editors['boolean'];
37053         }else{
37054             return this.editors['string'];
37055         }
37056     }
37057 });
37058
37059 /**
37060  * @class Roo.grid.PropertyGrid
37061  * @extends Roo.grid.EditorGrid
37062  * This class represents the  interface of a component based property grid control.
37063  * <br><br>Usage:<pre><code>
37064  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37065       
37066  });
37067  // set any options
37068  grid.render();
37069  * </code></pre>
37070   
37071  * @constructor
37072  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37073  * The container MUST have some type of size defined for the grid to fill. The container will be
37074  * automatically set to position relative if it isn't already.
37075  * @param {Object} config A config object that sets properties on this grid.
37076  */
37077 Roo.grid.PropertyGrid = function(container, config){
37078     config = config || {};
37079     var store = new Roo.grid.PropertyStore(this);
37080     this.store = store;
37081     var cm = new Roo.grid.PropertyColumnModel(this, store);
37082     store.store.sort('name', 'ASC');
37083     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37084         ds: store.store,
37085         cm: cm,
37086         enableColLock:false,
37087         enableColumnMove:false,
37088         stripeRows:false,
37089         trackMouseOver: false,
37090         clicksToEdit:1
37091     }, config));
37092     this.getGridEl().addClass('x-props-grid');
37093     this.lastEditRow = null;
37094     this.on('columnresize', this.onColumnResize, this);
37095     this.addEvents({
37096          /**
37097              * @event beforepropertychange
37098              * Fires before a property changes (return false to stop?)
37099              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37100              * @param {String} id Record Id
37101              * @param {String} newval New Value
37102          * @param {String} oldval Old Value
37103              */
37104         "beforepropertychange": true,
37105         /**
37106              * @event propertychange
37107              * Fires after a property changes
37108              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37109              * @param {String} id Record Id
37110              * @param {String} newval New Value
37111          * @param {String} oldval Old Value
37112              */
37113         "propertychange": true
37114     });
37115     this.customEditors = this.customEditors || {};
37116 };
37117 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37118     
37119      /**
37120      * @cfg {Object} customEditors map of colnames=> custom editors.
37121      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37122      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37123      * false disables editing of the field.
37124          */
37125     
37126       /**
37127      * @cfg {Object} propertyNames map of property Names to their displayed value
37128          */
37129     
37130     render : function(){
37131         Roo.grid.PropertyGrid.superclass.render.call(this);
37132         this.autoSize.defer(100, this);
37133     },
37134
37135     autoSize : function(){
37136         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37137         if(this.view){
37138             this.view.fitColumns();
37139         }
37140     },
37141
37142     onColumnResize : function(){
37143         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37144         this.autoSize();
37145     },
37146     /**
37147      * Sets the data for the Grid
37148      * accepts a Key => Value object of all the elements avaiable.
37149      * @param {Object} data  to appear in grid.
37150      */
37151     setSource : function(source){
37152         this.store.setSource(source);
37153         //this.autoSize();
37154     },
37155     /**
37156      * Gets all the data from the grid.
37157      * @return {Object} data  data stored in grid
37158      */
37159     getSource : function(){
37160         return this.store.getSource();
37161     }
37162 });/*
37163   
37164  * Licence LGPL
37165  
37166  */
37167  
37168 /**
37169  * @class Roo.grid.Calendar
37170  * @extends Roo.util.Grid
37171  * This class extends the Grid to provide a calendar widget
37172  * <br><br>Usage:<pre><code>
37173  var grid = new Roo.grid.Calendar("my-container-id", {
37174      ds: myDataStore,
37175      cm: myColModel,
37176      selModel: mySelectionModel,
37177      autoSizeColumns: true,
37178      monitorWindowResize: false,
37179      trackMouseOver: true
37180      eventstore : real data store..
37181  });
37182  // set any options
37183  grid.render();
37184   
37185   * @constructor
37186  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37187  * The container MUST have some type of size defined for the grid to fill. The container will be
37188  * automatically set to position relative if it isn't already.
37189  * @param {Object} config A config object that sets properties on this grid.
37190  */
37191 Roo.grid.Calendar = function(container, config){
37192         // initialize the container
37193         this.container = Roo.get(container);
37194         this.container.update("");
37195         this.container.setStyle("overflow", "hidden");
37196     this.container.addClass('x-grid-container');
37197
37198     this.id = this.container.id;
37199
37200     Roo.apply(this, config);
37201     // check and correct shorthanded configs
37202     
37203     var rows = [];
37204     var d =1;
37205     for (var r = 0;r < 6;r++) {
37206         
37207         rows[r]=[];
37208         for (var c =0;c < 7;c++) {
37209             rows[r][c]= '';
37210         }
37211     }
37212     if (this.eventStore) {
37213         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37214         this.eventStore.on('load',this.onLoad, this);
37215         this.eventStore.on('beforeload',this.clearEvents, this);
37216          
37217     }
37218     
37219     this.dataSource = new Roo.data.Store({
37220             proxy: new Roo.data.MemoryProxy(rows),
37221             reader: new Roo.data.ArrayReader({}, [
37222                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37223     });
37224
37225     this.dataSource.load();
37226     this.ds = this.dataSource;
37227     this.ds.xmodule = this.xmodule || false;
37228     
37229     
37230     var cellRender = function(v,x,r)
37231     {
37232         return String.format(
37233             '<div class="fc-day  fc-widget-content"><div>' +
37234                 '<div class="fc-event-container"></div>' +
37235                 '<div class="fc-day-number">{0}</div>'+
37236                 
37237                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37238             '</div></div>', v);
37239     
37240     }
37241     
37242     
37243     this.colModel = new Roo.grid.ColumnModel( [
37244         {
37245             xtype: 'ColumnModel',
37246             xns: Roo.grid,
37247             dataIndex : 'weekday0',
37248             header : 'Sunday',
37249             renderer : cellRender
37250         },
37251         {
37252             xtype: 'ColumnModel',
37253             xns: Roo.grid,
37254             dataIndex : 'weekday1',
37255             header : 'Monday',
37256             renderer : cellRender
37257         },
37258         {
37259             xtype: 'ColumnModel',
37260             xns: Roo.grid,
37261             dataIndex : 'weekday2',
37262             header : 'Tuesday',
37263             renderer : cellRender
37264         },
37265         {
37266             xtype: 'ColumnModel',
37267             xns: Roo.grid,
37268             dataIndex : 'weekday3',
37269             header : 'Wednesday',
37270             renderer : cellRender
37271         },
37272         {
37273             xtype: 'ColumnModel',
37274             xns: Roo.grid,
37275             dataIndex : 'weekday4',
37276             header : 'Thursday',
37277             renderer : cellRender
37278         },
37279         {
37280             xtype: 'ColumnModel',
37281             xns: Roo.grid,
37282             dataIndex : 'weekday5',
37283             header : 'Friday',
37284             renderer : cellRender
37285         },
37286         {
37287             xtype: 'ColumnModel',
37288             xns: Roo.grid,
37289             dataIndex : 'weekday6',
37290             header : 'Saturday',
37291             renderer : cellRender
37292         }
37293     ]);
37294     this.cm = this.colModel;
37295     this.cm.xmodule = this.xmodule || false;
37296  
37297         
37298           
37299     //this.selModel = new Roo.grid.CellSelectionModel();
37300     //this.sm = this.selModel;
37301     //this.selModel.init(this);
37302     
37303     
37304     if(this.width){
37305         this.container.setWidth(this.width);
37306     }
37307
37308     if(this.height){
37309         this.container.setHeight(this.height);
37310     }
37311     /** @private */
37312         this.addEvents({
37313         // raw events
37314         /**
37315          * @event click
37316          * The raw click event for the entire grid.
37317          * @param {Roo.EventObject} e
37318          */
37319         "click" : true,
37320         /**
37321          * @event dblclick
37322          * The raw dblclick event for the entire grid.
37323          * @param {Roo.EventObject} e
37324          */
37325         "dblclick" : true,
37326         /**
37327          * @event contextmenu
37328          * The raw contextmenu event for the entire grid.
37329          * @param {Roo.EventObject} e
37330          */
37331         "contextmenu" : true,
37332         /**
37333          * @event mousedown
37334          * The raw mousedown event for the entire grid.
37335          * @param {Roo.EventObject} e
37336          */
37337         "mousedown" : true,
37338         /**
37339          * @event mouseup
37340          * The raw mouseup event for the entire grid.
37341          * @param {Roo.EventObject} e
37342          */
37343         "mouseup" : true,
37344         /**
37345          * @event mouseover
37346          * The raw mouseover event for the entire grid.
37347          * @param {Roo.EventObject} e
37348          */
37349         "mouseover" : true,
37350         /**
37351          * @event mouseout
37352          * The raw mouseout event for the entire grid.
37353          * @param {Roo.EventObject} e
37354          */
37355         "mouseout" : true,
37356         /**
37357          * @event keypress
37358          * The raw keypress event for the entire grid.
37359          * @param {Roo.EventObject} e
37360          */
37361         "keypress" : true,
37362         /**
37363          * @event keydown
37364          * The raw keydown event for the entire grid.
37365          * @param {Roo.EventObject} e
37366          */
37367         "keydown" : true,
37368
37369         // custom events
37370
37371         /**
37372          * @event cellclick
37373          * Fires when a cell is clicked
37374          * @param {Grid} this
37375          * @param {Number} rowIndex
37376          * @param {Number} columnIndex
37377          * @param {Roo.EventObject} e
37378          */
37379         "cellclick" : true,
37380         /**
37381          * @event celldblclick
37382          * Fires when a cell is double clicked
37383          * @param {Grid} this
37384          * @param {Number} rowIndex
37385          * @param {Number} columnIndex
37386          * @param {Roo.EventObject} e
37387          */
37388         "celldblclick" : true,
37389         /**
37390          * @event rowclick
37391          * Fires when a row is clicked
37392          * @param {Grid} this
37393          * @param {Number} rowIndex
37394          * @param {Roo.EventObject} e
37395          */
37396         "rowclick" : true,
37397         /**
37398          * @event rowdblclick
37399          * Fires when a row is double clicked
37400          * @param {Grid} this
37401          * @param {Number} rowIndex
37402          * @param {Roo.EventObject} e
37403          */
37404         "rowdblclick" : true,
37405         /**
37406          * @event headerclick
37407          * Fires when a header is clicked
37408          * @param {Grid} this
37409          * @param {Number} columnIndex
37410          * @param {Roo.EventObject} e
37411          */
37412         "headerclick" : true,
37413         /**
37414          * @event headerdblclick
37415          * Fires when a header cell is double clicked
37416          * @param {Grid} this
37417          * @param {Number} columnIndex
37418          * @param {Roo.EventObject} e
37419          */
37420         "headerdblclick" : true,
37421         /**
37422          * @event rowcontextmenu
37423          * Fires when a row is right clicked
37424          * @param {Grid} this
37425          * @param {Number} rowIndex
37426          * @param {Roo.EventObject} e
37427          */
37428         "rowcontextmenu" : true,
37429         /**
37430          * @event cellcontextmenu
37431          * Fires when a cell is right clicked
37432          * @param {Grid} this
37433          * @param {Number} rowIndex
37434          * @param {Number} cellIndex
37435          * @param {Roo.EventObject} e
37436          */
37437          "cellcontextmenu" : true,
37438         /**
37439          * @event headercontextmenu
37440          * Fires when a header is right clicked
37441          * @param {Grid} this
37442          * @param {Number} columnIndex
37443          * @param {Roo.EventObject} e
37444          */
37445         "headercontextmenu" : true,
37446         /**
37447          * @event bodyscroll
37448          * Fires when the body element is scrolled
37449          * @param {Number} scrollLeft
37450          * @param {Number} scrollTop
37451          */
37452         "bodyscroll" : true,
37453         /**
37454          * @event columnresize
37455          * Fires when the user resizes a column
37456          * @param {Number} columnIndex
37457          * @param {Number} newSize
37458          */
37459         "columnresize" : true,
37460         /**
37461          * @event columnmove
37462          * Fires when the user moves a column
37463          * @param {Number} oldIndex
37464          * @param {Number} newIndex
37465          */
37466         "columnmove" : true,
37467         /**
37468          * @event startdrag
37469          * Fires when row(s) start being dragged
37470          * @param {Grid} this
37471          * @param {Roo.GridDD} dd The drag drop object
37472          * @param {event} e The raw browser event
37473          */
37474         "startdrag" : true,
37475         /**
37476          * @event enddrag
37477          * Fires when a drag operation is complete
37478          * @param {Grid} this
37479          * @param {Roo.GridDD} dd The drag drop object
37480          * @param {event} e The raw browser event
37481          */
37482         "enddrag" : true,
37483         /**
37484          * @event dragdrop
37485          * Fires when dragged row(s) are dropped on a valid DD target
37486          * @param {Grid} this
37487          * @param {Roo.GridDD} dd The drag drop object
37488          * @param {String} targetId The target drag drop object
37489          * @param {event} e The raw browser event
37490          */
37491         "dragdrop" : true,
37492         /**
37493          * @event dragover
37494          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37495          * @param {Grid} this
37496          * @param {Roo.GridDD} dd The drag drop object
37497          * @param {String} targetId The target drag drop object
37498          * @param {event} e The raw browser event
37499          */
37500         "dragover" : true,
37501         /**
37502          * @event dragenter
37503          *  Fires when the dragged row(s) first cross another DD target while being dragged
37504          * @param {Grid} this
37505          * @param {Roo.GridDD} dd The drag drop object
37506          * @param {String} targetId The target drag drop object
37507          * @param {event} e The raw browser event
37508          */
37509         "dragenter" : true,
37510         /**
37511          * @event dragout
37512          * Fires when the dragged row(s) leave another DD target while being dragged
37513          * @param {Grid} this
37514          * @param {Roo.GridDD} dd The drag drop object
37515          * @param {String} targetId The target drag drop object
37516          * @param {event} e The raw browser event
37517          */
37518         "dragout" : true,
37519         /**
37520          * @event rowclass
37521          * Fires when a row is rendered, so you can change add a style to it.
37522          * @param {GridView} gridview   The grid view
37523          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37524          */
37525         'rowclass' : true,
37526
37527         /**
37528          * @event render
37529          * Fires when the grid is rendered
37530          * @param {Grid} grid
37531          */
37532         'render' : true,
37533             /**
37534              * @event select
37535              * Fires when a date is selected
37536              * @param {DatePicker} this
37537              * @param {Date} date The selected date
37538              */
37539         'select': true,
37540         /**
37541              * @event monthchange
37542              * Fires when the displayed month changes 
37543              * @param {DatePicker} this
37544              * @param {Date} date The selected month
37545              */
37546         'monthchange': true,
37547         /**
37548              * @event evententer
37549              * Fires when mouse over an event
37550              * @param {Calendar} this
37551              * @param {event} Event
37552              */
37553         'evententer': true,
37554         /**
37555              * @event eventleave
37556              * Fires when the mouse leaves an
37557              * @param {Calendar} this
37558              * @param {event}
37559              */
37560         'eventleave': true,
37561         /**
37562              * @event eventclick
37563              * Fires when the mouse click an
37564              * @param {Calendar} this
37565              * @param {event}
37566              */
37567         'eventclick': true,
37568         /**
37569              * @event eventrender
37570              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37571              * @param {Calendar} this
37572              * @param {data} data to be modified
37573              */
37574         'eventrender': true
37575         
37576     });
37577
37578     Roo.grid.Grid.superclass.constructor.call(this);
37579     this.on('render', function() {
37580         this.view.el.addClass('x-grid-cal'); 
37581         
37582         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37583
37584     },this);
37585     
37586     if (!Roo.grid.Calendar.style) {
37587         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37588             
37589             
37590             '.x-grid-cal .x-grid-col' :  {
37591                 height: 'auto !important',
37592                 'vertical-align': 'top'
37593             },
37594             '.x-grid-cal  .fc-event-hori' : {
37595                 height: '14px'
37596             }
37597              
37598             
37599         }, Roo.id());
37600     }
37601
37602     
37603     
37604 };
37605 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37606     /**
37607      * @cfg {Store} eventStore The store that loads events.
37608      */
37609     eventStore : 25,
37610
37611      
37612     activeDate : false,
37613     startDay : 0,
37614     autoWidth : true,
37615     monitorWindowResize : false,
37616
37617     
37618     resizeColumns : function() {
37619         var col = (this.view.el.getWidth() / 7) - 3;
37620         // loop through cols, and setWidth
37621         for(var i =0 ; i < 7 ; i++){
37622             this.cm.setColumnWidth(i, col);
37623         }
37624     },
37625      setDate :function(date) {
37626         
37627         Roo.log('setDate?');
37628         
37629         this.resizeColumns();
37630         var vd = this.activeDate;
37631         this.activeDate = date;
37632 //        if(vd && this.el){
37633 //            var t = date.getTime();
37634 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37635 //                Roo.log('using add remove');
37636 //                
37637 //                this.fireEvent('monthchange', this, date);
37638 //                
37639 //                this.cells.removeClass("fc-state-highlight");
37640 //                this.cells.each(function(c){
37641 //                   if(c.dateValue == t){
37642 //                       c.addClass("fc-state-highlight");
37643 //                       setTimeout(function(){
37644 //                            try{c.dom.firstChild.focus();}catch(e){}
37645 //                       }, 50);
37646 //                       return false;
37647 //                   }
37648 //                   return true;
37649 //                });
37650 //                return;
37651 //            }
37652 //        }
37653         
37654         var days = date.getDaysInMonth();
37655         
37656         var firstOfMonth = date.getFirstDateOfMonth();
37657         var startingPos = firstOfMonth.getDay()-this.startDay;
37658         
37659         if(startingPos < this.startDay){
37660             startingPos += 7;
37661         }
37662         
37663         var pm = date.add(Date.MONTH, -1);
37664         var prevStart = pm.getDaysInMonth()-startingPos;
37665 //        
37666         
37667         
37668         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37669         
37670         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37671         //this.cells.addClassOnOver('fc-state-hover');
37672         
37673         var cells = this.cells.elements;
37674         var textEls = this.textNodes;
37675         
37676         //Roo.each(cells, function(cell){
37677         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37678         //});
37679         
37680         days += startingPos;
37681
37682         // convert everything to numbers so it's fast
37683         var day = 86400000;
37684         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37685         //Roo.log(d);
37686         //Roo.log(pm);
37687         //Roo.log(prevStart);
37688         
37689         var today = new Date().clearTime().getTime();
37690         var sel = date.clearTime().getTime();
37691         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37692         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37693         var ddMatch = this.disabledDatesRE;
37694         var ddText = this.disabledDatesText;
37695         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37696         var ddaysText = this.disabledDaysText;
37697         var format = this.format;
37698         
37699         var setCellClass = function(cal, cell){
37700             
37701             //Roo.log('set Cell Class');
37702             cell.title = "";
37703             var t = d.getTime();
37704             
37705             //Roo.log(d);
37706             
37707             
37708             cell.dateValue = t;
37709             if(t == today){
37710                 cell.className += " fc-today";
37711                 cell.className += " fc-state-highlight";
37712                 cell.title = cal.todayText;
37713             }
37714             if(t == sel){
37715                 // disable highlight in other month..
37716                 cell.className += " fc-state-highlight";
37717                 
37718             }
37719             // disabling
37720             if(t < min) {
37721                 //cell.className = " fc-state-disabled";
37722                 cell.title = cal.minText;
37723                 return;
37724             }
37725             if(t > max) {
37726                 //cell.className = " fc-state-disabled";
37727                 cell.title = cal.maxText;
37728                 return;
37729             }
37730             if(ddays){
37731                 if(ddays.indexOf(d.getDay()) != -1){
37732                     // cell.title = ddaysText;
37733                    // cell.className = " fc-state-disabled";
37734                 }
37735             }
37736             if(ddMatch && format){
37737                 var fvalue = d.dateFormat(format);
37738                 if(ddMatch.test(fvalue)){
37739                     cell.title = ddText.replace("%0", fvalue);
37740                    cell.className = " fc-state-disabled";
37741                 }
37742             }
37743             
37744             if (!cell.initialClassName) {
37745                 cell.initialClassName = cell.dom.className;
37746             }
37747             
37748             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37749         };
37750
37751         var i = 0;
37752         
37753         for(; i < startingPos; i++) {
37754             cells[i].dayName =  (++prevStart);
37755             Roo.log(textEls[i]);
37756             d.setDate(d.getDate()+1);
37757             
37758             //cells[i].className = "fc-past fc-other-month";
37759             setCellClass(this, cells[i]);
37760         }
37761         
37762         var intDay = 0;
37763         
37764         for(; i < days; i++){
37765             intDay = i - startingPos + 1;
37766             cells[i].dayName =  (intDay);
37767             d.setDate(d.getDate()+1);
37768             
37769             cells[i].className = ''; // "x-date-active";
37770             setCellClass(this, cells[i]);
37771         }
37772         var extraDays = 0;
37773         
37774         for(; i < 42; i++) {
37775             //textEls[i].innerHTML = (++extraDays);
37776             
37777             d.setDate(d.getDate()+1);
37778             cells[i].dayName = (++extraDays);
37779             cells[i].className = "fc-future fc-other-month";
37780             setCellClass(this, cells[i]);
37781         }
37782         
37783         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37784         
37785         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37786         
37787         // this will cause all the cells to mis
37788         var rows= [];
37789         var i =0;
37790         for (var r = 0;r < 6;r++) {
37791             for (var c =0;c < 7;c++) {
37792                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37793             }    
37794         }
37795         
37796         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37797         for(i=0;i<cells.length;i++) {
37798             
37799             this.cells.elements[i].dayName = cells[i].dayName ;
37800             this.cells.elements[i].className = cells[i].className;
37801             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37802             this.cells.elements[i].title = cells[i].title ;
37803             this.cells.elements[i].dateValue = cells[i].dateValue ;
37804         }
37805         
37806         
37807         
37808         
37809         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37810         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37811         
37812         ////if(totalRows != 6){
37813             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37814            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37815        // }
37816         
37817         this.fireEvent('monthchange', this, date);
37818         
37819         
37820     },
37821  /**
37822      * Returns the grid's SelectionModel.
37823      * @return {SelectionModel}
37824      */
37825     getSelectionModel : function(){
37826         if(!this.selModel){
37827             this.selModel = new Roo.grid.CellSelectionModel();
37828         }
37829         return this.selModel;
37830     },
37831
37832     load: function() {
37833         this.eventStore.load()
37834         
37835         
37836         
37837     },
37838     
37839     findCell : function(dt) {
37840         dt = dt.clearTime().getTime();
37841         var ret = false;
37842         this.cells.each(function(c){
37843             //Roo.log("check " +c.dateValue + '?=' + dt);
37844             if(c.dateValue == dt){
37845                 ret = c;
37846                 return false;
37847             }
37848             return true;
37849         });
37850         
37851         return ret;
37852     },
37853     
37854     findCells : function(rec) {
37855         var s = rec.data.start_dt.clone().clearTime().getTime();
37856        // Roo.log(s);
37857         var e= rec.data.end_dt.clone().clearTime().getTime();
37858        // Roo.log(e);
37859         var ret = [];
37860         this.cells.each(function(c){
37861              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37862             
37863             if(c.dateValue > e){
37864                 return ;
37865             }
37866             if(c.dateValue < s){
37867                 return ;
37868             }
37869             ret.push(c);
37870         });
37871         
37872         return ret;    
37873     },
37874     
37875     findBestRow: function(cells)
37876     {
37877         var ret = 0;
37878         
37879         for (var i =0 ; i < cells.length;i++) {
37880             ret  = Math.max(cells[i].rows || 0,ret);
37881         }
37882         return ret;
37883         
37884     },
37885     
37886     
37887     addItem : function(rec)
37888     {
37889         // look for vertical location slot in
37890         var cells = this.findCells(rec);
37891         
37892         rec.row = this.findBestRow(cells);
37893         
37894         // work out the location.
37895         
37896         var crow = false;
37897         var rows = [];
37898         for(var i =0; i < cells.length; i++) {
37899             if (!crow) {
37900                 crow = {
37901                     start : cells[i],
37902                     end :  cells[i]
37903                 };
37904                 continue;
37905             }
37906             if (crow.start.getY() == cells[i].getY()) {
37907                 // on same row.
37908                 crow.end = cells[i];
37909                 continue;
37910             }
37911             // different row.
37912             rows.push(crow);
37913             crow = {
37914                 start: cells[i],
37915                 end : cells[i]
37916             };
37917             
37918         }
37919         
37920         rows.push(crow);
37921         rec.els = [];
37922         rec.rows = rows;
37923         rec.cells = cells;
37924         for (var i = 0; i < cells.length;i++) {
37925             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37926             
37927         }
37928         
37929         
37930     },
37931     
37932     clearEvents: function() {
37933         
37934         if (!this.eventStore.getCount()) {
37935             return;
37936         }
37937         // reset number of rows in cells.
37938         Roo.each(this.cells.elements, function(c){
37939             c.rows = 0;
37940         });
37941         
37942         this.eventStore.each(function(e) {
37943             this.clearEvent(e);
37944         },this);
37945         
37946     },
37947     
37948     clearEvent : function(ev)
37949     {
37950         if (ev.els) {
37951             Roo.each(ev.els, function(el) {
37952                 el.un('mouseenter' ,this.onEventEnter, this);
37953                 el.un('mouseleave' ,this.onEventLeave, this);
37954                 el.remove();
37955             },this);
37956             ev.els = [];
37957         }
37958     },
37959     
37960     
37961     renderEvent : function(ev,ctr) {
37962         if (!ctr) {
37963              ctr = this.view.el.select('.fc-event-container',true).first();
37964         }
37965         
37966          
37967         this.clearEvent(ev);
37968             //code
37969        
37970         
37971         
37972         ev.els = [];
37973         var cells = ev.cells;
37974         var rows = ev.rows;
37975         this.fireEvent('eventrender', this, ev);
37976         
37977         for(var i =0; i < rows.length; i++) {
37978             
37979             cls = '';
37980             if (i == 0) {
37981                 cls += ' fc-event-start';
37982             }
37983             if ((i+1) == rows.length) {
37984                 cls += ' fc-event-end';
37985             }
37986             
37987             //Roo.log(ev.data);
37988             // how many rows should it span..
37989             var cg = this.eventTmpl.append(ctr,Roo.apply({
37990                 fccls : cls
37991                 
37992             }, ev.data) , true);
37993             
37994             
37995             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37996             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37997             cg.on('click', this.onEventClick, this, ev);
37998             
37999             ev.els.push(cg);
38000             
38001             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38002             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38003             //Roo.log(cg);
38004              
38005             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38006             cg.setWidth(ebox.right - sbox.x -2);
38007         }
38008     },
38009     
38010     renderEvents: function()
38011     {   
38012         // first make sure there is enough space..
38013         
38014         if (!this.eventTmpl) {
38015             this.eventTmpl = new Roo.Template(
38016                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38017                     '<div class="fc-event-inner">' +
38018                         '<span class="fc-event-time">{time}</span>' +
38019                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38020                     '</div>' +
38021                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38022                 '</div>'
38023             );
38024                 
38025         }
38026                
38027         
38028         
38029         this.cells.each(function(c) {
38030             //Roo.log(c.select('.fc-day-content div',true).first());
38031             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38032         });
38033         
38034         var ctr = this.view.el.select('.fc-event-container',true).first();
38035         
38036         var cls;
38037         this.eventStore.each(function(ev){
38038             
38039             this.renderEvent(ev);
38040              
38041              
38042         }, this);
38043         this.view.layout();
38044         
38045     },
38046     
38047     onEventEnter: function (e, el,event,d) {
38048         this.fireEvent('evententer', this, el, event);
38049     },
38050     
38051     onEventLeave: function (e, el,event,d) {
38052         this.fireEvent('eventleave', this, el, event);
38053     },
38054     
38055     onEventClick: function (e, el,event,d) {
38056         this.fireEvent('eventclick', this, el, event);
38057     },
38058     
38059     onMonthChange: function () {
38060         this.store.load();
38061     },
38062     
38063     onLoad: function () {
38064         
38065         //Roo.log('calendar onload');
38066 //         
38067         if(this.eventStore.getCount() > 0){
38068             
38069            
38070             
38071             this.eventStore.each(function(d){
38072                 
38073                 
38074                 // FIXME..
38075                 var add =   d.data;
38076                 if (typeof(add.end_dt) == 'undefined')  {
38077                     Roo.log("Missing End time in calendar data: ");
38078                     Roo.log(d);
38079                     return;
38080                 }
38081                 if (typeof(add.start_dt) == 'undefined')  {
38082                     Roo.log("Missing Start time in calendar data: ");
38083                     Roo.log(d);
38084                     return;
38085                 }
38086                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38087                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38088                 add.id = add.id || d.id;
38089                 add.title = add.title || '??';
38090                 
38091                 this.addItem(d);
38092                 
38093              
38094             },this);
38095         }
38096         
38097         this.renderEvents();
38098     }
38099     
38100
38101 });
38102 /*
38103  grid : {
38104                 xtype: 'Grid',
38105                 xns: Roo.grid,
38106                 listeners : {
38107                     render : function ()
38108                     {
38109                         _this.grid = this;
38110                         
38111                         if (!this.view.el.hasClass('course-timesheet')) {
38112                             this.view.el.addClass('course-timesheet');
38113                         }
38114                         if (this.tsStyle) {
38115                             this.ds.load({});
38116                             return; 
38117                         }
38118                         Roo.log('width');
38119                         Roo.log(_this.grid.view.el.getWidth());
38120                         
38121                         
38122                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38123                             '.course-timesheet .x-grid-row' : {
38124                                 height: '80px'
38125                             },
38126                             '.x-grid-row td' : {
38127                                 'vertical-align' : 0
38128                             },
38129                             '.course-edit-link' : {
38130                                 'color' : 'blue',
38131                                 'text-overflow' : 'ellipsis',
38132                                 'overflow' : 'hidden',
38133                                 'white-space' : 'nowrap',
38134                                 'cursor' : 'pointer'
38135                             },
38136                             '.sub-link' : {
38137                                 'color' : 'green'
38138                             },
38139                             '.de-act-sup-link' : {
38140                                 'color' : 'purple',
38141                                 'text-decoration' : 'line-through'
38142                             },
38143                             '.de-act-link' : {
38144                                 'color' : 'red',
38145                                 'text-decoration' : 'line-through'
38146                             },
38147                             '.course-timesheet .course-highlight' : {
38148                                 'border-top-style': 'dashed !important',
38149                                 'border-bottom-bottom': 'dashed !important'
38150                             },
38151                             '.course-timesheet .course-item' : {
38152                                 'font-family'   : 'tahoma, arial, helvetica',
38153                                 'font-size'     : '11px',
38154                                 'overflow'      : 'hidden',
38155                                 'padding-left'  : '10px',
38156                                 'padding-right' : '10px',
38157                                 'padding-top' : '10px' 
38158                             }
38159                             
38160                         }, Roo.id());
38161                                 this.ds.load({});
38162                     }
38163                 },
38164                 autoWidth : true,
38165                 monitorWindowResize : false,
38166                 cellrenderer : function(v,x,r)
38167                 {
38168                     return v;
38169                 },
38170                 sm : {
38171                     xtype: 'CellSelectionModel',
38172                     xns: Roo.grid
38173                 },
38174                 dataSource : {
38175                     xtype: 'Store',
38176                     xns: Roo.data,
38177                     listeners : {
38178                         beforeload : function (_self, options)
38179                         {
38180                             options.params = options.params || {};
38181                             options.params._month = _this.monthField.getValue();
38182                             options.params.limit = 9999;
38183                             options.params['sort'] = 'when_dt';    
38184                             options.params['dir'] = 'ASC';    
38185                             this.proxy.loadResponse = this.loadResponse;
38186                             Roo.log("load?");
38187                             //this.addColumns();
38188                         },
38189                         load : function (_self, records, options)
38190                         {
38191                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38192                                 // if you click on the translation.. you can edit it...
38193                                 var el = Roo.get(this);
38194                                 var id = el.dom.getAttribute('data-id');
38195                                 var d = el.dom.getAttribute('data-date');
38196                                 var t = el.dom.getAttribute('data-time');
38197                                 //var id = this.child('span').dom.textContent;
38198                                 
38199                                 //Roo.log(this);
38200                                 Pman.Dialog.CourseCalendar.show({
38201                                     id : id,
38202                                     when_d : d,
38203                                     when_t : t,
38204                                     productitem_active : id ? 1 : 0
38205                                 }, function() {
38206                                     _this.grid.ds.load({});
38207                                 });
38208                            
38209                            });
38210                            
38211                            _this.panel.fireEvent('resize', [ '', '' ]);
38212                         }
38213                     },
38214                     loadResponse : function(o, success, response){
38215                             // this is overridden on before load..
38216                             
38217                             Roo.log("our code?");       
38218                             //Roo.log(success);
38219                             //Roo.log(response)
38220                             delete this.activeRequest;
38221                             if(!success){
38222                                 this.fireEvent("loadexception", this, o, response);
38223                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38224                                 return;
38225                             }
38226                             var result;
38227                             try {
38228                                 result = o.reader.read(response);
38229                             }catch(e){
38230                                 Roo.log("load exception?");
38231                                 this.fireEvent("loadexception", this, o, response, e);
38232                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38233                                 return;
38234                             }
38235                             Roo.log("ready...");        
38236                             // loop through result.records;
38237                             // and set this.tdate[date] = [] << array of records..
38238                             _this.tdata  = {};
38239                             Roo.each(result.records, function(r){
38240                                 //Roo.log(r.data);
38241                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38242                                     _this.tdata[r.data.when_dt.format('j')] = [];
38243                                 }
38244                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38245                             });
38246                             
38247                             //Roo.log(_this.tdata);
38248                             
38249                             result.records = [];
38250                             result.totalRecords = 6;
38251                     
38252                             // let's generate some duumy records for the rows.
38253                             //var st = _this.dateField.getValue();
38254                             
38255                             // work out monday..
38256                             //st = st.add(Date.DAY, -1 * st.format('w'));
38257                             
38258                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38259                             
38260                             var firstOfMonth = date.getFirstDayOfMonth();
38261                             var days = date.getDaysInMonth();
38262                             var d = 1;
38263                             var firstAdded = false;
38264                             for (var i = 0; i < result.totalRecords ; i++) {
38265                                 //var d= st.add(Date.DAY, i);
38266                                 var row = {};
38267                                 var added = 0;
38268                                 for(var w = 0 ; w < 7 ; w++){
38269                                     if(!firstAdded && firstOfMonth != w){
38270                                         continue;
38271                                     }
38272                                     if(d > days){
38273                                         continue;
38274                                     }
38275                                     firstAdded = true;
38276                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38277                                     row['weekday'+w] = String.format(
38278                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38279                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38280                                                     d,
38281                                                     date.format('Y-m-')+dd
38282                                                 );
38283                                     added++;
38284                                     if(typeof(_this.tdata[d]) != 'undefined'){
38285                                         Roo.each(_this.tdata[d], function(r){
38286                                             var is_sub = '';
38287                                             var deactive = '';
38288                                             var id = r.id;
38289                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38290                                             if(r.parent_id*1>0){
38291                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38292                                                 id = r.parent_id;
38293                                             }
38294                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38295                                                 deactive = 'de-act-link';
38296                                             }
38297                                             
38298                                             row['weekday'+w] += String.format(
38299                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38300                                                     id, //0
38301                                                     r.product_id_name, //1
38302                                                     r.when_dt.format('h:ia'), //2
38303                                                     is_sub, //3
38304                                                     deactive, //4
38305                                                     desc // 5
38306                                             );
38307                                         });
38308                                     }
38309                                     d++;
38310                                 }
38311                                 
38312                                 // only do this if something added..
38313                                 if(added > 0){ 
38314                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38315                                 }
38316                                 
38317                                 
38318                                 // push it twice. (second one with an hour..
38319                                 
38320                             }
38321                             //Roo.log(result);
38322                             this.fireEvent("load", this, o, o.request.arg);
38323                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38324                         },
38325                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38326                     proxy : {
38327                         xtype: 'HttpProxy',
38328                         xns: Roo.data,
38329                         method : 'GET',
38330                         url : baseURL + '/Roo/Shop_course.php'
38331                     },
38332                     reader : {
38333                         xtype: 'JsonReader',
38334                         xns: Roo.data,
38335                         id : 'id',
38336                         fields : [
38337                             {
38338                                 'name': 'id',
38339                                 'type': 'int'
38340                             },
38341                             {
38342                                 'name': 'when_dt',
38343                                 'type': 'string'
38344                             },
38345                             {
38346                                 'name': 'end_dt',
38347                                 'type': 'string'
38348                             },
38349                             {
38350                                 'name': 'parent_id',
38351                                 'type': 'int'
38352                             },
38353                             {
38354                                 'name': 'product_id',
38355                                 'type': 'int'
38356                             },
38357                             {
38358                                 'name': 'productitem_id',
38359                                 'type': 'int'
38360                             },
38361                             {
38362                                 'name': 'guid',
38363                                 'type': 'int'
38364                             }
38365                         ]
38366                     }
38367                 },
38368                 toolbar : {
38369                     xtype: 'Toolbar',
38370                     xns: Roo,
38371                     items : [
38372                         {
38373                             xtype: 'Button',
38374                             xns: Roo.Toolbar,
38375                             listeners : {
38376                                 click : function (_self, e)
38377                                 {
38378                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38379                                     sd.setMonth(sd.getMonth()-1);
38380                                     _this.monthField.setValue(sd.format('Y-m-d'));
38381                                     _this.grid.ds.load({});
38382                                 }
38383                             },
38384                             text : "Back"
38385                         },
38386                         {
38387                             xtype: 'Separator',
38388                             xns: Roo.Toolbar
38389                         },
38390                         {
38391                             xtype: 'MonthField',
38392                             xns: Roo.form,
38393                             listeners : {
38394                                 render : function (_self)
38395                                 {
38396                                     _this.monthField = _self;
38397                                    // _this.monthField.set  today
38398                                 },
38399                                 select : function (combo, date)
38400                                 {
38401                                     _this.grid.ds.load({});
38402                                 }
38403                             },
38404                             value : (function() { return new Date(); })()
38405                         },
38406                         {
38407                             xtype: 'Separator',
38408                             xns: Roo.Toolbar
38409                         },
38410                         {
38411                             xtype: 'TextItem',
38412                             xns: Roo.Toolbar,
38413                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38414                         },
38415                         {
38416                             xtype: 'Fill',
38417                             xns: Roo.Toolbar
38418                         },
38419                         {
38420                             xtype: 'Button',
38421                             xns: Roo.Toolbar,
38422                             listeners : {
38423                                 click : function (_self, e)
38424                                 {
38425                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38426                                     sd.setMonth(sd.getMonth()+1);
38427                                     _this.monthField.setValue(sd.format('Y-m-d'));
38428                                     _this.grid.ds.load({});
38429                                 }
38430                             },
38431                             text : "Next"
38432                         }
38433                     ]
38434                 },
38435                  
38436             }
38437         };
38438         
38439         *//*
38440  * Based on:
38441  * Ext JS Library 1.1.1
38442  * Copyright(c) 2006-2007, Ext JS, LLC.
38443  *
38444  * Originally Released Under LGPL - original licence link has changed is not relivant.
38445  *
38446  * Fork - LGPL
38447  * <script type="text/javascript">
38448  */
38449  
38450 /**
38451  * @class Roo.LoadMask
38452  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38453  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38454  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38455  * element's UpdateManager load indicator and will be destroyed after the initial load.
38456  * @constructor
38457  * Create a new LoadMask
38458  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38459  * @param {Object} config The config object
38460  */
38461 Roo.LoadMask = function(el, config){
38462     this.el = Roo.get(el);
38463     Roo.apply(this, config);
38464     if(this.store){
38465         this.store.on('beforeload', this.onBeforeLoad, this);
38466         this.store.on('load', this.onLoad, this);
38467         this.store.on('loadexception', this.onLoadException, this);
38468         this.removeMask = false;
38469     }else{
38470         var um = this.el.getUpdateManager();
38471         um.showLoadIndicator = false; // disable the default indicator
38472         um.on('beforeupdate', this.onBeforeLoad, this);
38473         um.on('update', this.onLoad, this);
38474         um.on('failure', this.onLoad, this);
38475         this.removeMask = true;
38476     }
38477 };
38478
38479 Roo.LoadMask.prototype = {
38480     /**
38481      * @cfg {Boolean} removeMask
38482      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38483      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38484      */
38485     removeMask : false,
38486     /**
38487      * @cfg {String} msg
38488      * The text to display in a centered loading message box (defaults to 'Loading...')
38489      */
38490     msg : 'Loading...',
38491     /**
38492      * @cfg {String} msgCls
38493      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38494      */
38495     msgCls : 'x-mask-loading',
38496
38497     /**
38498      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38499      * @type Boolean
38500      */
38501     disabled: false,
38502
38503     /**
38504      * Disables the mask to prevent it from being displayed
38505      */
38506     disable : function(){
38507        this.disabled = true;
38508     },
38509
38510     /**
38511      * Enables the mask so that it can be displayed
38512      */
38513     enable : function(){
38514         this.disabled = false;
38515     },
38516     
38517     onLoadException : function()
38518     {
38519         Roo.log(arguments);
38520         
38521         if (typeof(arguments[3]) != 'undefined') {
38522             Roo.MessageBox.alert("Error loading",arguments[3]);
38523         } 
38524         /*
38525         try {
38526             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38527                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38528             }   
38529         } catch(e) {
38530             
38531         }
38532         */
38533     
38534         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38535     },
38536     // private
38537     onLoad : function()
38538     {
38539         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38540     },
38541
38542     // private
38543     onBeforeLoad : function(){
38544         if(!this.disabled){
38545             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38546         }
38547     },
38548
38549     // private
38550     destroy : function(){
38551         if(this.store){
38552             this.store.un('beforeload', this.onBeforeLoad, this);
38553             this.store.un('load', this.onLoad, this);
38554             this.store.un('loadexception', this.onLoadException, this);
38555         }else{
38556             var um = this.el.getUpdateManager();
38557             um.un('beforeupdate', this.onBeforeLoad, this);
38558             um.un('update', this.onLoad, this);
38559             um.un('failure', this.onLoad, this);
38560         }
38561     }
38562 };/*
38563  * Based on:
38564  * Ext JS Library 1.1.1
38565  * Copyright(c) 2006-2007, Ext JS, LLC.
38566  *
38567  * Originally Released Under LGPL - original licence link has changed is not relivant.
38568  *
38569  * Fork - LGPL
38570  * <script type="text/javascript">
38571  */
38572
38573
38574 /**
38575  * @class Roo.XTemplate
38576  * @extends Roo.Template
38577  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38578 <pre><code>
38579 var t = new Roo.XTemplate(
38580         '&lt;select name="{name}"&gt;',
38581                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38582         '&lt;/select&gt;'
38583 );
38584  
38585 // then append, applying the master template values
38586  </code></pre>
38587  *
38588  * Supported features:
38589  *
38590  *  Tags:
38591
38592 <pre><code>
38593       {a_variable} - output encoded.
38594       {a_variable.format:("Y-m-d")} - call a method on the variable
38595       {a_variable:raw} - unencoded output
38596       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38597       {a_variable:this.method_on_template(...)} - call a method on the template object.
38598  
38599 </code></pre>
38600  *  The tpl tag:
38601 <pre><code>
38602         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38603         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38604         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38605         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38606   
38607         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38608         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38609 </code></pre>
38610  *      
38611  */
38612 Roo.XTemplate = function()
38613 {
38614     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38615     if (this.html) {
38616         this.compile();
38617     }
38618 };
38619
38620
38621 Roo.extend(Roo.XTemplate, Roo.Template, {
38622
38623     /**
38624      * The various sub templates
38625      */
38626     tpls : false,
38627     /**
38628      *
38629      * basic tag replacing syntax
38630      * WORD:WORD()
38631      *
38632      * // you can fake an object call by doing this
38633      *  x.t:(test,tesT) 
38634      * 
38635      */
38636     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38637
38638     /**
38639      * compile the template
38640      *
38641      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38642      *
38643      */
38644     compile: function()
38645     {
38646         var s = this.html;
38647      
38648         s = ['<tpl>', s, '</tpl>'].join('');
38649     
38650         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38651             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38652             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38653             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38654             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38655             m,
38656             id     = 0,
38657             tpls   = [];
38658     
38659         while(true == !!(m = s.match(re))){
38660             var forMatch   = m[0].match(nameRe),
38661                 ifMatch   = m[0].match(ifRe),
38662                 execMatch   = m[0].match(execRe),
38663                 namedMatch   = m[0].match(namedRe),
38664                 
38665                 exp  = null, 
38666                 fn   = null,
38667                 exec = null,
38668                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38669                 
38670             if (ifMatch) {
38671                 // if - puts fn into test..
38672                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38673                 if(exp){
38674                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38675                 }
38676             }
38677             
38678             if (execMatch) {
38679                 // exec - calls a function... returns empty if true is  returned.
38680                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38681                 if(exp){
38682                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38683                 }
38684             }
38685             
38686             
38687             if (name) {
38688                 // for = 
38689                 switch(name){
38690                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38691                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38692                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38693                 }
38694             }
38695             var uid = namedMatch ? namedMatch[1] : id;
38696             
38697             
38698             tpls.push({
38699                 id:     namedMatch ? namedMatch[1] : id,
38700                 target: name,
38701                 exec:   exec,
38702                 test:   fn,
38703                 body:   m[1] || ''
38704             });
38705             if (namedMatch) {
38706                 s = s.replace(m[0], '');
38707             } else { 
38708                 s = s.replace(m[0], '{xtpl'+ id + '}');
38709             }
38710             ++id;
38711         }
38712         this.tpls = [];
38713         for(var i = tpls.length-1; i >= 0; --i){
38714             this.compileTpl(tpls[i]);
38715             this.tpls[tpls[i].id] = tpls[i];
38716         }
38717         this.master = tpls[tpls.length-1];
38718         return this;
38719     },
38720     /**
38721      * same as applyTemplate, except it's done to one of the subTemplates
38722      * when using named templates, you can do:
38723      *
38724      * var str = pl.applySubTemplate('your-name', values);
38725      *
38726      * 
38727      * @param {Number} id of the template
38728      * @param {Object} values to apply to template
38729      * @param {Object} parent (normaly the instance of this object)
38730      */
38731     applySubTemplate : function(id, values, parent)
38732     {
38733         
38734         
38735         var t = this.tpls[id];
38736         
38737         
38738         try { 
38739             if(t.test && !t.test.call(this, values, parent)){
38740                 return '';
38741             }
38742         } catch(e) {
38743             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38744             Roo.log(e.toString());
38745             Roo.log(t.test);
38746             return ''
38747         }
38748         try { 
38749             
38750             if(t.exec && t.exec.call(this, values, parent)){
38751                 return '';
38752             }
38753         } catch(e) {
38754             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38755             Roo.log(e.toString());
38756             Roo.log(t.exec);
38757             return ''
38758         }
38759         try {
38760             var vs = t.target ? t.target.call(this, values, parent) : values;
38761             parent = t.target ? values : parent;
38762             if(t.target && vs instanceof Array){
38763                 var buf = [];
38764                 for(var i = 0, len = vs.length; i < len; i++){
38765                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38766                 }
38767                 return buf.join('');
38768             }
38769             return t.compiled.call(this, vs, parent);
38770         } catch (e) {
38771             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38772             Roo.log(e.toString());
38773             Roo.log(t.compiled);
38774             return '';
38775         }
38776     },
38777
38778     compileTpl : function(tpl)
38779     {
38780         var fm = Roo.util.Format;
38781         var useF = this.disableFormats !== true;
38782         var sep = Roo.isGecko ? "+" : ",";
38783         var undef = function(str) {
38784             Roo.log("Property not found :"  + str);
38785             return '';
38786         };
38787         
38788         var fn = function(m, name, format, args)
38789         {
38790             //Roo.log(arguments);
38791             args = args ? args.replace(/\\'/g,"'") : args;
38792             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38793             if (typeof(format) == 'undefined') {
38794                 format= 'htmlEncode';
38795             }
38796             if (format == 'raw' ) {
38797                 format = false;
38798             }
38799             
38800             if(name.substr(0, 4) == 'xtpl'){
38801                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38802             }
38803             
38804             // build an array of options to determine if value is undefined..
38805             
38806             // basically get 'xxxx.yyyy' then do
38807             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38808             //    (function () { Roo.log("Property not found"); return ''; })() :
38809             //    ......
38810             
38811             var udef_ar = [];
38812             var lookfor = '';
38813             Roo.each(name.split('.'), function(st) {
38814                 lookfor += (lookfor.length ? '.': '') + st;
38815                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38816             });
38817             
38818             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38819             
38820             
38821             if(format && useF){
38822                 
38823                 args = args ? ',' + args : "";
38824                  
38825                 if(format.substr(0, 5) != "this."){
38826                     format = "fm." + format + '(';
38827                 }else{
38828                     format = 'this.call("'+ format.substr(5) + '", ';
38829                     args = ", values";
38830                 }
38831                 
38832                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38833             }
38834              
38835             if (args.length) {
38836                 // called with xxyx.yuu:(test,test)
38837                 // change to ()
38838                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38839             }
38840             // raw.. - :raw modifier..
38841             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38842             
38843         };
38844         var body;
38845         // branched to use + in gecko and [].join() in others
38846         if(Roo.isGecko){
38847             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38848                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38849                     "';};};";
38850         }else{
38851             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38852             body.push(tpl.body.replace(/(\r\n|\n)/g,
38853                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38854             body.push("'].join('');};};");
38855             body = body.join('');
38856         }
38857         
38858         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38859        
38860         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38861         eval(body);
38862         
38863         return this;
38864     },
38865
38866     applyTemplate : function(values){
38867         return this.master.compiled.call(this, values, {});
38868         //var s = this.subs;
38869     },
38870
38871     apply : function(){
38872         return this.applyTemplate.apply(this, arguments);
38873     }
38874
38875  });
38876
38877 Roo.XTemplate.from = function(el){
38878     el = Roo.getDom(el);
38879     return new Roo.XTemplate(el.value || el.innerHTML);
38880 };