roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570         if(this.pruneModifiedRecords){
571             this.modified.remove(record);
572         }
573         this.fireEvent("remove", this, record, index);
574     },
575
576     /**
577      * Remove all Records from the Store and fires the clear event.
578      */
579     removeAll : function(){
580         this.data.clear();
581         if(this.pruneModifiedRecords){
582             this.modified = [];
583         }
584         this.fireEvent("clear", this);
585     },
586
587     /**
588      * Inserts Records to the Store at the given index and fires the add event.
589      * @param {Number} index The start index at which to insert the passed Records.
590      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
591      */
592     insert : function(index, records){
593         records = [].concat(records);
594         for(var i = 0, len = records.length; i < len; i++){
595             this.data.insert(index, records[i]);
596             records[i].join(this);
597         }
598         this.fireEvent("add", this, records, index);
599     },
600
601     /**
602      * Get the index within the cache of the passed Record.
603      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
604      * @return {Number} The index of the passed Record. Returns -1 if not found.
605      */
606     indexOf : function(record){
607         return this.data.indexOf(record);
608     },
609
610     /**
611      * Get the index within the cache of the Record with the passed id.
612      * @param {String} id The id of the Record to find.
613      * @return {Number} The index of the Record. Returns -1 if not found.
614      */
615     indexOfId : function(id){
616         return this.data.indexOfKey(id);
617     },
618
619     /**
620      * Get the Record with the specified id.
621      * @param {String} id The id of the Record to find.
622      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
623      */
624     getById : function(id){
625         return this.data.key(id);
626     },
627
628     /**
629      * Get the Record at the specified index.
630      * @param {Number} index The index of the Record to find.
631      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
632      */
633     getAt : function(index){
634         return this.data.itemAt(index);
635     },
636
637     /**
638      * Returns a range of Records between specified indices.
639      * @param {Number} startIndex (optional) The starting index (defaults to 0)
640      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
641      * @return {Roo.data.Record[]} An array of Records
642      */
643     getRange : function(start, end){
644         return this.data.getRange(start, end);
645     },
646
647     // private
648     storeOptions : function(o){
649         o = Roo.apply({}, o);
650         delete o.callback;
651         delete o.scope;
652         this.lastOptions = o;
653     },
654
655     /**
656      * Loads the Record cache from the configured Proxy using the configured Reader.
657      * <p>
658      * If using remote paging, then the first load call must specify the <em>start</em>
659      * and <em>limit</em> properties in the options.params property to establish the initial
660      * position within the dataset, and the number of Records to cache on each read from the Proxy.
661      * <p>
662      * <strong>It is important to note that for remote data sources, loading is asynchronous,
663      * and this call will return before the new data has been loaded. Perform any post-processing
664      * in a callback function, or in a "load" event handler.</strong>
665      * <p>
666      * @param {Object} options An object containing properties which control loading options:<ul>
667      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
668      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
669      * passed the following arguments:<ul>
670      * <li>r : Roo.data.Record[]</li>
671      * <li>options: Options object from the load call</li>
672      * <li>success: Boolean success indicator</li></ul></li>
673      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
674      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
675      * </ul>
676      */
677     load : function(options){
678         options = options || {};
679         if(this.fireEvent("beforeload", this, options) !== false){
680             this.storeOptions(options);
681             var p = Roo.apply(options.params || {}, this.baseParams);
682             // if meta was not loaded from remote source.. try requesting it.
683             if (!this.reader.metaFromRemote) {
684                 p._requestMeta = 1;
685             }
686             if(this.sortInfo && this.remoteSort){
687                 var pn = this.paramNames;
688                 p[pn["sort"]] = this.sortInfo.field;
689                 p[pn["dir"]] = this.sortInfo.direction;
690             }
691             if (this.multiSort) {
692                 var pn = this.paramNames;
693                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
694             }
695             
696             this.proxy.load(p, this.reader, this.loadRecords, this, options);
697         }
698     },
699
700     /**
701      * Reloads the Record cache from the configured Proxy using the configured Reader and
702      * the options from the last load operation performed.
703      * @param {Object} options (optional) An object containing properties which may override the options
704      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
705      * the most recently used options are reused).
706      */
707     reload : function(options){
708         this.load(Roo.applyIf(options||{}, this.lastOptions));
709     },
710
711     // private
712     // Called as a callback by the Reader during a load operation.
713     loadRecords : function(o, options, success){
714         if(!o || success === false){
715             if(success !== false){
716                 this.fireEvent("load", this, [], options, o);
717             }
718             if(options.callback){
719                 options.callback.call(options.scope || this, [], options, false);
720             }
721             return;
722         }
723         // if data returned failure - throw an exception.
724         if (o.success === false) {
725             // show a message if no listener is registered.
726             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
727                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
728             }
729             // loadmask wil be hooked into this..
730             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
731             return;
732         }
733         var r = o.records, t = o.totalRecords || r.length;
734         
735         this.fireEvent("beforeloadadd", this, r, options, o);
736         
737         if(!options || options.add !== true){
738             if(this.pruneModifiedRecords){
739                 this.modified = [];
740             }
741             for(var i = 0, len = r.length; i < len; i++){
742                 r[i].join(this);
743             }
744             if(this.snapshot){
745                 this.data = this.snapshot;
746                 delete this.snapshot;
747             }
748             this.data.clear();
749             this.data.addAll(r);
750             this.totalLength = t;
751             this.applySort();
752             this.fireEvent("datachanged", this);
753         }else{
754             this.totalLength = Math.max(t, this.data.length+r.length);
755             this.add(r);
756         }
757         
758         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
759                 
760             var e = new Roo.data.Record({});
761
762             e.set(this.parent.displayField, this.parent.emptyTitle);
763             e.set(this.parent.valueField, '');
764
765             this.insert(0, e);
766         }
767             
768         this.fireEvent("load", this, r, options, o);
769         if(options.callback){
770             options.callback.call(options.scope || this, r, options, true);
771         }
772     },
773
774
775     /**
776      * Loads data from a passed data block. A Reader which understands the format of the data
777      * must have been configured in the constructor.
778      * @param {Object} data The data block from which to read the Records.  The format of the data expected
779      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
780      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
781      */
782     loadData : function(o, append){
783         var r = this.reader.readRecords(o);
784         this.loadRecords(r, {add: append}, true);
785     },
786
787     /**
788      * Gets the number of cached records.
789      * <p>
790      * <em>If using paging, this may not be the total size of the dataset. If the data object
791      * used by the Reader contains the dataset size, then the getTotalCount() function returns
792      * the data set size</em>
793      */
794     getCount : function(){
795         return this.data.length || 0;
796     },
797
798     /**
799      * Gets the total number of records in the dataset as returned by the server.
800      * <p>
801      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
802      * the dataset size</em>
803      */
804     getTotalCount : function(){
805         return this.totalLength || 0;
806     },
807
808     /**
809      * Returns the sort state of the Store as an object with two properties:
810      * <pre><code>
811  field {String} The name of the field by which the Records are sorted
812  direction {String} The sort order, "ASC" or "DESC"
813      * </code></pre>
814      */
815     getSortState : function(){
816         return this.sortInfo;
817     },
818
819     // private
820     applySort : function(){
821         if(this.sortInfo && !this.remoteSort){
822             var s = this.sortInfo, f = s.field;
823             var st = this.fields.get(f).sortType;
824             var fn = function(r1, r2){
825                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
826                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
827             };
828             this.data.sort(s.direction, fn);
829             if(this.snapshot && this.snapshot != this.data){
830                 this.snapshot.sort(s.direction, fn);
831             }
832         }
833     },
834
835     /**
836      * Sets the default sort column and order to be used by the next load operation.
837      * @param {String} fieldName The name of the field to sort by.
838      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
839      */
840     setDefaultSort : function(field, dir){
841         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
842     },
843
844     /**
845      * Sort the Records.
846      * If remote sorting is used, the sort is performed on the server, and the cache is
847      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
852         var f = this.fields.get(fieldName);
853         if(!dir){
854             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
855             
856             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
857                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
858             }else{
859                 dir = f.sortDir;
860             }
861         }
862         this.sortToggle[f.name] = dir;
863         this.sortInfo = {field: f.name, direction: dir};
864         if(!this.remoteSort){
865             this.applySort();
866             this.fireEvent("datachanged", this);
867         }else{
868             this.load(this.lastOptions);
869         }
870     },
871
872     /**
873      * Calls the specified function for each of the Records in the cache.
874      * @param {Function} fn The function to call. The Record is passed as the first parameter.
875      * Returning <em>false</em> aborts and exits the iteration.
876      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
877      */
878     each : function(fn, scope){
879         this.data.each(fn, scope);
880     },
881
882     /**
883      * Gets all records modified since the last commit.  Modified records are persisted across load operations
884      * (e.g., during paging).
885      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
886      */
887     getModifiedRecords : function(){
888         return this.modified;
889     },
890
891     // private
892     createFilterFn : function(property, value, anyMatch){
893         if(!value.exec){ // not a regex
894             value = String(value);
895             if(value.length == 0){
896                 return false;
897             }
898             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
899         }
900         return function(r){
901             return value.test(r.data[property]);
902         };
903     },
904
905     /**
906      * Sums the value of <i>property</i> for each record between start and end and returns the result.
907      * @param {String} property A field on your records
908      * @param {Number} start The record index to start at (defaults to 0)
909      * @param {Number} end The last record index to include (defaults to length - 1)
910      * @return {Number} The sum
911      */
912     sum : function(property, start, end){
913         var rs = this.data.items, v = 0;
914         start = start || 0;
915         end = (end || end === 0) ? end : rs.length-1;
916
917         for(var i = start; i <= end; i++){
918             v += (rs[i].data[property] || 0);
919         }
920         return v;
921     },
922
923     /**
924      * Filter the records by a specified property.
925      * @param {String} field A field on your records
926      * @param {String/RegExp} value Either a string that the field
927      * should start with or a RegExp to test against the field
928      * @param {Boolean} anyMatch True to match any part not just the beginning
929      */
930     filter : function(property, value, anyMatch){
931         var fn = this.createFilterFn(property, value, anyMatch);
932         return fn ? this.filterBy(fn) : this.clearFilter();
933     },
934
935     /**
936      * Filter by a function. The specified function will be called with each
937      * record in this data source. If the function returns true the record is included,
938      * otherwise it is filtered.
939      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
940      * @param {Object} scope (optional) The scope of the function (defaults to this)
941      */
942     filterBy : function(fn, scope){
943         this.snapshot = this.snapshot || this.data;
944         this.data = this.queryBy(fn, scope||this);
945         this.fireEvent("datachanged", this);
946     },
947
948     /**
949      * Query the records by a specified property.
950      * @param {String} field A field on your records
951      * @param {String/RegExp} value Either a string that the field
952      * should start with or a RegExp to test against the field
953      * @param {Boolean} anyMatch True to match any part not just the beginning
954      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
955      */
956     query : function(property, value, anyMatch){
957         var fn = this.createFilterFn(property, value, anyMatch);
958         return fn ? this.queryBy(fn) : this.data.clone();
959     },
960
961     /**
962      * Query by a function. The specified function will be called with each
963      * record in this data source. If the function returns true the record is included
964      * in the results.
965      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
966      * @param {Object} scope (optional) The scope of the function (defaults to this)
967       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
968      **/
969     queryBy : function(fn, scope){
970         var data = this.snapshot || this.data;
971         return data.filterBy(fn, scope||this);
972     },
973
974     /**
975      * Collects unique values for a particular dataIndex from this store.
976      * @param {String} dataIndex The property to collect
977      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
978      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
979      * @return {Array} An array of the unique values
980      **/
981     collect : function(dataIndex, allowNull, bypassFilter){
982         var d = (bypassFilter === true && this.snapshot) ?
983                 this.snapshot.items : this.data.items;
984         var v, sv, r = [], l = {};
985         for(var i = 0, len = d.length; i < len; i++){
986             v = d[i].data[dataIndex];
987             sv = String(v);
988             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
989                 l[sv] = true;
990                 r[r.length] = v;
991             }
992         }
993         return r;
994     },
995
996     /**
997      * Revert to a view of the Record cache with no filtering applied.
998      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
999      */
1000     clearFilter : function(suppressEvent){
1001         if(this.snapshot && this.snapshot != this.data){
1002             this.data = this.snapshot;
1003             delete this.snapshot;
1004             if(suppressEvent !== true){
1005                 this.fireEvent("datachanged", this);
1006             }
1007         }
1008     },
1009
1010     // private
1011     afterEdit : function(record){
1012         if(this.modified.indexOf(record) == -1){
1013             this.modified.push(record);
1014         }
1015         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1016     },
1017     
1018     // private
1019     afterReject : function(record){
1020         this.modified.remove(record);
1021         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1022     },
1023
1024     // private
1025     afterCommit : function(record){
1026         this.modified.remove(record);
1027         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1028     },
1029
1030     /**
1031      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1032      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1033      */
1034     commitChanges : function(){
1035         var m = this.modified.slice(0);
1036         this.modified = [];
1037         for(var i = 0, len = m.length; i < len; i++){
1038             m[i].commit();
1039         }
1040     },
1041
1042     /**
1043      * Cancel outstanding changes on all changed records.
1044      */
1045     rejectChanges : 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].reject();
1050         }
1051     },
1052
1053     onMetaChange : function(meta, rtype, o){
1054         this.recordType = rtype;
1055         this.fields = rtype.prototype.fields;
1056         delete this.snapshot;
1057         this.sortInfo = meta.sortInfo || this.sortInfo;
1058         this.modified = [];
1059         this.fireEvent('metachange', this, this.reader.meta);
1060     },
1061     
1062     moveIndex : function(data, type)
1063     {
1064         var index = this.indexOf(data);
1065         
1066         var newIndex = index + type;
1067         
1068         this.remove(data);
1069         
1070         this.insert(newIndex, data);
1071         
1072     }
1073 });/*
1074  * Based on:
1075  * Ext JS Library 1.1.1
1076  * Copyright(c) 2006-2007, Ext JS, LLC.
1077  *
1078  * Originally Released Under LGPL - original licence link has changed is not relivant.
1079  *
1080  * Fork - LGPL
1081  * <script type="text/javascript">
1082  */
1083
1084 /**
1085  * @class Roo.data.SimpleStore
1086  * @extends Roo.data.Store
1087  * Small helper class to make creating Stores from Array data easier.
1088  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1089  * @cfg {Array} fields An array of field definition objects, or field name strings.
1090  * @cfg {Array} data The multi-dimensional array of data
1091  * @constructor
1092  * @param {Object} config
1093  */
1094 Roo.data.SimpleStore = function(config){
1095     Roo.data.SimpleStore.superclass.constructor.call(this, {
1096         isLocal : true,
1097         reader: new Roo.data.ArrayReader({
1098                 id: config.id
1099             },
1100             Roo.data.Record.create(config.fields)
1101         ),
1102         proxy : new Roo.data.MemoryProxy(config.data)
1103     });
1104     this.load();
1105 };
1106 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1107  * Based on:
1108  * Ext JS Library 1.1.1
1109  * Copyright(c) 2006-2007, Ext JS, LLC.
1110  *
1111  * Originally Released Under LGPL - original licence link has changed is not relivant.
1112  *
1113  * Fork - LGPL
1114  * <script type="text/javascript">
1115  */
1116
1117 /**
1118 /**
1119  * @extends Roo.data.Store
1120  * @class Roo.data.JsonStore
1121  * Small helper class to make creating Stores for JSON data easier. <br/>
1122 <pre><code>
1123 var store = new Roo.data.JsonStore({
1124     url: 'get-images.php',
1125     root: 'images',
1126     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1127 });
1128 </code></pre>
1129  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1130  * JsonReader and HttpProxy (unless inline data is provided).</b>
1131  * @cfg {Array} fields An array of field definition objects, or field name strings.
1132  * @constructor
1133  * @param {Object} config
1134  */
1135 Roo.data.JsonStore = function(c){
1136     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1137         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1138         reader: new Roo.data.JsonReader(c, c.fields)
1139     }));
1140 };
1141 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1142  * Based on:
1143  * Ext JS Library 1.1.1
1144  * Copyright(c) 2006-2007, Ext JS, LLC.
1145  *
1146  * Originally Released Under LGPL - original licence link has changed is not relivant.
1147  *
1148  * Fork - LGPL
1149  * <script type="text/javascript">
1150  */
1151
1152  
1153 Roo.data.Field = function(config){
1154     if(typeof config == "string"){
1155         config = {name: config};
1156     }
1157     Roo.apply(this, config);
1158     
1159     if(!this.type){
1160         this.type = "auto";
1161     }
1162     
1163     var st = Roo.data.SortTypes;
1164     // named sortTypes are supported, here we look them up
1165     if(typeof this.sortType == "string"){
1166         this.sortType = st[this.sortType];
1167     }
1168     
1169     // set default sortType for strings and dates
1170     if(!this.sortType){
1171         switch(this.type){
1172             case "string":
1173                 this.sortType = st.asUCString;
1174                 break;
1175             case "date":
1176                 this.sortType = st.asDate;
1177                 break;
1178             default:
1179                 this.sortType = st.none;
1180         }
1181     }
1182
1183     // define once
1184     var stripRe = /[\$,%]/g;
1185
1186     // prebuilt conversion function for this field, instead of
1187     // switching every time we're reading a value
1188     if(!this.convert){
1189         var cv, dateFormat = this.dateFormat;
1190         switch(this.type){
1191             case "":
1192             case "auto":
1193             case undefined:
1194                 cv = function(v){ return v; };
1195                 break;
1196             case "string":
1197                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1198                 break;
1199             case "int":
1200                 cv = function(v){
1201                     return v !== undefined && v !== null && v !== '' ?
1202                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1203                     };
1204                 break;
1205             case "float":
1206                 cv = function(v){
1207                     return v !== undefined && v !== null && v !== '' ?
1208                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1209                     };
1210                 break;
1211             case "bool":
1212             case "boolean":
1213                 cv = function(v){ return v === true || v === "true" || v == 1; };
1214                 break;
1215             case "date":
1216                 cv = function(v){
1217                     if(!v){
1218                         return '';
1219                     }
1220                     if(v instanceof Date){
1221                         return v;
1222                     }
1223                     if(dateFormat){
1224                         if(dateFormat == "timestamp"){
1225                             return new Date(v*1000);
1226                         }
1227                         return Date.parseDate(v, dateFormat);
1228                     }
1229                     var parsed = Date.parse(v);
1230                     return parsed ? new Date(parsed) : null;
1231                 };
1232              break;
1233             
1234         }
1235         this.convert = cv;
1236     }
1237 };
1238
1239 Roo.data.Field.prototype = {
1240     dateFormat: null,
1241     defaultValue: "",
1242     mapping: null,
1243     sortType : null,
1244     sortDir : "ASC"
1245 };/*
1246  * Based on:
1247  * Ext JS Library 1.1.1
1248  * Copyright(c) 2006-2007, Ext JS, LLC.
1249  *
1250  * Originally Released Under LGPL - original licence link has changed is not relivant.
1251  *
1252  * Fork - LGPL
1253  * <script type="text/javascript">
1254  */
1255  
1256 // Base class for reading structured data from a data source.  This class is intended to be
1257 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1258
1259 /**
1260  * @class Roo.data.DataReader
1261  * Base class for reading structured data from a data source.  This class is intended to be
1262  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1263  */
1264
1265 Roo.data.DataReader = function(meta, recordType){
1266     
1267     this.meta = meta;
1268     
1269     this.recordType = recordType instanceof Array ? 
1270         Roo.data.Record.create(recordType) : recordType;
1271 };
1272
1273 Roo.data.DataReader.prototype = {
1274      /**
1275      * Create an empty record
1276      * @param {Object} data (optional) - overlay some values
1277      * @return {Roo.data.Record} record created.
1278      */
1279     newRow :  function(d) {
1280         var da =  {};
1281         this.recordType.prototype.fields.each(function(c) {
1282             switch( c.type) {
1283                 case 'int' : da[c.name] = 0; break;
1284                 case 'date' : da[c.name] = new Date(); break;
1285                 case 'float' : da[c.name] = 0.0; break;
1286                 case 'boolean' : da[c.name] = false; break;
1287                 default : da[c.name] = ""; break;
1288             }
1289             
1290         });
1291         return new this.recordType(Roo.apply(da, d));
1292     }
1293     
1294 };/*
1295  * Based on:
1296  * Ext JS Library 1.1.1
1297  * Copyright(c) 2006-2007, Ext JS, LLC.
1298  *
1299  * Originally Released Under LGPL - original licence link has changed is not relivant.
1300  *
1301  * Fork - LGPL
1302  * <script type="text/javascript">
1303  */
1304
1305 /**
1306  * @class Roo.data.DataProxy
1307  * @extends Roo.data.Observable
1308  * This class is an abstract base class for implementations which provide retrieval of
1309  * unformatted data objects.<br>
1310  * <p>
1311  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1312  * (of the appropriate type which knows how to parse the data object) to provide a block of
1313  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1314  * <p>
1315  * Custom implementations must implement the load method as described in
1316  * {@link Roo.data.HttpProxy#load}.
1317  */
1318 Roo.data.DataProxy = function(){
1319     this.addEvents({
1320         /**
1321          * @event beforeload
1322          * Fires before a network request is made to retrieve a data object.
1323          * @param {Object} This DataProxy object.
1324          * @param {Object} params The params parameter to the load function.
1325          */
1326         beforeload : true,
1327         /**
1328          * @event load
1329          * Fires before the load method's callback is called.
1330          * @param {Object} This DataProxy object.
1331          * @param {Object} o The data object.
1332          * @param {Object} arg The callback argument object passed to the load function.
1333          */
1334         load : true,
1335         /**
1336          * @event loadexception
1337          * Fires if an Exception occurs during data retrieval.
1338          * @param {Object} This DataProxy object.
1339          * @param {Object} o The data object.
1340          * @param {Object} arg The callback argument object passed to the load function.
1341          * @param {Object} e The Exception.
1342          */
1343         loadexception : true
1344     });
1345     Roo.data.DataProxy.superclass.constructor.call(this);
1346 };
1347
1348 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1349
1350     /**
1351      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1352      */
1353 /*
1354  * Based on:
1355  * Ext JS Library 1.1.1
1356  * Copyright(c) 2006-2007, Ext JS, LLC.
1357  *
1358  * Originally Released Under LGPL - original licence link has changed is not relivant.
1359  *
1360  * Fork - LGPL
1361  * <script type="text/javascript">
1362  */
1363 /**
1364  * @class Roo.data.MemoryProxy
1365  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1366  * to the Reader when its load method is called.
1367  * @constructor
1368  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1369  */
1370 Roo.data.MemoryProxy = function(data){
1371     if (data.data) {
1372         data = data.data;
1373     }
1374     Roo.data.MemoryProxy.superclass.constructor.call(this);
1375     this.data = data;
1376 };
1377
1378 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1379     
1380     /**
1381      * Load data from the requested source (in this case an in-memory
1382      * data object passed to the constructor), read the data object into
1383      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1384      * process that block using the passed callback.
1385      * @param {Object} params This parameter is not used by the MemoryProxy class.
1386      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1387      * object into a block of Roo.data.Records.
1388      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1389      * The function must be passed <ul>
1390      * <li>The Record block object</li>
1391      * <li>The "arg" argument from the load function</li>
1392      * <li>A boolean success indicator</li>
1393      * </ul>
1394      * @param {Object} scope The scope in which to call the callback
1395      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1396      */
1397     load : function(params, reader, callback, scope, arg){
1398         params = params || {};
1399         var result;
1400         try {
1401             result = reader.readRecords(this.data);
1402         }catch(e){
1403             this.fireEvent("loadexception", this, arg, null, e);
1404             callback.call(scope, null, arg, false);
1405             return;
1406         }
1407         callback.call(scope, result, arg, true);
1408     },
1409     
1410     // private
1411     update : function(params, records){
1412         
1413     }
1414 });/*
1415  * Based on:
1416  * Ext JS Library 1.1.1
1417  * Copyright(c) 2006-2007, Ext JS, LLC.
1418  *
1419  * Originally Released Under LGPL - original licence link has changed is not relivant.
1420  *
1421  * Fork - LGPL
1422  * <script type="text/javascript">
1423  */
1424 /**
1425  * @class Roo.data.HttpProxy
1426  * @extends Roo.data.DataProxy
1427  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1428  * configured to reference a certain URL.<br><br>
1429  * <p>
1430  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1431  * from which the running page was served.<br><br>
1432  * <p>
1433  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1434  * <p>
1435  * Be aware that to enable the browser to parse an XML document, the server must set
1436  * the Content-Type header in the HTTP response to "text/xml".
1437  * @constructor
1438  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1439  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1440  * will be used to make the request.
1441  */
1442 Roo.data.HttpProxy = function(conn){
1443     Roo.data.HttpProxy.superclass.constructor.call(this);
1444     // is conn a conn config or a real conn?
1445     this.conn = conn;
1446     this.useAjax = !conn || !conn.events;
1447   
1448 };
1449
1450 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1451     // thse are take from connection...
1452     
1453     /**
1454      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1455      */
1456     /**
1457      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1458      * extra parameters to each request made by this object. (defaults to undefined)
1459      */
1460     /**
1461      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1462      *  to each request made by this object. (defaults to undefined)
1463      */
1464     /**
1465      * @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)
1466      */
1467     /**
1468      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1469      */
1470      /**
1471      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1472      * @type Boolean
1473      */
1474   
1475
1476     /**
1477      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1478      * @type Boolean
1479      */
1480     /**
1481      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1482      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1483      * a finer-grained basis than the DataProxy events.
1484      */
1485     getConnection : function(){
1486         return this.useAjax ? Roo.Ajax : this.conn;
1487     },
1488
1489     /**
1490      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1491      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1492      * process that block using the passed callback.
1493      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1494      * for the request to the remote server.
1495      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1496      * object into a block of Roo.data.Records.
1497      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1498      * The function must be passed <ul>
1499      * <li>The Record block object</li>
1500      * <li>The "arg" argument from the load function</li>
1501      * <li>A boolean success indicator</li>
1502      * </ul>
1503      * @param {Object} scope The scope in which to call the callback
1504      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1505      */
1506     load : function(params, reader, callback, scope, arg){
1507         if(this.fireEvent("beforeload", this, params) !== false){
1508             var  o = {
1509                 params : params || {},
1510                 request: {
1511                     callback : callback,
1512                     scope : scope,
1513                     arg : arg
1514                 },
1515                 reader: reader,
1516                 callback : this.loadResponse,
1517                 scope: this
1518             };
1519             if(this.useAjax){
1520                 Roo.applyIf(o, this.conn);
1521                 if(this.activeRequest){
1522                     Roo.Ajax.abort(this.activeRequest);
1523                 }
1524                 this.activeRequest = Roo.Ajax.request(o);
1525             }else{
1526                 this.conn.request(o);
1527             }
1528         }else{
1529             callback.call(scope||this, null, arg, false);
1530         }
1531     },
1532
1533     // private
1534     loadResponse : function(o, success, response){
1535         delete this.activeRequest;
1536         if(!success){
1537             this.fireEvent("loadexception", this, o, response);
1538             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1539             return;
1540         }
1541         var result;
1542         try {
1543             result = o.reader.read(response);
1544         }catch(e){
1545             this.fireEvent("loadexception", this, o, response, e);
1546             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1547             return;
1548         }
1549         
1550         this.fireEvent("load", this, o, o.request.arg);
1551         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1552     },
1553
1554     // private
1555     update : function(dataSet){
1556
1557     },
1558
1559     // private
1560     updateResponse : function(dataSet){
1561
1562     }
1563 });/*
1564  * Based on:
1565  * Ext JS Library 1.1.1
1566  * Copyright(c) 2006-2007, Ext JS, LLC.
1567  *
1568  * Originally Released Under LGPL - original licence link has changed is not relivant.
1569  *
1570  * Fork - LGPL
1571  * <script type="text/javascript">
1572  */
1573
1574 /**
1575  * @class Roo.data.ScriptTagProxy
1576  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1577  * other than the originating domain of the running page.<br><br>
1578  * <p>
1579  * <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
1580  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1581  * <p>
1582  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1583  * source code that is used as the source inside a &lt;script> tag.<br><br>
1584  * <p>
1585  * In order for the browser to process the returned data, the server must wrap the data object
1586  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1587  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1588  * depending on whether the callback name was passed:
1589  * <p>
1590  * <pre><code>
1591 boolean scriptTag = false;
1592 String cb = request.getParameter("callback");
1593 if (cb != null) {
1594     scriptTag = true;
1595     response.setContentType("text/javascript");
1596 } else {
1597     response.setContentType("application/x-json");
1598 }
1599 Writer out = response.getWriter();
1600 if (scriptTag) {
1601     out.write(cb + "(");
1602 }
1603 out.print(dataBlock.toJsonString());
1604 if (scriptTag) {
1605     out.write(");");
1606 }
1607 </pre></code>
1608  *
1609  * @constructor
1610  * @param {Object} config A configuration object.
1611  */
1612 Roo.data.ScriptTagProxy = function(config){
1613     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1614     Roo.apply(this, config);
1615     this.head = document.getElementsByTagName("head")[0];
1616 };
1617
1618 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1619
1620 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1621     /**
1622      * @cfg {String} url The URL from which to request the data object.
1623      */
1624     /**
1625      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1626      */
1627     timeout : 30000,
1628     /**
1629      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1630      * the server the name of the callback function set up by the load call to process the returned data object.
1631      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1632      * javascript output which calls this named function passing the data object as its only parameter.
1633      */
1634     callbackParam : "callback",
1635     /**
1636      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1637      * name to the request.
1638      */
1639     nocache : true,
1640
1641     /**
1642      * Load data from the configured URL, read the data object into
1643      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1644      * process that block using the passed callback.
1645      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1646      * for the request to the remote server.
1647      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1648      * object into a block of Roo.data.Records.
1649      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1650      * The function must be passed <ul>
1651      * <li>The Record block object</li>
1652      * <li>The "arg" argument from the load function</li>
1653      * <li>A boolean success indicator</li>
1654      * </ul>
1655      * @param {Object} scope The scope in which to call the callback
1656      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1657      */
1658     load : function(params, reader, callback, scope, arg){
1659         if(this.fireEvent("beforeload", this, params) !== false){
1660
1661             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1662
1663             var url = this.url;
1664             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1665             if(this.nocache){
1666                 url += "&_dc=" + (new Date().getTime());
1667             }
1668             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1669             var trans = {
1670                 id : transId,
1671                 cb : "stcCallback"+transId,
1672                 scriptId : "stcScript"+transId,
1673                 params : params,
1674                 arg : arg,
1675                 url : url,
1676                 callback : callback,
1677                 scope : scope,
1678                 reader : reader
1679             };
1680             var conn = this;
1681
1682             window[trans.cb] = function(o){
1683                 conn.handleResponse(o, trans);
1684             };
1685
1686             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1687
1688             if(this.autoAbort !== false){
1689                 this.abort();
1690             }
1691
1692             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1693
1694             var script = document.createElement("script");
1695             script.setAttribute("src", url);
1696             script.setAttribute("type", "text/javascript");
1697             script.setAttribute("id", trans.scriptId);
1698             this.head.appendChild(script);
1699
1700             this.trans = trans;
1701         }else{
1702             callback.call(scope||this, null, arg, false);
1703         }
1704     },
1705
1706     // private
1707     isLoading : function(){
1708         return this.trans ? true : false;
1709     },
1710
1711     /**
1712      * Abort the current server request.
1713      */
1714     abort : function(){
1715         if(this.isLoading()){
1716             this.destroyTrans(this.trans);
1717         }
1718     },
1719
1720     // private
1721     destroyTrans : function(trans, isLoaded){
1722         this.head.removeChild(document.getElementById(trans.scriptId));
1723         clearTimeout(trans.timeoutId);
1724         if(isLoaded){
1725             window[trans.cb] = undefined;
1726             try{
1727                 delete window[trans.cb];
1728             }catch(e){}
1729         }else{
1730             // if hasn't been loaded, wait for load to remove it to prevent script error
1731             window[trans.cb] = function(){
1732                 window[trans.cb] = undefined;
1733                 try{
1734                     delete window[trans.cb];
1735                 }catch(e){}
1736             };
1737         }
1738     },
1739
1740     // private
1741     handleResponse : function(o, trans){
1742         this.trans = false;
1743         this.destroyTrans(trans, true);
1744         var result;
1745         try {
1746             result = trans.reader.readRecords(o);
1747         }catch(e){
1748             this.fireEvent("loadexception", this, o, trans.arg, e);
1749             trans.callback.call(trans.scope||window, null, trans.arg, false);
1750             return;
1751         }
1752         this.fireEvent("load", this, o, trans.arg);
1753         trans.callback.call(trans.scope||window, result, trans.arg, true);
1754     },
1755
1756     // private
1757     handleFailure : function(trans){
1758         this.trans = false;
1759         this.destroyTrans(trans, false);
1760         this.fireEvent("loadexception", this, null, trans.arg);
1761         trans.callback.call(trans.scope||window, null, trans.arg, false);
1762     }
1763 });/*
1764  * Based on:
1765  * Ext JS Library 1.1.1
1766  * Copyright(c) 2006-2007, Ext JS, LLC.
1767  *
1768  * Originally Released Under LGPL - original licence link has changed is not relivant.
1769  *
1770  * Fork - LGPL
1771  * <script type="text/javascript">
1772  */
1773
1774 /**
1775  * @class Roo.data.JsonReader
1776  * @extends Roo.data.DataReader
1777  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1778  * based on mappings in a provided Roo.data.Record constructor.
1779  * 
1780  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1781  * in the reply previously. 
1782  * 
1783  * <p>
1784  * Example code:
1785  * <pre><code>
1786 var RecordDef = Roo.data.Record.create([
1787     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1788     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1789 ]);
1790 var myReader = new Roo.data.JsonReader({
1791     totalProperty: "results",    // The property which contains the total dataset size (optional)
1792     root: "rows",                // The property which contains an Array of row objects
1793     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1794 }, RecordDef);
1795 </code></pre>
1796  * <p>
1797  * This would consume a JSON file like this:
1798  * <pre><code>
1799 { 'results': 2, 'rows': [
1800     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1801     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1802 }
1803 </code></pre>
1804  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1805  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1806  * paged from the remote server.
1807  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1808  * @cfg {String} root name of the property which contains the Array of row objects.
1809  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1810  * @cfg {Array} fields Array of field definition objects
1811  * @constructor
1812  * Create a new JsonReader
1813  * @param {Object} meta Metadata configuration options
1814  * @param {Object} recordType Either an Array of field definition objects,
1815  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1816  */
1817 Roo.data.JsonReader = function(meta, recordType){
1818     
1819     meta = meta || {};
1820     // set some defaults:
1821     Roo.applyIf(meta, {
1822         totalProperty: 'total',
1823         successProperty : 'success',
1824         root : 'data',
1825         id : 'id'
1826     });
1827     
1828     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1829 };
1830 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1831     
1832     /**
1833      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1834      * Used by Store query builder to append _requestMeta to params.
1835      * 
1836      */
1837     metaFromRemote : false,
1838     /**
1839      * This method is only used by a DataProxy which has retrieved data from a remote server.
1840      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1841      * @return {Object} data A data block which is used by an Roo.data.Store object as
1842      * a cache of Roo.data.Records.
1843      */
1844     read : function(response){
1845         var json = response.responseText;
1846        
1847         var o = /* eval:var:o */ eval("("+json+")");
1848         if(!o) {
1849             throw {message: "JsonReader.read: Json object not found"};
1850         }
1851         
1852         if(o.metaData){
1853             
1854             delete this.ef;
1855             this.metaFromRemote = true;
1856             this.meta = o.metaData;
1857             this.recordType = Roo.data.Record.create(o.metaData.fields);
1858             this.onMetaChange(this.meta, this.recordType, o);
1859         }
1860         return this.readRecords(o);
1861     },
1862
1863     // private function a store will implement
1864     onMetaChange : function(meta, recordType, o){
1865
1866     },
1867
1868     /**
1869          * @ignore
1870          */
1871     simpleAccess: function(obj, subsc) {
1872         return obj[subsc];
1873     },
1874
1875         /**
1876          * @ignore
1877          */
1878     getJsonAccessor: function(){
1879         var re = /[\[\.]/;
1880         return function(expr) {
1881             try {
1882                 return(re.test(expr))
1883                     ? new Function("obj", "return obj." + expr)
1884                     : function(obj){
1885                         return obj[expr];
1886                     };
1887             } catch(e){}
1888             return Roo.emptyFn;
1889         };
1890     }(),
1891
1892     /**
1893      * Create a data block containing Roo.data.Records from an XML document.
1894      * @param {Object} o An object which contains an Array of row objects in the property specified
1895      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1896      * which contains the total size of the dataset.
1897      * @return {Object} data A data block which is used by an Roo.data.Store object as
1898      * a cache of Roo.data.Records.
1899      */
1900     readRecords : function(o){
1901         /**
1902          * After any data loads, the raw JSON data is available for further custom processing.
1903          * @type Object
1904          */
1905         this.o = o;
1906         var s = this.meta, Record = this.recordType,
1907             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1908
1909 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1910         if (!this.ef) {
1911             if(s.totalProperty) {
1912                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1913                 }
1914                 if(s.successProperty) {
1915                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1916                 }
1917                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1918                 if (s.id) {
1919                         var g = this.getJsonAccessor(s.id);
1920                         this.getId = function(rec) {
1921                                 var r = g(rec);  
1922                                 return (r === undefined || r === "") ? null : r;
1923                         };
1924                 } else {
1925                         this.getId = function(){return null;};
1926                 }
1927             this.ef = [];
1928             for(var jj = 0; jj < fl; jj++){
1929                 f = fi[jj];
1930                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1931                 this.ef[jj] = this.getJsonAccessor(map);
1932             }
1933         }
1934
1935         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1936         if(s.totalProperty){
1937             var vt = parseInt(this.getTotal(o), 10);
1938             if(!isNaN(vt)){
1939                 totalRecords = vt;
1940             }
1941         }
1942         if(s.successProperty){
1943             var vs = this.getSuccess(o);
1944             if(vs === false || vs === 'false'){
1945                 success = false;
1946             }
1947         }
1948         var records = [];
1949         for(var i = 0; i < c; i++){
1950                 var n = root[i];
1951             var values = {};
1952             var id = this.getId(n);
1953             for(var j = 0; j < fl; j++){
1954                 f = fi[j];
1955             var v = this.ef[j](n);
1956             if (!f.convert) {
1957                 Roo.log('missing convert for ' + f.name);
1958                 Roo.log(f);
1959                 continue;
1960             }
1961             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1962             }
1963             var record = new Record(values, id);
1964             record.json = n;
1965             records[i] = record;
1966         }
1967         return {
1968             raw : o,
1969             success : success,
1970             records : records,
1971             totalRecords : totalRecords
1972         };
1973     }
1974 });/*
1975  * Based on:
1976  * Ext JS Library 1.1.1
1977  * Copyright(c) 2006-2007, Ext JS, LLC.
1978  *
1979  * Originally Released Under LGPL - original licence link has changed is not relivant.
1980  *
1981  * Fork - LGPL
1982  * <script type="text/javascript">
1983  */
1984
1985 /**
1986  * @class Roo.data.XmlReader
1987  * @extends Roo.data.DataReader
1988  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1989  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1990  * <p>
1991  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1992  * header in the HTTP response must be set to "text/xml".</em>
1993  * <p>
1994  * Example code:
1995  * <pre><code>
1996 var RecordDef = Roo.data.Record.create([
1997    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1998    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1999 ]);
2000 var myReader = new Roo.data.XmlReader({
2001    totalRecords: "results", // The element which contains the total dataset size (optional)
2002    record: "row",           // The repeated element which contains row information
2003    id: "id"                 // The element within the row that provides an ID for the record (optional)
2004 }, RecordDef);
2005 </code></pre>
2006  * <p>
2007  * This would consume an XML file like this:
2008  * <pre><code>
2009 &lt;?xml?>
2010 &lt;dataset>
2011  &lt;results>2&lt;/results>
2012  &lt;row>
2013    &lt;id>1&lt;/id>
2014    &lt;name>Bill&lt;/name>
2015    &lt;occupation>Gardener&lt;/occupation>
2016  &lt;/row>
2017  &lt;row>
2018    &lt;id>2&lt;/id>
2019    &lt;name>Ben&lt;/name>
2020    &lt;occupation>Horticulturalist&lt;/occupation>
2021  &lt;/row>
2022 &lt;/dataset>
2023 </code></pre>
2024  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2025  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2026  * paged from the remote server.
2027  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2028  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2029  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2030  * a record identifier value.
2031  * @constructor
2032  * Create a new XmlReader
2033  * @param {Object} meta Metadata configuration options
2034  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2035  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2036  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2037  */
2038 Roo.data.XmlReader = function(meta, recordType){
2039     meta = meta || {};
2040     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2041 };
2042 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2043     /**
2044      * This method is only used by a DataProxy which has retrieved data from a remote server.
2045          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2046          * to contain a method called 'responseXML' that returns an XML document object.
2047      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2048      * a cache of Roo.data.Records.
2049      */
2050     read : function(response){
2051         var doc = response.responseXML;
2052         if(!doc) {
2053             throw {message: "XmlReader.read: XML Document not available"};
2054         }
2055         return this.readRecords(doc);
2056     },
2057
2058     /**
2059      * Create a data block containing Roo.data.Records from an XML document.
2060          * @param {Object} doc A parsed XML document.
2061      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2062      * a cache of Roo.data.Records.
2063      */
2064     readRecords : function(doc){
2065         /**
2066          * After any data loads/reads, the raw XML Document is available for further custom processing.
2067          * @type XMLDocument
2068          */
2069         this.xmlData = doc;
2070         var root = doc.documentElement || doc;
2071         var q = Roo.DomQuery;
2072         var recordType = this.recordType, fields = recordType.prototype.fields;
2073         var sid = this.meta.id;
2074         var totalRecords = 0, success = true;
2075         if(this.meta.totalRecords){
2076             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2077         }
2078         
2079         if(this.meta.success){
2080             var sv = q.selectValue(this.meta.success, root, true);
2081             success = sv !== false && sv !== 'false';
2082         }
2083         var records = [];
2084         var ns = q.select(this.meta.record, root);
2085         for(var i = 0, len = ns.length; i < len; i++) {
2086                 var n = ns[i];
2087                 var values = {};
2088                 var id = sid ? q.selectValue(sid, n) : undefined;
2089                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2090                     var f = fields.items[j];
2091                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2092                     v = f.convert(v);
2093                     values[f.name] = v;
2094                 }
2095                 var record = new recordType(values, id);
2096                 record.node = n;
2097                 records[records.length] = record;
2098             }
2099
2100             return {
2101                 success : success,
2102                 records : records,
2103                 totalRecords : totalRecords || records.length
2104             };
2105     }
2106 });/*
2107  * Based on:
2108  * Ext JS Library 1.1.1
2109  * Copyright(c) 2006-2007, Ext JS, LLC.
2110  *
2111  * Originally Released Under LGPL - original licence link has changed is not relivant.
2112  *
2113  * Fork - LGPL
2114  * <script type="text/javascript">
2115  */
2116
2117 /**
2118  * @class Roo.data.ArrayReader
2119  * @extends Roo.data.DataReader
2120  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2121  * Each element of that Array represents a row of data fields. The
2122  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2123  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2124  * <p>
2125  * Example code:.
2126  * <pre><code>
2127 var RecordDef = Roo.data.Record.create([
2128     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2129     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2130 ]);
2131 var myReader = new Roo.data.ArrayReader({
2132     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2133 }, RecordDef);
2134 </code></pre>
2135  * <p>
2136  * This would consume an Array like this:
2137  * <pre><code>
2138 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2139   </code></pre>
2140  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
2141  * @constructor
2142  * Create a new JsonReader
2143  * @param {Object} meta Metadata configuration options.
2144  * @param {Object} recordType Either an Array of field definition objects
2145  * as specified to {@link Roo.data.Record#create},
2146  * or an {@link Roo.data.Record} object
2147  * created using {@link Roo.data.Record#create}.
2148  */
2149 Roo.data.ArrayReader = function(meta, recordType){
2150     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
2151 };
2152
2153 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2154     /**
2155      * Create a data block containing Roo.data.Records from an XML document.
2156      * @param {Object} o An Array of row objects which represents the dataset.
2157      * @return {Object} data A data block which is used by an Roo.data.Store object as
2158      * a cache of Roo.data.Records.
2159      */
2160     readRecords : function(o){
2161         var sid = this.meta ? this.meta.id : null;
2162         var recordType = this.recordType, fields = recordType.prototype.fields;
2163         var records = [];
2164         var root = o;
2165             for(var i = 0; i < root.length; i++){
2166                     var n = root[i];
2167                 var values = {};
2168                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2169                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2170                 var f = fields.items[j];
2171                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2172                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2173                 v = f.convert(v);
2174                 values[f.name] = v;
2175             }
2176                 var record = new recordType(values, id);
2177                 record.json = n;
2178                 records[records.length] = record;
2179             }
2180             return {
2181                 records : records,
2182                 totalRecords : records.length
2183             };
2184     }
2185 });/*
2186  * Based on:
2187  * Ext JS Library 1.1.1
2188  * Copyright(c) 2006-2007, Ext JS, LLC.
2189  *
2190  * Originally Released Under LGPL - original licence link has changed is not relivant.
2191  *
2192  * Fork - LGPL
2193  * <script type="text/javascript">
2194  */
2195
2196
2197 /**
2198  * @class Roo.data.Tree
2199  * @extends Roo.util.Observable
2200  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2201  * in the tree have most standard DOM functionality.
2202  * @constructor
2203  * @param {Node} root (optional) The root node
2204  */
2205 Roo.data.Tree = function(root){
2206    this.nodeHash = {};
2207    /**
2208     * The root node for this tree
2209     * @type Node
2210     */
2211    this.root = null;
2212    if(root){
2213        this.setRootNode(root);
2214    }
2215    this.addEvents({
2216        /**
2217         * @event append
2218         * Fires when a new child node is appended to a node in this tree.
2219         * @param {Tree} tree The owner tree
2220         * @param {Node} parent The parent node
2221         * @param {Node} node The newly appended node
2222         * @param {Number} index The index of the newly appended node
2223         */
2224        "append" : true,
2225        /**
2226         * @event remove
2227         * Fires when a child node is removed from a node in this tree.
2228         * @param {Tree} tree The owner tree
2229         * @param {Node} parent The parent node
2230         * @param {Node} node The child node removed
2231         */
2232        "remove" : true,
2233        /**
2234         * @event move
2235         * Fires when a node is moved to a new location in the tree
2236         * @param {Tree} tree The owner tree
2237         * @param {Node} node The node moved
2238         * @param {Node} oldParent The old parent of this node
2239         * @param {Node} newParent The new parent of this node
2240         * @param {Number} index The index it was moved to
2241         */
2242        "move" : true,
2243        /**
2244         * @event insert
2245         * Fires when a new child node is inserted in a node in this tree.
2246         * @param {Tree} tree The owner tree
2247         * @param {Node} parent The parent node
2248         * @param {Node} node The child node inserted
2249         * @param {Node} refNode The child node the node was inserted before
2250         */
2251        "insert" : true,
2252        /**
2253         * @event beforeappend
2254         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2255         * @param {Tree} tree The owner tree
2256         * @param {Node} parent The parent node
2257         * @param {Node} node The child node to be appended
2258         */
2259        "beforeappend" : true,
2260        /**
2261         * @event beforeremove
2262         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2263         * @param {Tree} tree The owner tree
2264         * @param {Node} parent The parent node
2265         * @param {Node} node The child node to be removed
2266         */
2267        "beforeremove" : true,
2268        /**
2269         * @event beforemove
2270         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} node The node being moved
2273         * @param {Node} oldParent The parent of the node
2274         * @param {Node} newParent The new parent the node is moving to
2275         * @param {Number} index The index it is being moved to
2276         */
2277        "beforemove" : true,
2278        /**
2279         * @event beforeinsert
2280         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2281         * @param {Tree} tree The owner tree
2282         * @param {Node} parent The parent node
2283         * @param {Node} node The child node to be inserted
2284         * @param {Node} refNode The child node the node is being inserted before
2285         */
2286        "beforeinsert" : true
2287    });
2288
2289     Roo.data.Tree.superclass.constructor.call(this);
2290 };
2291
2292 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2293     pathSeparator: "/",
2294
2295     proxyNodeEvent : function(){
2296         return this.fireEvent.apply(this, arguments);
2297     },
2298
2299     /**
2300      * Returns the root node for this tree.
2301      * @return {Node}
2302      */
2303     getRootNode : function(){
2304         return this.root;
2305     },
2306
2307     /**
2308      * Sets the root node for this tree.
2309      * @param {Node} node
2310      * @return {Node}
2311      */
2312     setRootNode : function(node){
2313         this.root = node;
2314         node.ownerTree = this;
2315         node.isRoot = true;
2316         this.registerNode(node);
2317         return node;
2318     },
2319
2320     /**
2321      * Gets a node in this tree by its id.
2322      * @param {String} id
2323      * @return {Node}
2324      */
2325     getNodeById : function(id){
2326         return this.nodeHash[id];
2327     },
2328
2329     registerNode : function(node){
2330         this.nodeHash[node.id] = node;
2331     },
2332
2333     unregisterNode : function(node){
2334         delete this.nodeHash[node.id];
2335     },
2336
2337     toString : function(){
2338         return "[Tree"+(this.id?" "+this.id:"")+"]";
2339     }
2340 });
2341
2342 /**
2343  * @class Roo.data.Node
2344  * @extends Roo.util.Observable
2345  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2346  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2347  * @constructor
2348  * @param {Object} attributes The attributes/config for the node
2349  */
2350 Roo.data.Node = function(attributes){
2351     /**
2352      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2353      * @type {Object}
2354      */
2355     this.attributes = attributes || {};
2356     this.leaf = this.attributes.leaf;
2357     /**
2358      * The node id. @type String
2359      */
2360     this.id = this.attributes.id;
2361     if(!this.id){
2362         this.id = Roo.id(null, "ynode-");
2363         this.attributes.id = this.id;
2364     }
2365      
2366     
2367     /**
2368      * All child nodes of this node. @type Array
2369      */
2370     this.childNodes = [];
2371     if(!this.childNodes.indexOf){ // indexOf is a must
2372         this.childNodes.indexOf = function(o){
2373             for(var i = 0, len = this.length; i < len; i++){
2374                 if(this[i] == o) {
2375                     return i;
2376                 }
2377             }
2378             return -1;
2379         };
2380     }
2381     /**
2382      * The parent node for this node. @type Node
2383      */
2384     this.parentNode = null;
2385     /**
2386      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2387      */
2388     this.firstChild = null;
2389     /**
2390      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2391      */
2392     this.lastChild = null;
2393     /**
2394      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2395      */
2396     this.previousSibling = null;
2397     /**
2398      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2399      */
2400     this.nextSibling = null;
2401
2402     this.addEvents({
2403        /**
2404         * @event append
2405         * Fires when a new child node is appended
2406         * @param {Tree} tree The owner tree
2407         * @param {Node} this This node
2408         * @param {Node} node The newly appended node
2409         * @param {Number} index The index of the newly appended node
2410         */
2411        "append" : true,
2412        /**
2413         * @event remove
2414         * Fires when a child node is removed
2415         * @param {Tree} tree The owner tree
2416         * @param {Node} this This node
2417         * @param {Node} node The removed node
2418         */
2419        "remove" : true,
2420        /**
2421         * @event move
2422         * Fires when this node is moved to a new location in the tree
2423         * @param {Tree} tree The owner tree
2424         * @param {Node} this This node
2425         * @param {Node} oldParent The old parent of this node
2426         * @param {Node} newParent The new parent of this node
2427         * @param {Number} index The index it was moved to
2428         */
2429        "move" : true,
2430        /**
2431         * @event insert
2432         * Fires when a new child node is inserted.
2433         * @param {Tree} tree The owner tree
2434         * @param {Node} this This node
2435         * @param {Node} node The child node inserted
2436         * @param {Node} refNode The child node the node was inserted before
2437         */
2438        "insert" : true,
2439        /**
2440         * @event beforeappend
2441         * Fires before a new child is appended, return false to cancel the append.
2442         * @param {Tree} tree The owner tree
2443         * @param {Node} this This node
2444         * @param {Node} node The child node to be appended
2445         */
2446        "beforeappend" : true,
2447        /**
2448         * @event beforeremove
2449         * Fires before a child is removed, return false to cancel the remove.
2450         * @param {Tree} tree The owner tree
2451         * @param {Node} this This node
2452         * @param {Node} node The child node to be removed
2453         */
2454        "beforeremove" : true,
2455        /**
2456         * @event beforemove
2457         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} oldParent The parent of this node
2461         * @param {Node} newParent The new parent this node is moving to
2462         * @param {Number} index The index it is being moved to
2463         */
2464        "beforemove" : true,
2465        /**
2466         * @event beforeinsert
2467         * Fires before a new child is inserted, return false to cancel the insert.
2468         * @param {Tree} tree The owner tree
2469         * @param {Node} this This node
2470         * @param {Node} node The child node to be inserted
2471         * @param {Node} refNode The child node the node is being inserted before
2472         */
2473        "beforeinsert" : true
2474    });
2475     this.listeners = this.attributes.listeners;
2476     Roo.data.Node.superclass.constructor.call(this);
2477 };
2478
2479 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2480     fireEvent : function(evtName){
2481         // first do standard event for this node
2482         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2483             return false;
2484         }
2485         // then bubble it up to the tree if the event wasn't cancelled
2486         var ot = this.getOwnerTree();
2487         if(ot){
2488             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2489                 return false;
2490             }
2491         }
2492         return true;
2493     },
2494
2495     /**
2496      * Returns true if this node is a leaf
2497      * @return {Boolean}
2498      */
2499     isLeaf : function(){
2500         return this.leaf === true;
2501     },
2502
2503     // private
2504     setFirstChild : function(node){
2505         this.firstChild = node;
2506     },
2507
2508     //private
2509     setLastChild : function(node){
2510         this.lastChild = node;
2511     },
2512
2513
2514     /**
2515      * Returns true if this node is the last child of its parent
2516      * @return {Boolean}
2517      */
2518     isLast : function(){
2519        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2520     },
2521
2522     /**
2523      * Returns true if this node is the first child of its parent
2524      * @return {Boolean}
2525      */
2526     isFirst : function(){
2527        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2528     },
2529
2530     hasChildNodes : function(){
2531         return !this.isLeaf() && this.childNodes.length > 0;
2532     },
2533
2534     /**
2535      * Insert node(s) as the last child node of this node.
2536      * @param {Node/Array} node The node or Array of nodes to append
2537      * @return {Node} The appended node if single append, or null if an array was passed
2538      */
2539     appendChild : function(node){
2540         var multi = false;
2541         if(node instanceof Array){
2542             multi = node;
2543         }else if(arguments.length > 1){
2544             multi = arguments;
2545         }
2546         // if passed an array or multiple args do them one by one
2547         if(multi){
2548             for(var i = 0, len = multi.length; i < len; i++) {
2549                 this.appendChild(multi[i]);
2550             }
2551         }else{
2552             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2553                 return false;
2554             }
2555             var index = this.childNodes.length;
2556             var oldParent = node.parentNode;
2557             // it's a move, make sure we move it cleanly
2558             if(oldParent){
2559                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2560                     return false;
2561                 }
2562                 oldParent.removeChild(node);
2563             }
2564             index = this.childNodes.length;
2565             if(index == 0){
2566                 this.setFirstChild(node);
2567             }
2568             this.childNodes.push(node);
2569             node.parentNode = this;
2570             var ps = this.childNodes[index-1];
2571             if(ps){
2572                 node.previousSibling = ps;
2573                 ps.nextSibling = node;
2574             }else{
2575                 node.previousSibling = null;
2576             }
2577             node.nextSibling = null;
2578             this.setLastChild(node);
2579             node.setOwnerTree(this.getOwnerTree());
2580             this.fireEvent("append", this.ownerTree, this, node, index);
2581             if(oldParent){
2582                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2583             }
2584             return node;
2585         }
2586     },
2587
2588     /**
2589      * Removes a child node from this node.
2590      * @param {Node} node The node to remove
2591      * @return {Node} The removed node
2592      */
2593     removeChild : function(node){
2594         var index = this.childNodes.indexOf(node);
2595         if(index == -1){
2596             return false;
2597         }
2598         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2599             return false;
2600         }
2601
2602         // remove it from childNodes collection
2603         this.childNodes.splice(index, 1);
2604
2605         // update siblings
2606         if(node.previousSibling){
2607             node.previousSibling.nextSibling = node.nextSibling;
2608         }
2609         if(node.nextSibling){
2610             node.nextSibling.previousSibling = node.previousSibling;
2611         }
2612
2613         // update child refs
2614         if(this.firstChild == node){
2615             this.setFirstChild(node.nextSibling);
2616         }
2617         if(this.lastChild == node){
2618             this.setLastChild(node.previousSibling);
2619         }
2620
2621         node.setOwnerTree(null);
2622         // clear any references from the node
2623         node.parentNode = null;
2624         node.previousSibling = null;
2625         node.nextSibling = null;
2626         this.fireEvent("remove", this.ownerTree, this, node);
2627         return node;
2628     },
2629
2630     /**
2631      * Inserts the first node before the second node in this nodes childNodes collection.
2632      * @param {Node} node The node to insert
2633      * @param {Node} refNode The node to insert before (if null the node is appended)
2634      * @return {Node} The inserted node
2635      */
2636     insertBefore : function(node, refNode){
2637         if(!refNode){ // like standard Dom, refNode can be null for append
2638             return this.appendChild(node);
2639         }
2640         // nothing to do
2641         if(node == refNode){
2642             return false;
2643         }
2644
2645         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2646             return false;
2647         }
2648         var index = this.childNodes.indexOf(refNode);
2649         var oldParent = node.parentNode;
2650         var refIndex = index;
2651
2652         // when moving internally, indexes will change after remove
2653         if(oldParent == this && this.childNodes.indexOf(node) < index){
2654             refIndex--;
2655         }
2656
2657         // it's a move, make sure we move it cleanly
2658         if(oldParent){
2659             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2660                 return false;
2661             }
2662             oldParent.removeChild(node);
2663         }
2664         if(refIndex == 0){
2665             this.setFirstChild(node);
2666         }
2667         this.childNodes.splice(refIndex, 0, node);
2668         node.parentNode = this;
2669         var ps = this.childNodes[refIndex-1];
2670         if(ps){
2671             node.previousSibling = ps;
2672             ps.nextSibling = node;
2673         }else{
2674             node.previousSibling = null;
2675         }
2676         node.nextSibling = refNode;
2677         refNode.previousSibling = node;
2678         node.setOwnerTree(this.getOwnerTree());
2679         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2680         if(oldParent){
2681             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2682         }
2683         return node;
2684     },
2685
2686     /**
2687      * Returns the child node at the specified index.
2688      * @param {Number} index
2689      * @return {Node}
2690      */
2691     item : function(index){
2692         return this.childNodes[index];
2693     },
2694
2695     /**
2696      * Replaces one child node in this node with another.
2697      * @param {Node} newChild The replacement node
2698      * @param {Node} oldChild The node to replace
2699      * @return {Node} The replaced node
2700      */
2701     replaceChild : function(newChild, oldChild){
2702         this.insertBefore(newChild, oldChild);
2703         this.removeChild(oldChild);
2704         return oldChild;
2705     },
2706
2707     /**
2708      * Returns the index of a child node
2709      * @param {Node} node
2710      * @return {Number} The index of the node or -1 if it was not found
2711      */
2712     indexOf : function(child){
2713         return this.childNodes.indexOf(child);
2714     },
2715
2716     /**
2717      * Returns the tree this node is in.
2718      * @return {Tree}
2719      */
2720     getOwnerTree : function(){
2721         // if it doesn't have one, look for one
2722         if(!this.ownerTree){
2723             var p = this;
2724             while(p){
2725                 if(p.ownerTree){
2726                     this.ownerTree = p.ownerTree;
2727                     break;
2728                 }
2729                 p = p.parentNode;
2730             }
2731         }
2732         return this.ownerTree;
2733     },
2734
2735     /**
2736      * Returns depth of this node (the root node has a depth of 0)
2737      * @return {Number}
2738      */
2739     getDepth : function(){
2740         var depth = 0;
2741         var p = this;
2742         while(p.parentNode){
2743             ++depth;
2744             p = p.parentNode;
2745         }
2746         return depth;
2747     },
2748
2749     // private
2750     setOwnerTree : function(tree){
2751         // if it's move, we need to update everyone
2752         if(tree != this.ownerTree){
2753             if(this.ownerTree){
2754                 this.ownerTree.unregisterNode(this);
2755             }
2756             this.ownerTree = tree;
2757             var cs = this.childNodes;
2758             for(var i = 0, len = cs.length; i < len; i++) {
2759                 cs[i].setOwnerTree(tree);
2760             }
2761             if(tree){
2762                 tree.registerNode(this);
2763             }
2764         }
2765     },
2766
2767     /**
2768      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2769      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2770      * @return {String} The path
2771      */
2772     getPath : function(attr){
2773         attr = attr || "id";
2774         var p = this.parentNode;
2775         var b = [this.attributes[attr]];
2776         while(p){
2777             b.unshift(p.attributes[attr]);
2778             p = p.parentNode;
2779         }
2780         var sep = this.getOwnerTree().pathSeparator;
2781         return sep + b.join(sep);
2782     },
2783
2784     /**
2785      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2786      * function call will be the scope provided or the current node. The arguments to the function
2787      * will be the args provided or the current node. If the function returns false at any point,
2788      * the bubble is stopped.
2789      * @param {Function} fn The function to call
2790      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2791      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2792      */
2793     bubble : function(fn, scope, args){
2794         var p = this;
2795         while(p){
2796             if(fn.call(scope || p, args || p) === false){
2797                 break;
2798             }
2799             p = p.parentNode;
2800         }
2801     },
2802
2803     /**
2804      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2805      * function call will be the scope provided or the current node. The arguments to the function
2806      * will be the args provided or the current node. If the function returns false at any point,
2807      * the cascade is stopped on that branch.
2808      * @param {Function} fn The function to call
2809      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2810      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2811      */
2812     cascade : function(fn, scope, args){
2813         if(fn.call(scope || this, args || this) !== false){
2814             var cs = this.childNodes;
2815             for(var i = 0, len = cs.length; i < len; i++) {
2816                 cs[i].cascade(fn, scope, args);
2817             }
2818         }
2819     },
2820
2821     /**
2822      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2823      * function call will be the scope provided or the current node. The arguments to the function
2824      * will be the args provided or the current node. If the function returns false at any point,
2825      * the iteration stops.
2826      * @param {Function} fn The function to call
2827      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2828      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2829      */
2830     eachChild : function(fn, scope, args){
2831         var cs = this.childNodes;
2832         for(var i = 0, len = cs.length; i < len; i++) {
2833                 if(fn.call(scope || this, args || cs[i]) === false){
2834                     break;
2835                 }
2836         }
2837     },
2838
2839     /**
2840      * Finds the first child that has the attribute with the specified value.
2841      * @param {String} attribute The attribute name
2842      * @param {Mixed} value The value to search for
2843      * @return {Node} The found child or null if none was found
2844      */
2845     findChild : function(attribute, value){
2846         var cs = this.childNodes;
2847         for(var i = 0, len = cs.length; i < len; i++) {
2848                 if(cs[i].attributes[attribute] == value){
2849                     return cs[i];
2850                 }
2851         }
2852         return null;
2853     },
2854
2855     /**
2856      * Finds the first child by a custom function. The child matches if the function passed
2857      * returns true.
2858      * @param {Function} fn
2859      * @param {Object} scope (optional)
2860      * @return {Node} The found child or null if none was found
2861      */
2862     findChildBy : function(fn, scope){
2863         var cs = this.childNodes;
2864         for(var i = 0, len = cs.length; i < len; i++) {
2865                 if(fn.call(scope||cs[i], cs[i]) === true){
2866                     return cs[i];
2867                 }
2868         }
2869         return null;
2870     },
2871
2872     /**
2873      * Sorts this nodes children using the supplied sort function
2874      * @param {Function} fn
2875      * @param {Object} scope (optional)
2876      */
2877     sort : function(fn, scope){
2878         var cs = this.childNodes;
2879         var len = cs.length;
2880         if(len > 0){
2881             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2882             cs.sort(sortFn);
2883             for(var i = 0; i < len; i++){
2884                 var n = cs[i];
2885                 n.previousSibling = cs[i-1];
2886                 n.nextSibling = cs[i+1];
2887                 if(i == 0){
2888                     this.setFirstChild(n);
2889                 }
2890                 if(i == len-1){
2891                     this.setLastChild(n);
2892                 }
2893             }
2894         }
2895     },
2896
2897     /**
2898      * Returns true if this node is an ancestor (at any point) of the passed node.
2899      * @param {Node} node
2900      * @return {Boolean}
2901      */
2902     contains : function(node){
2903         return node.isAncestor(this);
2904     },
2905
2906     /**
2907      * Returns true if the passed node is an ancestor (at any point) of this node.
2908      * @param {Node} node
2909      * @return {Boolean}
2910      */
2911     isAncestor : function(node){
2912         var p = this.parentNode;
2913         while(p){
2914             if(p == node){
2915                 return true;
2916             }
2917             p = p.parentNode;
2918         }
2919         return false;
2920     },
2921
2922     toString : function(){
2923         return "[Node"+(this.id?" "+this.id:"")+"]";
2924     }
2925 });/*
2926  * Based on:
2927  * Ext JS Library 1.1.1
2928  * Copyright(c) 2006-2007, Ext JS, LLC.
2929  *
2930  * Originally Released Under LGPL - original licence link has changed is not relivant.
2931  *
2932  * Fork - LGPL
2933  * <script type="text/javascript">
2934  */
2935  (function(){ 
2936 /**
2937  * @class Roo.Layer
2938  * @extends Roo.Element
2939  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2940  * automatic maintaining of shadow/shim positions.
2941  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2942  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2943  * you can pass a string with a CSS class name. False turns off the shadow.
2944  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2945  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2946  * @cfg {String} cls CSS class to add to the element
2947  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2948  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2949  * @constructor
2950  * @param {Object} config An object with config options.
2951  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2952  */
2953
2954 Roo.Layer = function(config, existingEl){
2955     config = config || {};
2956     var dh = Roo.DomHelper;
2957     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2958     if(existingEl){
2959         this.dom = Roo.getDom(existingEl);
2960     }
2961     if(!this.dom){
2962         var o = config.dh || {tag: "div", cls: "x-layer"};
2963         this.dom = dh.append(pel, o);
2964     }
2965     if(config.cls){
2966         this.addClass(config.cls);
2967     }
2968     this.constrain = config.constrain !== false;
2969     this.visibilityMode = Roo.Element.VISIBILITY;
2970     if(config.id){
2971         this.id = this.dom.id = config.id;
2972     }else{
2973         this.id = Roo.id(this.dom);
2974     }
2975     this.zindex = config.zindex || this.getZIndex();
2976     this.position("absolute", this.zindex);
2977     if(config.shadow){
2978         this.shadowOffset = config.shadowOffset || 4;
2979         this.shadow = new Roo.Shadow({
2980             offset : this.shadowOffset,
2981             mode : config.shadow
2982         });
2983     }else{
2984         this.shadowOffset = 0;
2985     }
2986     this.useShim = config.shim !== false && Roo.useShims;
2987     this.useDisplay = config.useDisplay;
2988     this.hide();
2989 };
2990
2991 var supr = Roo.Element.prototype;
2992
2993 // shims are shared among layer to keep from having 100 iframes
2994 var shims = [];
2995
2996 Roo.extend(Roo.Layer, Roo.Element, {
2997
2998     getZIndex : function(){
2999         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3000     },
3001
3002     getShim : function(){
3003         if(!this.useShim){
3004             return null;
3005         }
3006         if(this.shim){
3007             return this.shim;
3008         }
3009         var shim = shims.shift();
3010         if(!shim){
3011             shim = this.createShim();
3012             shim.enableDisplayMode('block');
3013             shim.dom.style.display = 'none';
3014             shim.dom.style.visibility = 'visible';
3015         }
3016         var pn = this.dom.parentNode;
3017         if(shim.dom.parentNode != pn){
3018             pn.insertBefore(shim.dom, this.dom);
3019         }
3020         shim.setStyle('z-index', this.getZIndex()-2);
3021         this.shim = shim;
3022         return shim;
3023     },
3024
3025     hideShim : function(){
3026         if(this.shim){
3027             this.shim.setDisplayed(false);
3028             shims.push(this.shim);
3029             delete this.shim;
3030         }
3031     },
3032
3033     disableShadow : function(){
3034         if(this.shadow){
3035             this.shadowDisabled = true;
3036             this.shadow.hide();
3037             this.lastShadowOffset = this.shadowOffset;
3038             this.shadowOffset = 0;
3039         }
3040     },
3041
3042     enableShadow : function(show){
3043         if(this.shadow){
3044             this.shadowDisabled = false;
3045             this.shadowOffset = this.lastShadowOffset;
3046             delete this.lastShadowOffset;
3047             if(show){
3048                 this.sync(true);
3049             }
3050         }
3051     },
3052
3053     // private
3054     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3055     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3056     sync : function(doShow){
3057         var sw = this.shadow;
3058         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3059             var sh = this.getShim();
3060
3061             var w = this.getWidth(),
3062                 h = this.getHeight();
3063
3064             var l = this.getLeft(true),
3065                 t = this.getTop(true);
3066
3067             if(sw && !this.shadowDisabled){
3068                 if(doShow && !sw.isVisible()){
3069                     sw.show(this);
3070                 }else{
3071                     sw.realign(l, t, w, h);
3072                 }
3073                 if(sh){
3074                     if(doShow){
3075                        sh.show();
3076                     }
3077                     // fit the shim behind the shadow, so it is shimmed too
3078                     var a = sw.adjusts, s = sh.dom.style;
3079                     s.left = (Math.min(l, l+a.l))+"px";
3080                     s.top = (Math.min(t, t+a.t))+"px";
3081                     s.width = (w+a.w)+"px";
3082                     s.height = (h+a.h)+"px";
3083                 }
3084             }else if(sh){
3085                 if(doShow){
3086                    sh.show();
3087                 }
3088                 sh.setSize(w, h);
3089                 sh.setLeftTop(l, t);
3090             }
3091             
3092         }
3093     },
3094
3095     // private
3096     destroy : function(){
3097         this.hideShim();
3098         if(this.shadow){
3099             this.shadow.hide();
3100         }
3101         this.removeAllListeners();
3102         var pn = this.dom.parentNode;
3103         if(pn){
3104             pn.removeChild(this.dom);
3105         }
3106         Roo.Element.uncache(this.id);
3107     },
3108
3109     remove : function(){
3110         this.destroy();
3111     },
3112
3113     // private
3114     beginUpdate : function(){
3115         this.updating = true;
3116     },
3117
3118     // private
3119     endUpdate : function(){
3120         this.updating = false;
3121         this.sync(true);
3122     },
3123
3124     // private
3125     hideUnders : function(negOffset){
3126         if(this.shadow){
3127             this.shadow.hide();
3128         }
3129         this.hideShim();
3130     },
3131
3132     // private
3133     constrainXY : function(){
3134         if(this.constrain){
3135             var vw = Roo.lib.Dom.getViewWidth(),
3136                 vh = Roo.lib.Dom.getViewHeight();
3137             var s = Roo.get(document).getScroll();
3138
3139             var xy = this.getXY();
3140             var x = xy[0], y = xy[1];   
3141             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3142             // only move it if it needs it
3143             var moved = false;
3144             // first validate right/bottom
3145             if((x + w) > vw+s.left){
3146                 x = vw - w - this.shadowOffset;
3147                 moved = true;
3148             }
3149             if((y + h) > vh+s.top){
3150                 y = vh - h - this.shadowOffset;
3151                 moved = true;
3152             }
3153             // then make sure top/left isn't negative
3154             if(x < s.left){
3155                 x = s.left;
3156                 moved = true;
3157             }
3158             if(y < s.top){
3159                 y = s.top;
3160                 moved = true;
3161             }
3162             if(moved){
3163                 if(this.avoidY){
3164                     var ay = this.avoidY;
3165                     if(y <= ay && (y+h) >= ay){
3166                         y = ay-h-5;   
3167                     }
3168                 }
3169                 xy = [x, y];
3170                 this.storeXY(xy);
3171                 supr.setXY.call(this, xy);
3172                 this.sync();
3173             }
3174         }
3175     },
3176
3177     isVisible : function(){
3178         return this.visible;    
3179     },
3180
3181     // private
3182     showAction : function(){
3183         this.visible = true; // track visibility to prevent getStyle calls
3184         if(this.useDisplay === true){
3185             this.setDisplayed("");
3186         }else if(this.lastXY){
3187             supr.setXY.call(this, this.lastXY);
3188         }else if(this.lastLT){
3189             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3190         }
3191     },
3192
3193     // private
3194     hideAction : function(){
3195         this.visible = false;
3196         if(this.useDisplay === true){
3197             this.setDisplayed(false);
3198         }else{
3199             this.setLeftTop(-10000,-10000);
3200         }
3201     },
3202
3203     // overridden Element method
3204     setVisible : function(v, a, d, c, e){
3205         if(v){
3206             this.showAction();
3207         }
3208         if(a && v){
3209             var cb = function(){
3210                 this.sync(true);
3211                 if(c){
3212                     c();
3213                 }
3214             }.createDelegate(this);
3215             supr.setVisible.call(this, true, true, d, cb, e);
3216         }else{
3217             if(!v){
3218                 this.hideUnders(true);
3219             }
3220             var cb = c;
3221             if(a){
3222                 cb = function(){
3223                     this.hideAction();
3224                     if(c){
3225                         c();
3226                     }
3227                 }.createDelegate(this);
3228             }
3229             supr.setVisible.call(this, v, a, d, cb, e);
3230             if(v){
3231                 this.sync(true);
3232             }else if(!a){
3233                 this.hideAction();
3234             }
3235         }
3236     },
3237
3238     storeXY : function(xy){
3239         delete this.lastLT;
3240         this.lastXY = xy;
3241     },
3242
3243     storeLeftTop : function(left, top){
3244         delete this.lastXY;
3245         this.lastLT = [left, top];
3246     },
3247
3248     // private
3249     beforeFx : function(){
3250         this.beforeAction();
3251         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3252     },
3253
3254     // private
3255     afterFx : function(){
3256         Roo.Layer.superclass.afterFx.apply(this, arguments);
3257         this.sync(this.isVisible());
3258     },
3259
3260     // private
3261     beforeAction : function(){
3262         if(!this.updating && this.shadow){
3263             this.shadow.hide();
3264         }
3265     },
3266
3267     // overridden Element method
3268     setLeft : function(left){
3269         this.storeLeftTop(left, this.getTop(true));
3270         supr.setLeft.apply(this, arguments);
3271         this.sync();
3272     },
3273
3274     setTop : function(top){
3275         this.storeLeftTop(this.getLeft(true), top);
3276         supr.setTop.apply(this, arguments);
3277         this.sync();
3278     },
3279
3280     setLeftTop : function(left, top){
3281         this.storeLeftTop(left, top);
3282         supr.setLeftTop.apply(this, arguments);
3283         this.sync();
3284     },
3285
3286     setXY : function(xy, a, d, c, e){
3287         this.fixDisplay();
3288         this.beforeAction();
3289         this.storeXY(xy);
3290         var cb = this.createCB(c);
3291         supr.setXY.call(this, xy, a, d, cb, e);
3292         if(!a){
3293             cb();
3294         }
3295     },
3296
3297     // private
3298     createCB : function(c){
3299         var el = this;
3300         return function(){
3301             el.constrainXY();
3302             el.sync(true);
3303             if(c){
3304                 c();
3305             }
3306         };
3307     },
3308
3309     // overridden Element method
3310     setX : function(x, a, d, c, e){
3311         this.setXY([x, this.getY()], a, d, c, e);
3312     },
3313
3314     // overridden Element method
3315     setY : function(y, a, d, c, e){
3316         this.setXY([this.getX(), y], a, d, c, e);
3317     },
3318
3319     // overridden Element method
3320     setSize : function(w, h, a, d, c, e){
3321         this.beforeAction();
3322         var cb = this.createCB(c);
3323         supr.setSize.call(this, w, h, a, d, cb, e);
3324         if(!a){
3325             cb();
3326         }
3327     },
3328
3329     // overridden Element method
3330     setWidth : function(w, a, d, c, e){
3331         this.beforeAction();
3332         var cb = this.createCB(c);
3333         supr.setWidth.call(this, w, a, d, cb, e);
3334         if(!a){
3335             cb();
3336         }
3337     },
3338
3339     // overridden Element method
3340     setHeight : function(h, a, d, c, e){
3341         this.beforeAction();
3342         var cb = this.createCB(c);
3343         supr.setHeight.call(this, h, a, d, cb, e);
3344         if(!a){
3345             cb();
3346         }
3347     },
3348
3349     // overridden Element method
3350     setBounds : function(x, y, w, h, a, d, c, e){
3351         this.beforeAction();
3352         var cb = this.createCB(c);
3353         if(!a){
3354             this.storeXY([x, y]);
3355             supr.setXY.call(this, [x, y]);
3356             supr.setSize.call(this, w, h, a, d, cb, e);
3357             cb();
3358         }else{
3359             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3360         }
3361         return this;
3362     },
3363     
3364     /**
3365      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3366      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3367      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3368      * @param {Number} zindex The new z-index to set
3369      * @return {this} The Layer
3370      */
3371     setZIndex : function(zindex){
3372         this.zindex = zindex;
3373         this.setStyle("z-index", zindex + 2);
3374         if(this.shadow){
3375             this.shadow.setZIndex(zindex + 1);
3376         }
3377         if(this.shim){
3378             this.shim.setStyle("z-index", zindex);
3379         }
3380     }
3381 });
3382 })();/*
3383  * Based on:
3384  * Ext JS Library 1.1.1
3385  * Copyright(c) 2006-2007, Ext JS, LLC.
3386  *
3387  * Originally Released Under LGPL - original licence link has changed is not relivant.
3388  *
3389  * Fork - LGPL
3390  * <script type="text/javascript">
3391  */
3392
3393
3394 /**
3395  * @class Roo.Shadow
3396  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3397  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3398  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3399  * @constructor
3400  * Create a new Shadow
3401  * @param {Object} config The config object
3402  */
3403 Roo.Shadow = function(config){
3404     Roo.apply(this, config);
3405     if(typeof this.mode != "string"){
3406         this.mode = this.defaultMode;
3407     }
3408     var o = this.offset, a = {h: 0};
3409     var rad = Math.floor(this.offset/2);
3410     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3411         case "drop":
3412             a.w = 0;
3413             a.l = a.t = o;
3414             a.t -= 1;
3415             if(Roo.isIE){
3416                 a.l -= this.offset + rad;
3417                 a.t -= this.offset + rad;
3418                 a.w -= rad;
3419                 a.h -= rad;
3420                 a.t += 1;
3421             }
3422         break;
3423         case "sides":
3424             a.w = (o*2);
3425             a.l = -o;
3426             a.t = o-1;
3427             if(Roo.isIE){
3428                 a.l -= (this.offset - rad);
3429                 a.t -= this.offset + rad;
3430                 a.l += 1;
3431                 a.w -= (this.offset - rad)*2;
3432                 a.w -= rad + 1;
3433                 a.h -= 1;
3434             }
3435         break;
3436         case "frame":
3437             a.w = a.h = (o*2);
3438             a.l = a.t = -o;
3439             a.t += 1;
3440             a.h -= 2;
3441             if(Roo.isIE){
3442                 a.l -= (this.offset - rad);
3443                 a.t -= (this.offset - rad);
3444                 a.l += 1;
3445                 a.w -= (this.offset + rad + 1);
3446                 a.h -= (this.offset + rad);
3447                 a.h += 1;
3448             }
3449         break;
3450     };
3451
3452     this.adjusts = a;
3453 };
3454
3455 Roo.Shadow.prototype = {
3456     /**
3457      * @cfg {String} mode
3458      * The shadow display mode.  Supports the following options:<br />
3459      * sides: Shadow displays on both sides and bottom only<br />
3460      * frame: Shadow displays equally on all four sides<br />
3461      * drop: Traditional bottom-right drop shadow (default)
3462      */
3463     /**
3464      * @cfg {String} offset
3465      * The number of pixels to offset the shadow from the element (defaults to 4)
3466      */
3467     offset: 4,
3468
3469     // private
3470     defaultMode: "drop",
3471
3472     /**
3473      * Displays the shadow under the target element
3474      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3475      */
3476     show : function(target){
3477         target = Roo.get(target);
3478         if(!this.el){
3479             this.el = Roo.Shadow.Pool.pull();
3480             if(this.el.dom.nextSibling != target.dom){
3481                 this.el.insertBefore(target);
3482             }
3483         }
3484         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3485         if(Roo.isIE){
3486             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3487         }
3488         this.realign(
3489             target.getLeft(true),
3490             target.getTop(true),
3491             target.getWidth(),
3492             target.getHeight()
3493         );
3494         this.el.dom.style.display = "block";
3495     },
3496
3497     /**
3498      * Returns true if the shadow is visible, else false
3499      */
3500     isVisible : function(){
3501         return this.el ? true : false;  
3502     },
3503
3504     /**
3505      * Direct alignment when values are already available. Show must be called at least once before
3506      * calling this method to ensure it is initialized.
3507      * @param {Number} left The target element left position
3508      * @param {Number} top The target element top position
3509      * @param {Number} width The target element width
3510      * @param {Number} height The target element height
3511      */
3512     realign : function(l, t, w, h){
3513         if(!this.el){
3514             return;
3515         }
3516         var a = this.adjusts, d = this.el.dom, s = d.style;
3517         var iea = 0;
3518         s.left = (l+a.l)+"px";
3519         s.top = (t+a.t)+"px";
3520         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3521  
3522         if(s.width != sws || s.height != shs){
3523             s.width = sws;
3524             s.height = shs;
3525             if(!Roo.isIE){
3526                 var cn = d.childNodes;
3527                 var sww = Math.max(0, (sw-12))+"px";
3528                 cn[0].childNodes[1].style.width = sww;
3529                 cn[1].childNodes[1].style.width = sww;
3530                 cn[2].childNodes[1].style.width = sww;
3531                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3532             }
3533         }
3534     },
3535
3536     /**
3537      * Hides this shadow
3538      */
3539     hide : function(){
3540         if(this.el){
3541             this.el.dom.style.display = "none";
3542             Roo.Shadow.Pool.push(this.el);
3543             delete this.el;
3544         }
3545     },
3546
3547     /**
3548      * Adjust the z-index of this shadow
3549      * @param {Number} zindex The new z-index
3550      */
3551     setZIndex : function(z){
3552         this.zIndex = z;
3553         if(this.el){
3554             this.el.setStyle("z-index", z);
3555         }
3556     }
3557 };
3558
3559 // Private utility class that manages the internal Shadow cache
3560 Roo.Shadow.Pool = function(){
3561     var p = [];
3562     var markup = Roo.isIE ?
3563                  '<div class="x-ie-shadow"></div>' :
3564                  '<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>';
3565     return {
3566         pull : function(){
3567             var sh = p.shift();
3568             if(!sh){
3569                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3570                 sh.autoBoxAdjust = false;
3571             }
3572             return sh;
3573         },
3574
3575         push : function(sh){
3576             p.push(sh);
3577         }
3578     };
3579 }();/*
3580  * Based on:
3581  * Ext JS Library 1.1.1
3582  * Copyright(c) 2006-2007, Ext JS, LLC.
3583  *
3584  * Originally Released Under LGPL - original licence link has changed is not relivant.
3585  *
3586  * Fork - LGPL
3587  * <script type="text/javascript">
3588  */
3589
3590
3591 /**
3592  * @class Roo.SplitBar
3593  * @extends Roo.util.Observable
3594  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3595  * <br><br>
3596  * Usage:
3597  * <pre><code>
3598 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3599                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3600 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3601 split.minSize = 100;
3602 split.maxSize = 600;
3603 split.animate = true;
3604 split.on('moved', splitterMoved);
3605 </code></pre>
3606  * @constructor
3607  * Create a new SplitBar
3608  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3609  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3610  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3611  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3612                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3613                         position of the SplitBar).
3614  */
3615 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3616     
3617     /** @private */
3618     this.el = Roo.get(dragElement, true);
3619     this.el.dom.unselectable = "on";
3620     /** @private */
3621     this.resizingEl = Roo.get(resizingElement, true);
3622
3623     /**
3624      * @private
3625      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3626      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3627      * @type Number
3628      */
3629     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3630     
3631     /**
3632      * The minimum size of the resizing element. (Defaults to 0)
3633      * @type Number
3634      */
3635     this.minSize = 0;
3636     
3637     /**
3638      * The maximum size of the resizing element. (Defaults to 2000)
3639      * @type Number
3640      */
3641     this.maxSize = 2000;
3642     
3643     /**
3644      * Whether to animate the transition to the new size
3645      * @type Boolean
3646      */
3647     this.animate = false;
3648     
3649     /**
3650      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3651      * @type Boolean
3652      */
3653     this.useShim = false;
3654     
3655     /** @private */
3656     this.shim = null;
3657     
3658     if(!existingProxy){
3659         /** @private */
3660         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3661     }else{
3662         this.proxy = Roo.get(existingProxy).dom;
3663     }
3664     /** @private */
3665     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3666     
3667     /** @private */
3668     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3669     
3670     /** @private */
3671     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3672     
3673     /** @private */
3674     this.dragSpecs = {};
3675     
3676     /**
3677      * @private The adapter to use to positon and resize elements
3678      */
3679     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3680     this.adapter.init(this);
3681     
3682     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3683         /** @private */
3684         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3685         this.el.addClass("x-splitbar-h");
3686     }else{
3687         /** @private */
3688         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3689         this.el.addClass("x-splitbar-v");
3690     }
3691     
3692     this.addEvents({
3693         /**
3694          * @event resize
3695          * Fires when the splitter is moved (alias for {@link #event-moved})
3696          * @param {Roo.SplitBar} this
3697          * @param {Number} newSize the new width or height
3698          */
3699         "resize" : true,
3700         /**
3701          * @event moved
3702          * Fires when the splitter is moved
3703          * @param {Roo.SplitBar} this
3704          * @param {Number} newSize the new width or height
3705          */
3706         "moved" : true,
3707         /**
3708          * @event beforeresize
3709          * Fires before the splitter is dragged
3710          * @param {Roo.SplitBar} this
3711          */
3712         "beforeresize" : true,
3713
3714         "beforeapply" : true
3715     });
3716
3717     Roo.util.Observable.call(this);
3718 };
3719
3720 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3721     onStartProxyDrag : function(x, y){
3722         this.fireEvent("beforeresize", this);
3723         if(!this.overlay){
3724             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3725             o.unselectable();
3726             o.enableDisplayMode("block");
3727             // all splitbars share the same overlay
3728             Roo.SplitBar.prototype.overlay = o;
3729         }
3730         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3731         this.overlay.show();
3732         Roo.get(this.proxy).setDisplayed("block");
3733         var size = this.adapter.getElementSize(this);
3734         this.activeMinSize = this.getMinimumSize();;
3735         this.activeMaxSize = this.getMaximumSize();;
3736         var c1 = size - this.activeMinSize;
3737         var c2 = Math.max(this.activeMaxSize - size, 0);
3738         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3739             this.dd.resetConstraints();
3740             this.dd.setXConstraint(
3741                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3742                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3743             );
3744             this.dd.setYConstraint(0, 0);
3745         }else{
3746             this.dd.resetConstraints();
3747             this.dd.setXConstraint(0, 0);
3748             this.dd.setYConstraint(
3749                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3750                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3751             );
3752          }
3753         this.dragSpecs.startSize = size;
3754         this.dragSpecs.startPoint = [x, y];
3755         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3756     },
3757     
3758     /** 
3759      * @private Called after the drag operation by the DDProxy
3760      */
3761     onEndProxyDrag : function(e){
3762         Roo.get(this.proxy).setDisplayed(false);
3763         var endPoint = Roo.lib.Event.getXY(e);
3764         if(this.overlay){
3765             this.overlay.hide();
3766         }
3767         var newSize;
3768         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3769             newSize = this.dragSpecs.startSize + 
3770                 (this.placement == Roo.SplitBar.LEFT ?
3771                     endPoint[0] - this.dragSpecs.startPoint[0] :
3772                     this.dragSpecs.startPoint[0] - endPoint[0]
3773                 );
3774         }else{
3775             newSize = this.dragSpecs.startSize + 
3776                 (this.placement == Roo.SplitBar.TOP ?
3777                     endPoint[1] - this.dragSpecs.startPoint[1] :
3778                     this.dragSpecs.startPoint[1] - endPoint[1]
3779                 );
3780         }
3781         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3782         if(newSize != this.dragSpecs.startSize){
3783             if(this.fireEvent('beforeapply', this, newSize) !== false){
3784                 this.adapter.setElementSize(this, newSize);
3785                 this.fireEvent("moved", this, newSize);
3786                 this.fireEvent("resize", this, newSize);
3787             }
3788         }
3789     },
3790     
3791     /**
3792      * Get the adapter this SplitBar uses
3793      * @return The adapter object
3794      */
3795     getAdapter : function(){
3796         return this.adapter;
3797     },
3798     
3799     /**
3800      * Set the adapter this SplitBar uses
3801      * @param {Object} adapter A SplitBar adapter object
3802      */
3803     setAdapter : function(adapter){
3804         this.adapter = adapter;
3805         this.adapter.init(this);
3806     },
3807     
3808     /**
3809      * Gets the minimum size for the resizing element
3810      * @return {Number} The minimum size
3811      */
3812     getMinimumSize : function(){
3813         return this.minSize;
3814     },
3815     
3816     /**
3817      * Sets the minimum size for the resizing element
3818      * @param {Number} minSize The minimum size
3819      */
3820     setMinimumSize : function(minSize){
3821         this.minSize = minSize;
3822     },
3823     
3824     /**
3825      * Gets the maximum size for the resizing element
3826      * @return {Number} The maximum size
3827      */
3828     getMaximumSize : function(){
3829         return this.maxSize;
3830     },
3831     
3832     /**
3833      * Sets the maximum size for the resizing element
3834      * @param {Number} maxSize The maximum size
3835      */
3836     setMaximumSize : function(maxSize){
3837         this.maxSize = maxSize;
3838     },
3839     
3840     /**
3841      * Sets the initialize size for the resizing element
3842      * @param {Number} size The initial size
3843      */
3844     setCurrentSize : function(size){
3845         var oldAnimate = this.animate;
3846         this.animate = false;
3847         this.adapter.setElementSize(this, size);
3848         this.animate = oldAnimate;
3849     },
3850     
3851     /**
3852      * Destroy this splitbar. 
3853      * @param {Boolean} removeEl True to remove the element
3854      */
3855     destroy : function(removeEl){
3856         if(this.shim){
3857             this.shim.remove();
3858         }
3859         this.dd.unreg();
3860         this.proxy.parentNode.removeChild(this.proxy);
3861         if(removeEl){
3862             this.el.remove();
3863         }
3864     }
3865 });
3866
3867 /**
3868  * @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.
3869  */
3870 Roo.SplitBar.createProxy = function(dir){
3871     var proxy = new Roo.Element(document.createElement("div"));
3872     proxy.unselectable();
3873     var cls = 'x-splitbar-proxy';
3874     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3875     document.body.appendChild(proxy.dom);
3876     return proxy.dom;
3877 };
3878
3879 /** 
3880  * @class Roo.SplitBar.BasicLayoutAdapter
3881  * Default Adapter. It assumes the splitter and resizing element are not positioned
3882  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3883  */
3884 Roo.SplitBar.BasicLayoutAdapter = function(){
3885 };
3886
3887 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3888     // do nothing for now
3889     init : function(s){
3890     
3891     },
3892     /**
3893      * Called before drag operations to get the current size of the resizing element. 
3894      * @param {Roo.SplitBar} s The SplitBar using this adapter
3895      */
3896      getElementSize : function(s){
3897         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3898             return s.resizingEl.getWidth();
3899         }else{
3900             return s.resizingEl.getHeight();
3901         }
3902     },
3903     
3904     /**
3905      * Called after drag operations to set the size of the resizing element.
3906      * @param {Roo.SplitBar} s The SplitBar using this adapter
3907      * @param {Number} newSize The new size to set
3908      * @param {Function} onComplete A function to be invoked when resizing is complete
3909      */
3910     setElementSize : function(s, newSize, onComplete){
3911         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3912             if(!s.animate){
3913                 s.resizingEl.setWidth(newSize);
3914                 if(onComplete){
3915                     onComplete(s, newSize);
3916                 }
3917             }else{
3918                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3919             }
3920         }else{
3921             
3922             if(!s.animate){
3923                 s.resizingEl.setHeight(newSize);
3924                 if(onComplete){
3925                     onComplete(s, newSize);
3926                 }
3927             }else{
3928                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3929             }
3930         }
3931     }
3932 };
3933
3934 /** 
3935  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3936  * @extends Roo.SplitBar.BasicLayoutAdapter
3937  * Adapter that  moves the splitter element to align with the resized sizing element. 
3938  * Used with an absolute positioned SplitBar.
3939  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3940  * document.body, make sure you assign an id to the body element.
3941  */
3942 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3943     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3944     this.container = Roo.get(container);
3945 };
3946
3947 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3948     init : function(s){
3949         this.basic.init(s);
3950     },
3951     
3952     getElementSize : function(s){
3953         return this.basic.getElementSize(s);
3954     },
3955     
3956     setElementSize : function(s, newSize, onComplete){
3957         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3958     },
3959     
3960     moveSplitter : function(s){
3961         var yes = Roo.SplitBar;
3962         switch(s.placement){
3963             case yes.LEFT:
3964                 s.el.setX(s.resizingEl.getRight());
3965                 break;
3966             case yes.RIGHT:
3967                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3968                 break;
3969             case yes.TOP:
3970                 s.el.setY(s.resizingEl.getBottom());
3971                 break;
3972             case yes.BOTTOM:
3973                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3974                 break;
3975         }
3976     }
3977 };
3978
3979 /**
3980  * Orientation constant - Create a vertical SplitBar
3981  * @static
3982  * @type Number
3983  */
3984 Roo.SplitBar.VERTICAL = 1;
3985
3986 /**
3987  * Orientation constant - Create a horizontal SplitBar
3988  * @static
3989  * @type Number
3990  */
3991 Roo.SplitBar.HORIZONTAL = 2;
3992
3993 /**
3994  * Placement constant - The resizing element is to the left of the splitter element
3995  * @static
3996  * @type Number
3997  */
3998 Roo.SplitBar.LEFT = 1;
3999
4000 /**
4001  * Placement constant - The resizing element is to the right of the splitter element
4002  * @static
4003  * @type Number
4004  */
4005 Roo.SplitBar.RIGHT = 2;
4006
4007 /**
4008  * Placement constant - The resizing element is positioned above the splitter element
4009  * @static
4010  * @type Number
4011  */
4012 Roo.SplitBar.TOP = 3;
4013
4014 /**
4015  * Placement constant - The resizing element is positioned under splitter element
4016  * @static
4017  * @type Number
4018  */
4019 Roo.SplitBar.BOTTOM = 4;
4020 /*
4021  * Based on:
4022  * Ext JS Library 1.1.1
4023  * Copyright(c) 2006-2007, Ext JS, LLC.
4024  *
4025  * Originally Released Under LGPL - original licence link has changed is not relivant.
4026  *
4027  * Fork - LGPL
4028  * <script type="text/javascript">
4029  */
4030
4031 /**
4032  * @class Roo.View
4033  * @extends Roo.util.Observable
4034  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4035  * This class also supports single and multi selection modes. <br>
4036  * Create a data model bound view:
4037  <pre><code>
4038  var store = new Roo.data.Store(...);
4039
4040  var view = new Roo.View({
4041     el : "my-element",
4042     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4043  
4044     singleSelect: true,
4045     selectedClass: "ydataview-selected",
4046     store: store
4047  });
4048
4049  // listen for node click?
4050  view.on("click", function(vw, index, node, e){
4051  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4052  });
4053
4054  // load XML data
4055  dataModel.load("foobar.xml");
4056  </code></pre>
4057  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4058  * <br><br>
4059  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4060  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4061  * 
4062  * Note: old style constructor is still suported (container, template, config)
4063  * 
4064  * @constructor
4065  * Create a new View
4066  * @param {Object} config The config object
4067  * 
4068  */
4069 Roo.View = function(config, depreciated_tpl, depreciated_config){
4070     
4071     this.parent = false;
4072     
4073     if (typeof(depreciated_tpl) == 'undefined') {
4074         // new way.. - universal constructor.
4075         Roo.apply(this, config);
4076         this.el  = Roo.get(this.el);
4077     } else {
4078         // old format..
4079         this.el  = Roo.get(config);
4080         this.tpl = depreciated_tpl;
4081         Roo.apply(this, depreciated_config);
4082     }
4083     this.wrapEl  = this.el.wrap().wrap();
4084     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4085     
4086     
4087     if(typeof(this.tpl) == "string"){
4088         this.tpl = new Roo.Template(this.tpl);
4089     } else {
4090         // support xtype ctors..
4091         this.tpl = new Roo.factory(this.tpl, Roo);
4092     }
4093     
4094     
4095     this.tpl.compile();
4096     
4097     /** @private */
4098     this.addEvents({
4099         /**
4100          * @event beforeclick
4101          * Fires before a click is processed. Returns false to cancel the default action.
4102          * @param {Roo.View} this
4103          * @param {Number} index The index of the target node
4104          * @param {HTMLElement} node The target node
4105          * @param {Roo.EventObject} e The raw event object
4106          */
4107             "beforeclick" : true,
4108         /**
4109          * @event click
4110          * Fires when a template node is clicked.
4111          * @param {Roo.View} this
4112          * @param {Number} index The index of the target node
4113          * @param {HTMLElement} node The target node
4114          * @param {Roo.EventObject} e The raw event object
4115          */
4116             "click" : true,
4117         /**
4118          * @event dblclick
4119          * Fires when a template node is double clicked.
4120          * @param {Roo.View} this
4121          * @param {Number} index The index of the target node
4122          * @param {HTMLElement} node The target node
4123          * @param {Roo.EventObject} e The raw event object
4124          */
4125             "dblclick" : true,
4126         /**
4127          * @event contextmenu
4128          * Fires when a template node is right clicked.
4129          * @param {Roo.View} this
4130          * @param {Number} index The index of the target node
4131          * @param {HTMLElement} node The target node
4132          * @param {Roo.EventObject} e The raw event object
4133          */
4134             "contextmenu" : true,
4135         /**
4136          * @event selectionchange
4137          * Fires when the selected nodes change.
4138          * @param {Roo.View} this
4139          * @param {Array} selections Array of the selected nodes
4140          */
4141             "selectionchange" : true,
4142     
4143         /**
4144          * @event beforeselect
4145          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4146          * @param {Roo.View} this
4147          * @param {HTMLElement} node The node to be selected
4148          * @param {Array} selections Array of currently selected nodes
4149          */
4150             "beforeselect" : true,
4151         /**
4152          * @event preparedata
4153          * Fires on every row to render, to allow you to change the data.
4154          * @param {Roo.View} this
4155          * @param {Object} data to be rendered (change this)
4156          */
4157           "preparedata" : true
4158           
4159           
4160         });
4161
4162
4163
4164     this.el.on({
4165         "click": this.onClick,
4166         "dblclick": this.onDblClick,
4167         "contextmenu": this.onContextMenu,
4168         scope:this
4169     });
4170
4171     this.selections = [];
4172     this.nodes = [];
4173     this.cmp = new Roo.CompositeElementLite([]);
4174     if(this.store){
4175         this.store = Roo.factory(this.store, Roo.data);
4176         this.setStore(this.store, true);
4177     }
4178     
4179     if ( this.footer && this.footer.xtype) {
4180            
4181          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4182         
4183         this.footer.dataSource = this.store;
4184         this.footer.container = fctr;
4185         this.footer = Roo.factory(this.footer, Roo);
4186         fctr.insertFirst(this.el);
4187         
4188         // this is a bit insane - as the paging toolbar seems to detach the el..
4189 //        dom.parentNode.parentNode.parentNode
4190          // they get detached?
4191     }
4192     
4193     
4194     Roo.View.superclass.constructor.call(this);
4195     
4196     
4197 };
4198
4199 Roo.extend(Roo.View, Roo.util.Observable, {
4200     
4201      /**
4202      * @cfg {Roo.data.Store} store Data store to load data from.
4203      */
4204     store : false,
4205     
4206     /**
4207      * @cfg {String|Roo.Element} el The container element.
4208      */
4209     el : '',
4210     
4211     /**
4212      * @cfg {String|Roo.Template} tpl The template used by this View 
4213      */
4214     tpl : false,
4215     /**
4216      * @cfg {String} dataName the named area of the template to use as the data area
4217      *                          Works with domtemplates roo-name="name"
4218      */
4219     dataName: false,
4220     /**
4221      * @cfg {String} selectedClass The css class to add to selected nodes
4222      */
4223     selectedClass : "x-view-selected",
4224      /**
4225      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4226      */
4227     emptyText : "",
4228     
4229     /**
4230      * @cfg {String} text to display on mask (default Loading)
4231      */
4232     mask : false,
4233     /**
4234      * @cfg {Boolean} multiSelect Allow multiple selection
4235      */
4236     multiSelect : false,
4237     /**
4238      * @cfg {Boolean} singleSelect Allow single selection
4239      */
4240     singleSelect:  false,
4241     
4242     /**
4243      * @cfg {Boolean} toggleSelect - selecting 
4244      */
4245     toggleSelect : false,
4246     
4247     /**
4248      * @cfg {Boolean} tickable - selecting 
4249      */
4250     tickable : false,
4251     
4252     /**
4253      * Returns the element this view is bound to.
4254      * @return {Roo.Element}
4255      */
4256     getEl : function(){
4257         return this.wrapEl;
4258     },
4259     
4260     
4261
4262     /**
4263      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4264      */
4265     refresh : function(){
4266         //Roo.log('refresh');
4267         var t = this.tpl;
4268         
4269         // if we are using something like 'domtemplate', then
4270         // the what gets used is:
4271         // t.applySubtemplate(NAME, data, wrapping data..)
4272         // the outer template then get' applied with
4273         //     the store 'extra data'
4274         // and the body get's added to the
4275         //      roo-name="data" node?
4276         //      <span class='roo-tpl-{name}'></span> ?????
4277         
4278         
4279         
4280         this.clearSelections();
4281         this.el.update("");
4282         var html = [];
4283         var records = this.store.getRange();
4284         if(records.length < 1) {
4285             
4286             // is this valid??  = should it render a template??
4287             
4288             this.el.update(this.emptyText);
4289             return;
4290         }
4291         var el = this.el;
4292         if (this.dataName) {
4293             this.el.update(t.apply(this.store.meta)); //????
4294             el = this.el.child('.roo-tpl-' + this.dataName);
4295         }
4296         
4297         for(var i = 0, len = records.length; i < len; i++){
4298             var data = this.prepareData(records[i].data, i, records[i]);
4299             this.fireEvent("preparedata", this, data, i, records[i]);
4300             
4301             var d = Roo.apply({}, data);
4302             
4303             if(this.tickable){
4304                 Roo.apply(d, {'roo-id' : Roo.id()});
4305                 
4306                 var _this = this;
4307             
4308                 Roo.each(this.parent.item, function(item){
4309                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4310                         return;
4311                     }
4312                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4313                 });
4314             }
4315             
4316             html[html.length] = Roo.util.Format.trim(
4317                 this.dataName ?
4318                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4319                     t.apply(d)
4320             );
4321         }
4322         
4323         
4324         
4325         el.update(html.join(""));
4326         this.nodes = el.dom.childNodes;
4327         this.updateIndexes(0);
4328     },
4329     
4330
4331     /**
4332      * Function to override to reformat the data that is sent to
4333      * the template for each node.
4334      * DEPRICATED - use the preparedata event handler.
4335      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4336      * a JSON object for an UpdateManager bound view).
4337      */
4338     prepareData : function(data, index, record)
4339     {
4340         this.fireEvent("preparedata", this, data, index, record);
4341         return data;
4342     },
4343
4344     onUpdate : function(ds, record){
4345         // Roo.log('on update');   
4346         this.clearSelections();
4347         var index = this.store.indexOf(record);
4348         var n = this.nodes[index];
4349         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4350         n.parentNode.removeChild(n);
4351         this.updateIndexes(index, index);
4352     },
4353
4354     
4355     
4356 // --------- FIXME     
4357     onAdd : function(ds, records, index)
4358     {
4359         //Roo.log(['on Add', ds, records, index] );        
4360         this.clearSelections();
4361         if(this.nodes.length == 0){
4362             this.refresh();
4363             return;
4364         }
4365         var n = this.nodes[index];
4366         for(var i = 0, len = records.length; i < len; i++){
4367             var d = this.prepareData(records[i].data, i, records[i]);
4368             if(n){
4369                 this.tpl.insertBefore(n, d);
4370             }else{
4371                 
4372                 this.tpl.append(this.el, d);
4373             }
4374         }
4375         this.updateIndexes(index);
4376     },
4377
4378     onRemove : function(ds, record, index){
4379        // Roo.log('onRemove');
4380         this.clearSelections();
4381         var el = this.dataName  ?
4382             this.el.child('.roo-tpl-' + this.dataName) :
4383             this.el; 
4384         
4385         el.dom.removeChild(this.nodes[index]);
4386         this.updateIndexes(index);
4387     },
4388
4389     /**
4390      * Refresh an individual node.
4391      * @param {Number} index
4392      */
4393     refreshNode : function(index){
4394         this.onUpdate(this.store, this.store.getAt(index));
4395     },
4396
4397     updateIndexes : function(startIndex, endIndex){
4398         var ns = this.nodes;
4399         startIndex = startIndex || 0;
4400         endIndex = endIndex || ns.length - 1;
4401         for(var i = startIndex; i <= endIndex; i++){
4402             ns[i].nodeIndex = i;
4403         }
4404     },
4405
4406     /**
4407      * Changes the data store this view uses and refresh the view.
4408      * @param {Store} store
4409      */
4410     setStore : function(store, initial){
4411         if(!initial && this.store){
4412             this.store.un("datachanged", this.refresh);
4413             this.store.un("add", this.onAdd);
4414             this.store.un("remove", this.onRemove);
4415             this.store.un("update", this.onUpdate);
4416             this.store.un("clear", this.refresh);
4417             this.store.un("beforeload", this.onBeforeLoad);
4418             this.store.un("load", this.onLoad);
4419             this.store.un("loadexception", this.onLoad);
4420         }
4421         if(store){
4422           
4423             store.on("datachanged", this.refresh, this);
4424             store.on("add", this.onAdd, this);
4425             store.on("remove", this.onRemove, this);
4426             store.on("update", this.onUpdate, this);
4427             store.on("clear", this.refresh, this);
4428             store.on("beforeload", this.onBeforeLoad, this);
4429             store.on("load", this.onLoad, this);
4430             store.on("loadexception", this.onLoad, this);
4431         }
4432         
4433         if(store){
4434             this.refresh();
4435         }
4436     },
4437     /**
4438      * onbeforeLoad - masks the loading area.
4439      *
4440      */
4441     onBeforeLoad : function(store,opts)
4442     {
4443          //Roo.log('onBeforeLoad');   
4444         if (!opts.add) {
4445             this.el.update("");
4446         }
4447         this.el.mask(this.mask ? this.mask : "Loading" ); 
4448     },
4449     onLoad : function ()
4450     {
4451         this.el.unmask();
4452     },
4453     
4454
4455     /**
4456      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4457      * @param {HTMLElement} node
4458      * @return {HTMLElement} The template node
4459      */
4460     findItemFromChild : function(node){
4461         var el = this.dataName  ?
4462             this.el.child('.roo-tpl-' + this.dataName,true) :
4463             this.el.dom; 
4464         
4465         if(!node || node.parentNode == el){
4466                     return node;
4467             }
4468             var p = node.parentNode;
4469             while(p && p != el){
4470             if(p.parentNode == el){
4471                 return p;
4472             }
4473             p = p.parentNode;
4474         }
4475             return null;
4476     },
4477
4478     /** @ignore */
4479     onClick : function(e){
4480         var item = this.findItemFromChild(e.getTarget());
4481         if(item){
4482             var index = this.indexOf(item);
4483             if(this.onItemClick(item, index, e) !== false){
4484                 this.fireEvent("click", this, index, item, e);
4485             }
4486         }else{
4487             this.clearSelections();
4488         }
4489     },
4490
4491     /** @ignore */
4492     onContextMenu : function(e){
4493         var item = this.findItemFromChild(e.getTarget());
4494         if(item){
4495             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4496         }
4497     },
4498
4499     /** @ignore */
4500     onDblClick : function(e){
4501         var item = this.findItemFromChild(e.getTarget());
4502         if(item){
4503             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4504         }
4505     },
4506
4507     onItemClick : function(item, index, e)
4508     {
4509         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4510             return false;
4511         }
4512         if (this.toggleSelect) {
4513             var m = this.isSelected(item) ? 'unselect' : 'select';
4514             //Roo.log(m);
4515             var _t = this;
4516             _t[m](item, true, false);
4517             return true;
4518         }
4519         if(this.multiSelect || this.singleSelect){
4520             if(this.multiSelect && e.shiftKey && this.lastSelection){
4521                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4522             }else{
4523                 this.select(item, this.multiSelect && e.ctrlKey);
4524                 this.lastSelection = item;
4525             }
4526             
4527             if(!this.tickable){
4528                 e.preventDefault();
4529             }
4530             
4531         }
4532         return true;
4533     },
4534
4535     /**
4536      * Get the number of selected nodes.
4537      * @return {Number}
4538      */
4539     getSelectionCount : function(){
4540         return this.selections.length;
4541     },
4542
4543     /**
4544      * Get the currently selected nodes.
4545      * @return {Array} An array of HTMLElements
4546      */
4547     getSelectedNodes : function(){
4548         return this.selections;
4549     },
4550
4551     /**
4552      * Get the indexes of the selected nodes.
4553      * @return {Array}
4554      */
4555     getSelectedIndexes : function(){
4556         var indexes = [], s = this.selections;
4557         for(var i = 0, len = s.length; i < len; i++){
4558             indexes.push(s[i].nodeIndex);
4559         }
4560         return indexes;
4561     },
4562
4563     /**
4564      * Clear all selections
4565      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4566      */
4567     clearSelections : function(suppressEvent){
4568         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4569             this.cmp.elements = this.selections;
4570             this.cmp.removeClass(this.selectedClass);
4571             this.selections = [];
4572             if(!suppressEvent){
4573                 this.fireEvent("selectionchange", this, this.selections);
4574             }
4575         }
4576     },
4577
4578     /**
4579      * Returns true if the passed node is selected
4580      * @param {HTMLElement/Number} node The node or node index
4581      * @return {Boolean}
4582      */
4583     isSelected : function(node){
4584         var s = this.selections;
4585         if(s.length < 1){
4586             return false;
4587         }
4588         node = this.getNode(node);
4589         return s.indexOf(node) !== -1;
4590     },
4591
4592     /**
4593      * Selects nodes.
4594      * @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
4595      * @param {Boolean} keepExisting (optional) true to keep existing selections
4596      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4597      */
4598     select : function(nodeInfo, keepExisting, suppressEvent){
4599         if(nodeInfo instanceof Array){
4600             if(!keepExisting){
4601                 this.clearSelections(true);
4602             }
4603             for(var i = 0, len = nodeInfo.length; i < len; i++){
4604                 this.select(nodeInfo[i], true, true);
4605             }
4606             return;
4607         } 
4608         var node = this.getNode(nodeInfo);
4609         if(!node || this.isSelected(node)){
4610             return; // already selected.
4611         }
4612         if(!keepExisting){
4613             this.clearSelections(true);
4614         }
4615         
4616         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4617             Roo.fly(node).addClass(this.selectedClass);
4618             this.selections.push(node);
4619             if(!suppressEvent){
4620                 this.fireEvent("selectionchange", this, this.selections);
4621             }
4622         }
4623         
4624         
4625     },
4626       /**
4627      * Unselects nodes.
4628      * @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
4629      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4630      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4631      */
4632     unselect : function(nodeInfo, keepExisting, suppressEvent)
4633     {
4634         if(nodeInfo instanceof Array){
4635             Roo.each(this.selections, function(s) {
4636                 this.unselect(s, nodeInfo);
4637             }, this);
4638             return;
4639         }
4640         var node = this.getNode(nodeInfo);
4641         if(!node || !this.isSelected(node)){
4642             //Roo.log("not selected");
4643             return; // not selected.
4644         }
4645         // fireevent???
4646         var ns = [];
4647         Roo.each(this.selections, function(s) {
4648             if (s == node ) {
4649                 Roo.fly(node).removeClass(this.selectedClass);
4650
4651                 return;
4652             }
4653             ns.push(s);
4654         },this);
4655         
4656         this.selections= ns;
4657         this.fireEvent("selectionchange", this, this.selections);
4658     },
4659
4660     /**
4661      * Gets a template node.
4662      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4663      * @return {HTMLElement} The node or null if it wasn't found
4664      */
4665     getNode : function(nodeInfo){
4666         if(typeof nodeInfo == "string"){
4667             return document.getElementById(nodeInfo);
4668         }else if(typeof nodeInfo == "number"){
4669             return this.nodes[nodeInfo];
4670         }
4671         return nodeInfo;
4672     },
4673
4674     /**
4675      * Gets a range template nodes.
4676      * @param {Number} startIndex
4677      * @param {Number} endIndex
4678      * @return {Array} An array of nodes
4679      */
4680     getNodes : function(start, end){
4681         var ns = this.nodes;
4682         start = start || 0;
4683         end = typeof end == "undefined" ? ns.length - 1 : end;
4684         var nodes = [];
4685         if(start <= end){
4686             for(var i = start; i <= end; i++){
4687                 nodes.push(ns[i]);
4688             }
4689         } else{
4690             for(var i = start; i >= end; i--){
4691                 nodes.push(ns[i]);
4692             }
4693         }
4694         return nodes;
4695     },
4696
4697     /**
4698      * Finds the index of the passed node
4699      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4700      * @return {Number} The index of the node or -1
4701      */
4702     indexOf : function(node){
4703         node = this.getNode(node);
4704         if(typeof node.nodeIndex == "number"){
4705             return node.nodeIndex;
4706         }
4707         var ns = this.nodes;
4708         for(var i = 0, len = ns.length; i < len; i++){
4709             if(ns[i] == node){
4710                 return i;
4711             }
4712         }
4713         return -1;
4714     }
4715 });
4716 /*
4717  * Based on:
4718  * Ext JS Library 1.1.1
4719  * Copyright(c) 2006-2007, Ext JS, LLC.
4720  *
4721  * Originally Released Under LGPL - original licence link has changed is not relivant.
4722  *
4723  * Fork - LGPL
4724  * <script type="text/javascript">
4725  */
4726
4727 /**
4728  * @class Roo.JsonView
4729  * @extends Roo.View
4730  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4731 <pre><code>
4732 var view = new Roo.JsonView({
4733     container: "my-element",
4734     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4735     multiSelect: true, 
4736     jsonRoot: "data" 
4737 });
4738
4739 // listen for node click?
4740 view.on("click", function(vw, index, node, e){
4741     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4742 });
4743
4744 // direct load of JSON data
4745 view.load("foobar.php");
4746
4747 // Example from my blog list
4748 var tpl = new Roo.Template(
4749     '&lt;div class="entry"&gt;' +
4750     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4751     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4752     "&lt;/div&gt;&lt;hr /&gt;"
4753 );
4754
4755 var moreView = new Roo.JsonView({
4756     container :  "entry-list", 
4757     template : tpl,
4758     jsonRoot: "posts"
4759 });
4760 moreView.on("beforerender", this.sortEntries, this);
4761 moreView.load({
4762     url: "/blog/get-posts.php",
4763     params: "allposts=true",
4764     text: "Loading Blog Entries..."
4765 });
4766 </code></pre>
4767
4768 * Note: old code is supported with arguments : (container, template, config)
4769
4770
4771  * @constructor
4772  * Create a new JsonView
4773  * 
4774  * @param {Object} config The config object
4775  * 
4776  */
4777 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4778     
4779     
4780     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4781
4782     var um = this.el.getUpdateManager();
4783     um.setRenderer(this);
4784     um.on("update", this.onLoad, this);
4785     um.on("failure", this.onLoadException, this);
4786
4787     /**
4788      * @event beforerender
4789      * Fires before rendering of the downloaded JSON data.
4790      * @param {Roo.JsonView} this
4791      * @param {Object} data The JSON data loaded
4792      */
4793     /**
4794      * @event load
4795      * Fires when data is loaded.
4796      * @param {Roo.JsonView} this
4797      * @param {Object} data The JSON data loaded
4798      * @param {Object} response The raw Connect response object
4799      */
4800     /**
4801      * @event loadexception
4802      * Fires when loading fails.
4803      * @param {Roo.JsonView} this
4804      * @param {Object} response The raw Connect response object
4805      */
4806     this.addEvents({
4807         'beforerender' : true,
4808         'load' : true,
4809         'loadexception' : true
4810     });
4811 };
4812 Roo.extend(Roo.JsonView, Roo.View, {
4813     /**
4814      * @type {String} The root property in the loaded JSON object that contains the data
4815      */
4816     jsonRoot : "",
4817
4818     /**
4819      * Refreshes the view.
4820      */
4821     refresh : function(){
4822         this.clearSelections();
4823         this.el.update("");
4824         var html = [];
4825         var o = this.jsonData;
4826         if(o && o.length > 0){
4827             for(var i = 0, len = o.length; i < len; i++){
4828                 var data = this.prepareData(o[i], i, o);
4829                 html[html.length] = this.tpl.apply(data);
4830             }
4831         }else{
4832             html.push(this.emptyText);
4833         }
4834         this.el.update(html.join(""));
4835         this.nodes = this.el.dom.childNodes;
4836         this.updateIndexes(0);
4837     },
4838
4839     /**
4840      * 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.
4841      * @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:
4842      <pre><code>
4843      view.load({
4844          url: "your-url.php",
4845          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4846          callback: yourFunction,
4847          scope: yourObject, //(optional scope)
4848          discardUrl: false,
4849          nocache: false,
4850          text: "Loading...",
4851          timeout: 30,
4852          scripts: false
4853      });
4854      </code></pre>
4855      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4856      * 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.
4857      * @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}
4858      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4859      * @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.
4860      */
4861     load : function(){
4862         var um = this.el.getUpdateManager();
4863         um.update.apply(um, arguments);
4864     },
4865
4866     // note - render is a standard framework call...
4867     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4868     render : function(el, response){
4869         
4870         this.clearSelections();
4871         this.el.update("");
4872         var o;
4873         try{
4874             if (response != '') {
4875                 o = Roo.util.JSON.decode(response.responseText);
4876                 if(this.jsonRoot){
4877                     
4878                     o = o[this.jsonRoot];
4879                 }
4880             }
4881         } catch(e){
4882         }
4883         /**
4884          * The current JSON data or null
4885          */
4886         this.jsonData = o;
4887         this.beforeRender();
4888         this.refresh();
4889     },
4890
4891 /**
4892  * Get the number of records in the current JSON dataset
4893  * @return {Number}
4894  */
4895     getCount : function(){
4896         return this.jsonData ? this.jsonData.length : 0;
4897     },
4898
4899 /**
4900  * Returns the JSON object for the specified node(s)
4901  * @param {HTMLElement/Array} node The node or an array of nodes
4902  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4903  * you get the JSON object for the node
4904  */
4905     getNodeData : function(node){
4906         if(node instanceof Array){
4907             var data = [];
4908             for(var i = 0, len = node.length; i < len; i++){
4909                 data.push(this.getNodeData(node[i]));
4910             }
4911             return data;
4912         }
4913         return this.jsonData[this.indexOf(node)] || null;
4914     },
4915
4916     beforeRender : function(){
4917         this.snapshot = this.jsonData;
4918         if(this.sortInfo){
4919             this.sort.apply(this, this.sortInfo);
4920         }
4921         this.fireEvent("beforerender", this, this.jsonData);
4922     },
4923
4924     onLoad : function(el, o){
4925         this.fireEvent("load", this, this.jsonData, o);
4926     },
4927
4928     onLoadException : function(el, o){
4929         this.fireEvent("loadexception", this, o);
4930     },
4931
4932 /**
4933  * Filter the data by a specific property.
4934  * @param {String} property A property on your JSON objects
4935  * @param {String/RegExp} value Either string that the property values
4936  * should start with, or a RegExp to test against the property
4937  */
4938     filter : function(property, value){
4939         if(this.jsonData){
4940             var data = [];
4941             var ss = this.snapshot;
4942             if(typeof value == "string"){
4943                 var vlen = value.length;
4944                 if(vlen == 0){
4945                     this.clearFilter();
4946                     return;
4947                 }
4948                 value = value.toLowerCase();
4949                 for(var i = 0, len = ss.length; i < len; i++){
4950                     var o = ss[i];
4951                     if(o[property].substr(0, vlen).toLowerCase() == value){
4952                         data.push(o);
4953                     }
4954                 }
4955             } else if(value.exec){ // regex?
4956                 for(var i = 0, len = ss.length; i < len; i++){
4957                     var o = ss[i];
4958                     if(value.test(o[property])){
4959                         data.push(o);
4960                     }
4961                 }
4962             } else{
4963                 return;
4964             }
4965             this.jsonData = data;
4966             this.refresh();
4967         }
4968     },
4969
4970 /**
4971  * Filter by a function. The passed function will be called with each
4972  * object in the current dataset. If the function returns true the value is kept,
4973  * otherwise it is filtered.
4974  * @param {Function} fn
4975  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4976  */
4977     filterBy : function(fn, scope){
4978         if(this.jsonData){
4979             var data = [];
4980             var ss = this.snapshot;
4981             for(var i = 0, len = ss.length; i < len; i++){
4982                 var o = ss[i];
4983                 if(fn.call(scope || this, o)){
4984                     data.push(o);
4985                 }
4986             }
4987             this.jsonData = data;
4988             this.refresh();
4989         }
4990     },
4991
4992 /**
4993  * Clears the current filter.
4994  */
4995     clearFilter : function(){
4996         if(this.snapshot && this.jsonData != this.snapshot){
4997             this.jsonData = this.snapshot;
4998             this.refresh();
4999         }
5000     },
5001
5002
5003 /**
5004  * Sorts the data for this view and refreshes it.
5005  * @param {String} property A property on your JSON objects to sort on
5006  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5007  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5008  */
5009     sort : function(property, dir, sortType){
5010         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5011         if(this.jsonData){
5012             var p = property;
5013             var dsc = dir && dir.toLowerCase() == "desc";
5014             var f = function(o1, o2){
5015                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5016                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5017                 ;
5018                 if(v1 < v2){
5019                     return dsc ? +1 : -1;
5020                 } else if(v1 > v2){
5021                     return dsc ? -1 : +1;
5022                 } else{
5023                     return 0;
5024                 }
5025             };
5026             this.jsonData.sort(f);
5027             this.refresh();
5028             if(this.jsonData != this.snapshot){
5029                 this.snapshot.sort(f);
5030             }
5031         }
5032     }
5033 });/*
5034  * Based on:
5035  * Ext JS Library 1.1.1
5036  * Copyright(c) 2006-2007, Ext JS, LLC.
5037  *
5038  * Originally Released Under LGPL - original licence link has changed is not relivant.
5039  *
5040  * Fork - LGPL
5041  * <script type="text/javascript">
5042  */
5043  
5044
5045 /**
5046  * @class Roo.ColorPalette
5047  * @extends Roo.Component
5048  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5049  * Here's an example of typical usage:
5050  * <pre><code>
5051 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5052 cp.render('my-div');
5053
5054 cp.on('select', function(palette, selColor){
5055     // do something with selColor
5056 });
5057 </code></pre>
5058  * @constructor
5059  * Create a new ColorPalette
5060  * @param {Object} config The config object
5061  */
5062 Roo.ColorPalette = function(config){
5063     Roo.ColorPalette.superclass.constructor.call(this, config);
5064     this.addEvents({
5065         /**
5066              * @event select
5067              * Fires when a color is selected
5068              * @param {ColorPalette} this
5069              * @param {String} color The 6-digit color hex code (without the # symbol)
5070              */
5071         select: true
5072     });
5073
5074     if(this.handler){
5075         this.on("select", this.handler, this.scope, true);
5076     }
5077 };
5078 Roo.extend(Roo.ColorPalette, Roo.Component, {
5079     /**
5080      * @cfg {String} itemCls
5081      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5082      */
5083     itemCls : "x-color-palette",
5084     /**
5085      * @cfg {String} value
5086      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5087      * the hex codes are case-sensitive.
5088      */
5089     value : null,
5090     clickEvent:'click',
5091     // private
5092     ctype: "Roo.ColorPalette",
5093
5094     /**
5095      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5096      */
5097     allowReselect : false,
5098
5099     /**
5100      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5101      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5102      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5103      * of colors with the width setting until the box is symmetrical.</p>
5104      * <p>You can override individual colors if needed:</p>
5105      * <pre><code>
5106 var cp = new Roo.ColorPalette();
5107 cp.colors[0] = "FF0000";  // change the first box to red
5108 </code></pre>
5109
5110 Or you can provide a custom array of your own for complete control:
5111 <pre><code>
5112 var cp = new Roo.ColorPalette();
5113 cp.colors = ["000000", "993300", "333300"];
5114 </code></pre>
5115      * @type Array
5116      */
5117     colors : [
5118         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5119         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5120         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5121         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5122         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5123     ],
5124
5125     // private
5126     onRender : function(container, position){
5127         var t = new Roo.MasterTemplate(
5128             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5129         );
5130         var c = this.colors;
5131         for(var i = 0, len = c.length; i < len; i++){
5132             t.add([c[i]]);
5133         }
5134         var el = document.createElement("div");
5135         el.className = this.itemCls;
5136         t.overwrite(el);
5137         container.dom.insertBefore(el, position);
5138         this.el = Roo.get(el);
5139         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5140         if(this.clickEvent != 'click'){
5141             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5142         }
5143     },
5144
5145     // private
5146     afterRender : function(){
5147         Roo.ColorPalette.superclass.afterRender.call(this);
5148         if(this.value){
5149             var s = this.value;
5150             this.value = null;
5151             this.select(s);
5152         }
5153     },
5154
5155     // private
5156     handleClick : function(e, t){
5157         e.preventDefault();
5158         if(!this.disabled){
5159             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5160             this.select(c.toUpperCase());
5161         }
5162     },
5163
5164     /**
5165      * Selects the specified color in the palette (fires the select event)
5166      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5167      */
5168     select : function(color){
5169         color = color.replace("#", "");
5170         if(color != this.value || this.allowReselect){
5171             var el = this.el;
5172             if(this.value){
5173                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5174             }
5175             el.child("a.color-"+color).addClass("x-color-palette-sel");
5176             this.value = color;
5177             this.fireEvent("select", this, color);
5178         }
5179     }
5180 });/*
5181  * Based on:
5182  * Ext JS Library 1.1.1
5183  * Copyright(c) 2006-2007, Ext JS, LLC.
5184  *
5185  * Originally Released Under LGPL - original licence link has changed is not relivant.
5186  *
5187  * Fork - LGPL
5188  * <script type="text/javascript">
5189  */
5190  
5191 /**
5192  * @class Roo.DatePicker
5193  * @extends Roo.Component
5194  * Simple date picker class.
5195  * @constructor
5196  * Create a new DatePicker
5197  * @param {Object} config The config object
5198  */
5199 Roo.DatePicker = function(config){
5200     Roo.DatePicker.superclass.constructor.call(this, config);
5201
5202     this.value = config && config.value ?
5203                  config.value.clearTime() : new Date().clearTime();
5204
5205     this.addEvents({
5206         /**
5207              * @event select
5208              * Fires when a date is selected
5209              * @param {DatePicker} this
5210              * @param {Date} date The selected date
5211              */
5212         'select': true,
5213         /**
5214              * @event monthchange
5215              * Fires when the displayed month changes 
5216              * @param {DatePicker} this
5217              * @param {Date} date The selected month
5218              */
5219         'monthchange': true
5220     });
5221
5222     if(this.handler){
5223         this.on("select", this.handler,  this.scope || this);
5224     }
5225     // build the disabledDatesRE
5226     if(!this.disabledDatesRE && this.disabledDates){
5227         var dd = this.disabledDates;
5228         var re = "(?:";
5229         for(var i = 0; i < dd.length; i++){
5230             re += dd[i];
5231             if(i != dd.length-1) {
5232                 re += "|";
5233             }
5234         }
5235         this.disabledDatesRE = new RegExp(re + ")");
5236     }
5237 };
5238
5239 Roo.extend(Roo.DatePicker, Roo.Component, {
5240     /**
5241      * @cfg {String} todayText
5242      * The text to display on the button that selects the current date (defaults to "Today")
5243      */
5244     todayText : "Today",
5245     /**
5246      * @cfg {String} okText
5247      * The text to display on the ok button
5248      */
5249     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5250     /**
5251      * @cfg {String} cancelText
5252      * The text to display on the cancel button
5253      */
5254     cancelText : "Cancel",
5255     /**
5256      * @cfg {String} todayTip
5257      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5258      */
5259     todayTip : "{0} (Spacebar)",
5260     /**
5261      * @cfg {Date} minDate
5262      * Minimum allowable date (JavaScript date object, defaults to null)
5263      */
5264     minDate : null,
5265     /**
5266      * @cfg {Date} maxDate
5267      * Maximum allowable date (JavaScript date object, defaults to null)
5268      */
5269     maxDate : null,
5270     /**
5271      * @cfg {String} minText
5272      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5273      */
5274     minText : "This date is before the minimum date",
5275     /**
5276      * @cfg {String} maxText
5277      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5278      */
5279     maxText : "This date is after the maximum date",
5280     /**
5281      * @cfg {String} format
5282      * The default date format string which can be overriden for localization support.  The format must be
5283      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5284      */
5285     format : "m/d/y",
5286     /**
5287      * @cfg {Array} disabledDays
5288      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5289      */
5290     disabledDays : null,
5291     /**
5292      * @cfg {String} disabledDaysText
5293      * The tooltip to display when the date falls on a disabled day (defaults to "")
5294      */
5295     disabledDaysText : "",
5296     /**
5297      * @cfg {RegExp} disabledDatesRE
5298      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5299      */
5300     disabledDatesRE : null,
5301     /**
5302      * @cfg {String} disabledDatesText
5303      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5304      */
5305     disabledDatesText : "",
5306     /**
5307      * @cfg {Boolean} constrainToViewport
5308      * True to constrain the date picker to the viewport (defaults to true)
5309      */
5310     constrainToViewport : true,
5311     /**
5312      * @cfg {Array} monthNames
5313      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5314      */
5315     monthNames : Date.monthNames,
5316     /**
5317      * @cfg {Array} dayNames
5318      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5319      */
5320     dayNames : Date.dayNames,
5321     /**
5322      * @cfg {String} nextText
5323      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5324      */
5325     nextText: 'Next Month (Control+Right)',
5326     /**
5327      * @cfg {String} prevText
5328      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5329      */
5330     prevText: 'Previous Month (Control+Left)',
5331     /**
5332      * @cfg {String} monthYearText
5333      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5334      */
5335     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5336     /**
5337      * @cfg {Number} startDay
5338      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5339      */
5340     startDay : 0,
5341     /**
5342      * @cfg {Bool} showClear
5343      * Show a clear button (usefull for date form elements that can be blank.)
5344      */
5345     
5346     showClear: false,
5347     
5348     /**
5349      * Sets the value of the date field
5350      * @param {Date} value The date to set
5351      */
5352     setValue : function(value){
5353         var old = this.value;
5354         
5355         if (typeof(value) == 'string') {
5356          
5357             value = Date.parseDate(value, this.format);
5358         }
5359         if (!value) {
5360             value = new Date();
5361         }
5362         
5363         this.value = value.clearTime(true);
5364         if(this.el){
5365             this.update(this.value);
5366         }
5367     },
5368
5369     /**
5370      * Gets the current selected value of the date field
5371      * @return {Date} The selected date
5372      */
5373     getValue : function(){
5374         return this.value;
5375     },
5376
5377     // private
5378     focus : function(){
5379         if(this.el){
5380             this.update(this.activeDate);
5381         }
5382     },
5383
5384     // privateval
5385     onRender : function(container, position){
5386         
5387         var m = [
5388              '<table cellspacing="0">',
5389                 '<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>',
5390                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5391         var dn = this.dayNames;
5392         for(var i = 0; i < 7; i++){
5393             var d = this.startDay+i;
5394             if(d > 6){
5395                 d = d-7;
5396             }
5397             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5398         }
5399         m[m.length] = "</tr></thead><tbody><tr>";
5400         for(var i = 0; i < 42; i++) {
5401             if(i % 7 == 0 && i != 0){
5402                 m[m.length] = "</tr><tr>";
5403             }
5404             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5405         }
5406         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5407             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5408
5409         var el = document.createElement("div");
5410         el.className = "x-date-picker";
5411         el.innerHTML = m.join("");
5412
5413         container.dom.insertBefore(el, position);
5414
5415         this.el = Roo.get(el);
5416         this.eventEl = Roo.get(el.firstChild);
5417
5418         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5419             handler: this.showPrevMonth,
5420             scope: this,
5421             preventDefault:true,
5422             stopDefault:true
5423         });
5424
5425         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5426             handler: this.showNextMonth,
5427             scope: this,
5428             preventDefault:true,
5429             stopDefault:true
5430         });
5431
5432         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5433
5434         this.monthPicker = this.el.down('div.x-date-mp');
5435         this.monthPicker.enableDisplayMode('block');
5436         
5437         var kn = new Roo.KeyNav(this.eventEl, {
5438             "left" : function(e){
5439                 e.ctrlKey ?
5440                     this.showPrevMonth() :
5441                     this.update(this.activeDate.add("d", -1));
5442             },
5443
5444             "right" : function(e){
5445                 e.ctrlKey ?
5446                     this.showNextMonth() :
5447                     this.update(this.activeDate.add("d", 1));
5448             },
5449
5450             "up" : function(e){
5451                 e.ctrlKey ?
5452                     this.showNextYear() :
5453                     this.update(this.activeDate.add("d", -7));
5454             },
5455
5456             "down" : function(e){
5457                 e.ctrlKey ?
5458                     this.showPrevYear() :
5459                     this.update(this.activeDate.add("d", 7));
5460             },
5461
5462             "pageUp" : function(e){
5463                 this.showNextMonth();
5464             },
5465
5466             "pageDown" : function(e){
5467                 this.showPrevMonth();
5468             },
5469
5470             "enter" : function(e){
5471                 e.stopPropagation();
5472                 return true;
5473             },
5474
5475             scope : this
5476         });
5477
5478         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5479
5480         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5481
5482         this.el.unselectable();
5483         
5484         this.cells = this.el.select("table.x-date-inner tbody td");
5485         this.textNodes = this.el.query("table.x-date-inner tbody span");
5486
5487         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5488             text: "&#160;",
5489             tooltip: this.monthYearText
5490         });
5491
5492         this.mbtn.on('click', this.showMonthPicker, this);
5493         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5494
5495
5496         var today = (new Date()).dateFormat(this.format);
5497         
5498         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5499         if (this.showClear) {
5500             baseTb.add( new Roo.Toolbar.Fill());
5501         }
5502         baseTb.add({
5503             text: String.format(this.todayText, today),
5504             tooltip: String.format(this.todayTip, today),
5505             handler: this.selectToday,
5506             scope: this
5507         });
5508         
5509         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5510             
5511         //});
5512         if (this.showClear) {
5513             
5514             baseTb.add( new Roo.Toolbar.Fill());
5515             baseTb.add({
5516                 text: '&#160;',
5517                 cls: 'x-btn-icon x-btn-clear',
5518                 handler: function() {
5519                     //this.value = '';
5520                     this.fireEvent("select", this, '');
5521                 },
5522                 scope: this
5523             });
5524         }
5525         
5526         
5527         if(Roo.isIE){
5528             this.el.repaint();
5529         }
5530         this.update(this.value);
5531     },
5532
5533     createMonthPicker : function(){
5534         if(!this.monthPicker.dom.firstChild){
5535             var buf = ['<table border="0" cellspacing="0">'];
5536             for(var i = 0; i < 6; i++){
5537                 buf.push(
5538                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5539                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5540                     i == 0 ?
5541                     '<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>' :
5542                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5543                 );
5544             }
5545             buf.push(
5546                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5547                     this.okText,
5548                     '</button><button type="button" class="x-date-mp-cancel">',
5549                     this.cancelText,
5550                     '</button></td></tr>',
5551                 '</table>'
5552             );
5553             this.monthPicker.update(buf.join(''));
5554             this.monthPicker.on('click', this.onMonthClick, this);
5555             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5556
5557             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5558             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5559
5560             this.mpMonths.each(function(m, a, i){
5561                 i += 1;
5562                 if((i%2) == 0){
5563                     m.dom.xmonth = 5 + Math.round(i * .5);
5564                 }else{
5565                     m.dom.xmonth = Math.round((i-1) * .5);
5566                 }
5567             });
5568         }
5569     },
5570
5571     showMonthPicker : function(){
5572         this.createMonthPicker();
5573         var size = this.el.getSize();
5574         this.monthPicker.setSize(size);
5575         this.monthPicker.child('table').setSize(size);
5576
5577         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5578         this.updateMPMonth(this.mpSelMonth);
5579         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5580         this.updateMPYear(this.mpSelYear);
5581
5582         this.monthPicker.slideIn('t', {duration:.2});
5583     },
5584
5585     updateMPYear : function(y){
5586         this.mpyear = y;
5587         var ys = this.mpYears.elements;
5588         for(var i = 1; i <= 10; i++){
5589             var td = ys[i-1], y2;
5590             if((i%2) == 0){
5591                 y2 = y + Math.round(i * .5);
5592                 td.firstChild.innerHTML = y2;
5593                 td.xyear = y2;
5594             }else{
5595                 y2 = y - (5-Math.round(i * .5));
5596                 td.firstChild.innerHTML = y2;
5597                 td.xyear = y2;
5598             }
5599             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5600         }
5601     },
5602
5603     updateMPMonth : function(sm){
5604         this.mpMonths.each(function(m, a, i){
5605             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5606         });
5607     },
5608
5609     selectMPMonth: function(m){
5610         
5611     },
5612
5613     onMonthClick : function(e, t){
5614         e.stopEvent();
5615         var el = new Roo.Element(t), pn;
5616         if(el.is('button.x-date-mp-cancel')){
5617             this.hideMonthPicker();
5618         }
5619         else if(el.is('button.x-date-mp-ok')){
5620             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5621             this.hideMonthPicker();
5622         }
5623         else if(pn = el.up('td.x-date-mp-month', 2)){
5624             this.mpMonths.removeClass('x-date-mp-sel');
5625             pn.addClass('x-date-mp-sel');
5626             this.mpSelMonth = pn.dom.xmonth;
5627         }
5628         else if(pn = el.up('td.x-date-mp-year', 2)){
5629             this.mpYears.removeClass('x-date-mp-sel');
5630             pn.addClass('x-date-mp-sel');
5631             this.mpSelYear = pn.dom.xyear;
5632         }
5633         else if(el.is('a.x-date-mp-prev')){
5634             this.updateMPYear(this.mpyear-10);
5635         }
5636         else if(el.is('a.x-date-mp-next')){
5637             this.updateMPYear(this.mpyear+10);
5638         }
5639     },
5640
5641     onMonthDblClick : function(e, t){
5642         e.stopEvent();
5643         var el = new Roo.Element(t), pn;
5644         if(pn = el.up('td.x-date-mp-month', 2)){
5645             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5646             this.hideMonthPicker();
5647         }
5648         else if(pn = el.up('td.x-date-mp-year', 2)){
5649             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5650             this.hideMonthPicker();
5651         }
5652     },
5653
5654     hideMonthPicker : function(disableAnim){
5655         if(this.monthPicker){
5656             if(disableAnim === true){
5657                 this.monthPicker.hide();
5658             }else{
5659                 this.monthPicker.slideOut('t', {duration:.2});
5660             }
5661         }
5662     },
5663
5664     // private
5665     showPrevMonth : function(e){
5666         this.update(this.activeDate.add("mo", -1));
5667     },
5668
5669     // private
5670     showNextMonth : function(e){
5671         this.update(this.activeDate.add("mo", 1));
5672     },
5673
5674     // private
5675     showPrevYear : function(){
5676         this.update(this.activeDate.add("y", -1));
5677     },
5678
5679     // private
5680     showNextYear : function(){
5681         this.update(this.activeDate.add("y", 1));
5682     },
5683
5684     // private
5685     handleMouseWheel : function(e){
5686         var delta = e.getWheelDelta();
5687         if(delta > 0){
5688             this.showPrevMonth();
5689             e.stopEvent();
5690         } else if(delta < 0){
5691             this.showNextMonth();
5692             e.stopEvent();
5693         }
5694     },
5695
5696     // private
5697     handleDateClick : function(e, t){
5698         e.stopEvent();
5699         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5700             this.setValue(new Date(t.dateValue));
5701             this.fireEvent("select", this, this.value);
5702         }
5703     },
5704
5705     // private
5706     selectToday : function(){
5707         this.setValue(new Date().clearTime());
5708         this.fireEvent("select", this, this.value);
5709     },
5710
5711     // private
5712     update : function(date)
5713     {
5714         var vd = this.activeDate;
5715         this.activeDate = date;
5716         if(vd && this.el){
5717             var t = date.getTime();
5718             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5719                 this.cells.removeClass("x-date-selected");
5720                 this.cells.each(function(c){
5721                    if(c.dom.firstChild.dateValue == t){
5722                        c.addClass("x-date-selected");
5723                        setTimeout(function(){
5724                             try{c.dom.firstChild.focus();}catch(e){}
5725                        }, 50);
5726                        return false;
5727                    }
5728                 });
5729                 return;
5730             }
5731         }
5732         
5733         var days = date.getDaysInMonth();
5734         var firstOfMonth = date.getFirstDateOfMonth();
5735         var startingPos = firstOfMonth.getDay()-this.startDay;
5736
5737         if(startingPos <= this.startDay){
5738             startingPos += 7;
5739         }
5740
5741         var pm = date.add("mo", -1);
5742         var prevStart = pm.getDaysInMonth()-startingPos;
5743
5744         var cells = this.cells.elements;
5745         var textEls = this.textNodes;
5746         days += startingPos;
5747
5748         // convert everything to numbers so it's fast
5749         var day = 86400000;
5750         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5751         var today = new Date().clearTime().getTime();
5752         var sel = date.clearTime().getTime();
5753         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5754         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5755         var ddMatch = this.disabledDatesRE;
5756         var ddText = this.disabledDatesText;
5757         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5758         var ddaysText = this.disabledDaysText;
5759         var format = this.format;
5760
5761         var setCellClass = function(cal, cell){
5762             cell.title = "";
5763             var t = d.getTime();
5764             cell.firstChild.dateValue = t;
5765             if(t == today){
5766                 cell.className += " x-date-today";
5767                 cell.title = cal.todayText;
5768             }
5769             if(t == sel){
5770                 cell.className += " x-date-selected";
5771                 setTimeout(function(){
5772                     try{cell.firstChild.focus();}catch(e){}
5773                 }, 50);
5774             }
5775             // disabling
5776             if(t < min) {
5777                 cell.className = " x-date-disabled";
5778                 cell.title = cal.minText;
5779                 return;
5780             }
5781             if(t > max) {
5782                 cell.className = " x-date-disabled";
5783                 cell.title = cal.maxText;
5784                 return;
5785             }
5786             if(ddays){
5787                 if(ddays.indexOf(d.getDay()) != -1){
5788                     cell.title = ddaysText;
5789                     cell.className = " x-date-disabled";
5790                 }
5791             }
5792             if(ddMatch && format){
5793                 var fvalue = d.dateFormat(format);
5794                 if(ddMatch.test(fvalue)){
5795                     cell.title = ddText.replace("%0", fvalue);
5796                     cell.className = " x-date-disabled";
5797                 }
5798             }
5799         };
5800
5801         var i = 0;
5802         for(; i < startingPos; i++) {
5803             textEls[i].innerHTML = (++prevStart);
5804             d.setDate(d.getDate()+1);
5805             cells[i].className = "x-date-prevday";
5806             setCellClass(this, cells[i]);
5807         }
5808         for(; i < days; i++){
5809             intDay = i - startingPos + 1;
5810             textEls[i].innerHTML = (intDay);
5811             d.setDate(d.getDate()+1);
5812             cells[i].className = "x-date-active";
5813             setCellClass(this, cells[i]);
5814         }
5815         var extraDays = 0;
5816         for(; i < 42; i++) {
5817              textEls[i].innerHTML = (++extraDays);
5818              d.setDate(d.getDate()+1);
5819              cells[i].className = "x-date-nextday";
5820              setCellClass(this, cells[i]);
5821         }
5822
5823         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5824         this.fireEvent('monthchange', this, date);
5825         
5826         if(!this.internalRender){
5827             var main = this.el.dom.firstChild;
5828             var w = main.offsetWidth;
5829             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5830             Roo.fly(main).setWidth(w);
5831             this.internalRender = true;
5832             // opera does not respect the auto grow header center column
5833             // then, after it gets a width opera refuses to recalculate
5834             // without a second pass
5835             if(Roo.isOpera && !this.secondPass){
5836                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5837                 this.secondPass = true;
5838                 this.update.defer(10, this, [date]);
5839             }
5840         }
5841         
5842         
5843     }
5844 });        /*
5845  * Based on:
5846  * Ext JS Library 1.1.1
5847  * Copyright(c) 2006-2007, Ext JS, LLC.
5848  *
5849  * Originally Released Under LGPL - original licence link has changed is not relivant.
5850  *
5851  * Fork - LGPL
5852  * <script type="text/javascript">
5853  */
5854 /**
5855  * @class Roo.TabPanel
5856  * @extends Roo.util.Observable
5857  * A lightweight tab container.
5858  * <br><br>
5859  * Usage:
5860  * <pre><code>
5861 // basic tabs 1, built from existing content
5862 var tabs = new Roo.TabPanel("tabs1");
5863 tabs.addTab("script", "View Script");
5864 tabs.addTab("markup", "View Markup");
5865 tabs.activate("script");
5866
5867 // more advanced tabs, built from javascript
5868 var jtabs = new Roo.TabPanel("jtabs");
5869 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5870
5871 // set up the UpdateManager
5872 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5873 var updater = tab2.getUpdateManager();
5874 updater.setDefaultUrl("ajax1.htm");
5875 tab2.on('activate', updater.refresh, updater, true);
5876
5877 // Use setUrl for Ajax loading
5878 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5879 tab3.setUrl("ajax2.htm", null, true);
5880
5881 // Disabled tab
5882 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5883 tab4.disable();
5884
5885 jtabs.activate("jtabs-1");
5886  * </code></pre>
5887  * @constructor
5888  * Create a new TabPanel.
5889  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5890  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5891  */
5892 Roo.TabPanel = function(container, config){
5893     /**
5894     * The container element for this TabPanel.
5895     * @type Roo.Element
5896     */
5897     this.el = Roo.get(container, true);
5898     if(config){
5899         if(typeof config == "boolean"){
5900             this.tabPosition = config ? "bottom" : "top";
5901         }else{
5902             Roo.apply(this, config);
5903         }
5904     }
5905     if(this.tabPosition == "bottom"){
5906         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5907         this.el.addClass("x-tabs-bottom");
5908     }
5909     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5910     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5911     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5912     if(Roo.isIE){
5913         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5914     }
5915     if(this.tabPosition != "bottom"){
5916         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5917          * @type Roo.Element
5918          */
5919         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5920         this.el.addClass("x-tabs-top");
5921     }
5922     this.items = [];
5923
5924     this.bodyEl.setStyle("position", "relative");
5925
5926     this.active = null;
5927     this.activateDelegate = this.activate.createDelegate(this);
5928
5929     this.addEvents({
5930         /**
5931          * @event tabchange
5932          * Fires when the active tab changes
5933          * @param {Roo.TabPanel} this
5934          * @param {Roo.TabPanelItem} activePanel The new active tab
5935          */
5936         "tabchange": true,
5937         /**
5938          * @event beforetabchange
5939          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5940          * @param {Roo.TabPanel} this
5941          * @param {Object} e Set cancel to true on this object to cancel the tab change
5942          * @param {Roo.TabPanelItem} tab The tab being changed to
5943          */
5944         "beforetabchange" : true
5945     });
5946
5947     Roo.EventManager.onWindowResize(this.onResize, this);
5948     this.cpad = this.el.getPadding("lr");
5949     this.hiddenCount = 0;
5950
5951
5952     // toolbar on the tabbar support...
5953     if (this.toolbar) {
5954         var tcfg = this.toolbar;
5955         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5956         this.toolbar = new Roo.Toolbar(tcfg);
5957         if (Roo.isSafari) {
5958             var tbl = tcfg.container.child('table', true);
5959             tbl.setAttribute('width', '100%');
5960         }
5961         
5962     }
5963    
5964
5965
5966     Roo.TabPanel.superclass.constructor.call(this);
5967 };
5968
5969 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5970     /*
5971      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5972      */
5973     tabPosition : "top",
5974     /*
5975      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5976      */
5977     currentTabWidth : 0,
5978     /*
5979      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5980      */
5981     minTabWidth : 40,
5982     /*
5983      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5984      */
5985     maxTabWidth : 250,
5986     /*
5987      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5988      */
5989     preferredTabWidth : 175,
5990     /*
5991      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5992      */
5993     resizeTabs : false,
5994     /*
5995      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5996      */
5997     monitorResize : true,
5998     /*
5999      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6000      */
6001     toolbar : false,
6002
6003     /**
6004      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6005      * @param {String} id The id of the div to use <b>or create</b>
6006      * @param {String} text The text for the tab
6007      * @param {String} content (optional) Content to put in the TabPanelItem body
6008      * @param {Boolean} closable (optional) True to create a close icon on the tab
6009      * @return {Roo.TabPanelItem} The created TabPanelItem
6010      */
6011     addTab : function(id, text, content, closable){
6012         var item = new Roo.TabPanelItem(this, id, text, closable);
6013         this.addTabItem(item);
6014         if(content){
6015             item.setContent(content);
6016         }
6017         return item;
6018     },
6019
6020     /**
6021      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6022      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6023      * @return {Roo.TabPanelItem}
6024      */
6025     getTab : function(id){
6026         return this.items[id];
6027     },
6028
6029     /**
6030      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6031      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6032      */
6033     hideTab : function(id){
6034         var t = this.items[id];
6035         if(!t.isHidden()){
6036            t.setHidden(true);
6037            this.hiddenCount++;
6038            this.autoSizeTabs();
6039         }
6040     },
6041
6042     /**
6043      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6044      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6045      */
6046     unhideTab : function(id){
6047         var t = this.items[id];
6048         if(t.isHidden()){
6049            t.setHidden(false);
6050            this.hiddenCount--;
6051            this.autoSizeTabs();
6052         }
6053     },
6054
6055     /**
6056      * Adds an existing {@link Roo.TabPanelItem}.
6057      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6058      */
6059     addTabItem : function(item){
6060         this.items[item.id] = item;
6061         this.items.push(item);
6062         if(this.resizeTabs){
6063            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6064            this.autoSizeTabs();
6065         }else{
6066             item.autoSize();
6067         }
6068     },
6069
6070     /**
6071      * Removes a {@link Roo.TabPanelItem}.
6072      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6073      */
6074     removeTab : function(id){
6075         var items = this.items;
6076         var tab = items[id];
6077         if(!tab) { return; }
6078         var index = items.indexOf(tab);
6079         if(this.active == tab && items.length > 1){
6080             var newTab = this.getNextAvailable(index);
6081             if(newTab) {
6082                 newTab.activate();
6083             }
6084         }
6085         this.stripEl.dom.removeChild(tab.pnode.dom);
6086         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6087             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6088         }
6089         items.splice(index, 1);
6090         delete this.items[tab.id];
6091         tab.fireEvent("close", tab);
6092         tab.purgeListeners();
6093         this.autoSizeTabs();
6094     },
6095
6096     getNextAvailable : function(start){
6097         var items = this.items;
6098         var index = start;
6099         // look for a next tab that will slide over to
6100         // replace the one being removed
6101         while(index < items.length){
6102             var item = items[++index];
6103             if(item && !item.isHidden()){
6104                 return item;
6105             }
6106         }
6107         // if one isn't found select the previous tab (on the left)
6108         index = start;
6109         while(index >= 0){
6110             var item = items[--index];
6111             if(item && !item.isHidden()){
6112                 return item;
6113             }
6114         }
6115         return null;
6116     },
6117
6118     /**
6119      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6120      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6121      */
6122     disableTab : function(id){
6123         var tab = this.items[id];
6124         if(tab && this.active != tab){
6125             tab.disable();
6126         }
6127     },
6128
6129     /**
6130      * Enables a {@link Roo.TabPanelItem} that is disabled.
6131      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6132      */
6133     enableTab : function(id){
6134         var tab = this.items[id];
6135         tab.enable();
6136     },
6137
6138     /**
6139      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6140      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6141      * @return {Roo.TabPanelItem} The TabPanelItem.
6142      */
6143     activate : function(id){
6144         var tab = this.items[id];
6145         if(!tab){
6146             return null;
6147         }
6148         if(tab == this.active || tab.disabled){
6149             return tab;
6150         }
6151         var e = {};
6152         this.fireEvent("beforetabchange", this, e, tab);
6153         if(e.cancel !== true && !tab.disabled){
6154             if(this.active){
6155                 this.active.hide();
6156             }
6157             this.active = this.items[id];
6158             this.active.show();
6159             this.fireEvent("tabchange", this, this.active);
6160         }
6161         return tab;
6162     },
6163
6164     /**
6165      * Gets the active {@link Roo.TabPanelItem}.
6166      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6167      */
6168     getActiveTab : function(){
6169         return this.active;
6170     },
6171
6172     /**
6173      * Updates the tab body element to fit the height of the container element
6174      * for overflow scrolling
6175      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6176      */
6177     syncHeight : function(targetHeight){
6178         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6179         var bm = this.bodyEl.getMargins();
6180         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6181         this.bodyEl.setHeight(newHeight);
6182         return newHeight;
6183     },
6184
6185     onResize : function(){
6186         if(this.monitorResize){
6187             this.autoSizeTabs();
6188         }
6189     },
6190
6191     /**
6192      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6193      */
6194     beginUpdate : function(){
6195         this.updating = true;
6196     },
6197
6198     /**
6199      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6200      */
6201     endUpdate : function(){
6202         this.updating = false;
6203         this.autoSizeTabs();
6204     },
6205
6206     /**
6207      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6208      */
6209     autoSizeTabs : function(){
6210         var count = this.items.length;
6211         var vcount = count - this.hiddenCount;
6212         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6213             return;
6214         }
6215         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6216         var availWidth = Math.floor(w / vcount);
6217         var b = this.stripBody;
6218         if(b.getWidth() > w){
6219             var tabs = this.items;
6220             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6221             if(availWidth < this.minTabWidth){
6222                 /*if(!this.sleft){    // incomplete scrolling code
6223                     this.createScrollButtons();
6224                 }
6225                 this.showScroll();
6226                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6227             }
6228         }else{
6229             if(this.currentTabWidth < this.preferredTabWidth){
6230                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6231             }
6232         }
6233     },
6234
6235     /**
6236      * Returns the number of tabs in this TabPanel.
6237      * @return {Number}
6238      */
6239      getCount : function(){
6240          return this.items.length;
6241      },
6242
6243     /**
6244      * Resizes all the tabs to the passed width
6245      * @param {Number} The new width
6246      */
6247     setTabWidth : function(width){
6248         this.currentTabWidth = width;
6249         for(var i = 0, len = this.items.length; i < len; i++) {
6250                 if(!this.items[i].isHidden()) {
6251                 this.items[i].setWidth(width);
6252             }
6253         }
6254     },
6255
6256     /**
6257      * Destroys this TabPanel
6258      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6259      */
6260     destroy : function(removeEl){
6261         Roo.EventManager.removeResizeListener(this.onResize, this);
6262         for(var i = 0, len = this.items.length; i < len; i++){
6263             this.items[i].purgeListeners();
6264         }
6265         if(removeEl === true){
6266             this.el.update("");
6267             this.el.remove();
6268         }
6269     }
6270 });
6271
6272 /**
6273  * @class Roo.TabPanelItem
6274  * @extends Roo.util.Observable
6275  * Represents an individual item (tab plus body) in a TabPanel.
6276  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6277  * @param {String} id The id of this TabPanelItem
6278  * @param {String} text The text for the tab of this TabPanelItem
6279  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6280  */
6281 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6282     /**
6283      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6284      * @type Roo.TabPanel
6285      */
6286     this.tabPanel = tabPanel;
6287     /**
6288      * The id for this TabPanelItem
6289      * @type String
6290      */
6291     this.id = id;
6292     /** @private */
6293     this.disabled = false;
6294     /** @private */
6295     this.text = text;
6296     /** @private */
6297     this.loaded = false;
6298     this.closable = closable;
6299
6300     /**
6301      * The body element for this TabPanelItem.
6302      * @type Roo.Element
6303      */
6304     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6305     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6306     this.bodyEl.setStyle("display", "block");
6307     this.bodyEl.setStyle("zoom", "1");
6308     this.hideAction();
6309
6310     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6311     /** @private */
6312     this.el = Roo.get(els.el, true);
6313     this.inner = Roo.get(els.inner, true);
6314     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6315     this.pnode = Roo.get(els.el.parentNode, true);
6316     this.el.on("mousedown", this.onTabMouseDown, this);
6317     this.el.on("click", this.onTabClick, this);
6318     /** @private */
6319     if(closable){
6320         var c = Roo.get(els.close, true);
6321         c.dom.title = this.closeText;
6322         c.addClassOnOver("close-over");
6323         c.on("click", this.closeClick, this);
6324      }
6325
6326     this.addEvents({
6327          /**
6328          * @event activate
6329          * Fires when this tab becomes the active tab.
6330          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6331          * @param {Roo.TabPanelItem} this
6332          */
6333         "activate": true,
6334         /**
6335          * @event beforeclose
6336          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6337          * @param {Roo.TabPanelItem} this
6338          * @param {Object} e Set cancel to true on this object to cancel the close.
6339          */
6340         "beforeclose": true,
6341         /**
6342          * @event close
6343          * Fires when this tab is closed.
6344          * @param {Roo.TabPanelItem} this
6345          */
6346          "close": true,
6347         /**
6348          * @event deactivate
6349          * Fires when this tab is no longer the active tab.
6350          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6351          * @param {Roo.TabPanelItem} this
6352          */
6353          "deactivate" : true
6354     });
6355     this.hidden = false;
6356
6357     Roo.TabPanelItem.superclass.constructor.call(this);
6358 };
6359
6360 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6361     purgeListeners : function(){
6362        Roo.util.Observable.prototype.purgeListeners.call(this);
6363        this.el.removeAllListeners();
6364     },
6365     /**
6366      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6367      */
6368     show : function(){
6369         this.pnode.addClass("on");
6370         this.showAction();
6371         if(Roo.isOpera){
6372             this.tabPanel.stripWrap.repaint();
6373         }
6374         this.fireEvent("activate", this.tabPanel, this);
6375     },
6376
6377     /**
6378      * Returns true if this tab is the active tab.
6379      * @return {Boolean}
6380      */
6381     isActive : function(){
6382         return this.tabPanel.getActiveTab() == this;
6383     },
6384
6385     /**
6386      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6387      */
6388     hide : function(){
6389         this.pnode.removeClass("on");
6390         this.hideAction();
6391         this.fireEvent("deactivate", this.tabPanel, this);
6392     },
6393
6394     hideAction : function(){
6395         this.bodyEl.hide();
6396         this.bodyEl.setStyle("position", "absolute");
6397         this.bodyEl.setLeft("-20000px");
6398         this.bodyEl.setTop("-20000px");
6399     },
6400
6401     showAction : function(){
6402         this.bodyEl.setStyle("position", "relative");
6403         this.bodyEl.setTop("");
6404         this.bodyEl.setLeft("");
6405         this.bodyEl.show();
6406     },
6407
6408     /**
6409      * Set the tooltip for the tab.
6410      * @param {String} tooltip The tab's tooltip
6411      */
6412     setTooltip : function(text){
6413         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6414             this.textEl.dom.qtip = text;
6415             this.textEl.dom.removeAttribute('title');
6416         }else{
6417             this.textEl.dom.title = text;
6418         }
6419     },
6420
6421     onTabClick : function(e){
6422         e.preventDefault();
6423         this.tabPanel.activate(this.id);
6424     },
6425
6426     onTabMouseDown : function(e){
6427         e.preventDefault();
6428         this.tabPanel.activate(this.id);
6429     },
6430
6431     getWidth : function(){
6432         return this.inner.getWidth();
6433     },
6434
6435     setWidth : function(width){
6436         var iwidth = width - this.pnode.getPadding("lr");
6437         this.inner.setWidth(iwidth);
6438         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6439         this.pnode.setWidth(width);
6440     },
6441
6442     /**
6443      * Show or hide the tab
6444      * @param {Boolean} hidden True to hide or false to show.
6445      */
6446     setHidden : function(hidden){
6447         this.hidden = hidden;
6448         this.pnode.setStyle("display", hidden ? "none" : "");
6449     },
6450
6451     /**
6452      * Returns true if this tab is "hidden"
6453      * @return {Boolean}
6454      */
6455     isHidden : function(){
6456         return this.hidden;
6457     },
6458
6459     /**
6460      * Returns the text for this tab
6461      * @return {String}
6462      */
6463     getText : function(){
6464         return this.text;
6465     },
6466
6467     autoSize : function(){
6468         //this.el.beginMeasure();
6469         this.textEl.setWidth(1);
6470         /*
6471          *  #2804 [new] Tabs in Roojs
6472          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6473          */
6474         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6475         //this.el.endMeasure();
6476     },
6477
6478     /**
6479      * Sets the text for the tab (Note: this also sets the tooltip text)
6480      * @param {String} text The tab's text and tooltip
6481      */
6482     setText : function(text){
6483         this.text = text;
6484         this.textEl.update(text);
6485         this.setTooltip(text);
6486         if(!this.tabPanel.resizeTabs){
6487             this.autoSize();
6488         }
6489     },
6490     /**
6491      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6492      */
6493     activate : function(){
6494         this.tabPanel.activate(this.id);
6495     },
6496
6497     /**
6498      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6499      */
6500     disable : function(){
6501         if(this.tabPanel.active != this){
6502             this.disabled = true;
6503             this.pnode.addClass("disabled");
6504         }
6505     },
6506
6507     /**
6508      * Enables this TabPanelItem if it was previously disabled.
6509      */
6510     enable : function(){
6511         this.disabled = false;
6512         this.pnode.removeClass("disabled");
6513     },
6514
6515     /**
6516      * Sets the content for this TabPanelItem.
6517      * @param {String} content The content
6518      * @param {Boolean} loadScripts true to look for and load scripts
6519      */
6520     setContent : function(content, loadScripts){
6521         this.bodyEl.update(content, loadScripts);
6522     },
6523
6524     /**
6525      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6526      * @return {Roo.UpdateManager} The UpdateManager
6527      */
6528     getUpdateManager : function(){
6529         return this.bodyEl.getUpdateManager();
6530     },
6531
6532     /**
6533      * Set a URL to be used to load the content for this TabPanelItem.
6534      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6535      * @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)
6536      * @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)
6537      * @return {Roo.UpdateManager} The UpdateManager
6538      */
6539     setUrl : function(url, params, loadOnce){
6540         if(this.refreshDelegate){
6541             this.un('activate', this.refreshDelegate);
6542         }
6543         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6544         this.on("activate", this.refreshDelegate);
6545         return this.bodyEl.getUpdateManager();
6546     },
6547
6548     /** @private */
6549     _handleRefresh : function(url, params, loadOnce){
6550         if(!loadOnce || !this.loaded){
6551             var updater = this.bodyEl.getUpdateManager();
6552             updater.update(url, params, this._setLoaded.createDelegate(this));
6553         }
6554     },
6555
6556     /**
6557      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6558      *   Will fail silently if the setUrl method has not been called.
6559      *   This does not activate the panel, just updates its content.
6560      */
6561     refresh : function(){
6562         if(this.refreshDelegate){
6563            this.loaded = false;
6564            this.refreshDelegate();
6565         }
6566     },
6567
6568     /** @private */
6569     _setLoaded : function(){
6570         this.loaded = true;
6571     },
6572
6573     /** @private */
6574     closeClick : function(e){
6575         var o = {};
6576         e.stopEvent();
6577         this.fireEvent("beforeclose", this, o);
6578         if(o.cancel !== true){
6579             this.tabPanel.removeTab(this.id);
6580         }
6581     },
6582     /**
6583      * The text displayed in the tooltip for the close icon.
6584      * @type String
6585      */
6586     closeText : "Close this tab"
6587 });
6588
6589 /** @private */
6590 Roo.TabPanel.prototype.createStrip = function(container){
6591     var strip = document.createElement("div");
6592     strip.className = "x-tabs-wrap";
6593     container.appendChild(strip);
6594     return strip;
6595 };
6596 /** @private */
6597 Roo.TabPanel.prototype.createStripList = function(strip){
6598     // div wrapper for retard IE
6599     // returns the "tr" element.
6600     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6601         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6602         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6603     return strip.firstChild.firstChild.firstChild.firstChild;
6604 };
6605 /** @private */
6606 Roo.TabPanel.prototype.createBody = function(container){
6607     var body = document.createElement("div");
6608     Roo.id(body, "tab-body");
6609     Roo.fly(body).addClass("x-tabs-body");
6610     container.appendChild(body);
6611     return body;
6612 };
6613 /** @private */
6614 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6615     var body = Roo.getDom(id);
6616     if(!body){
6617         body = document.createElement("div");
6618         body.id = id;
6619     }
6620     Roo.fly(body).addClass("x-tabs-item-body");
6621     bodyEl.insertBefore(body, bodyEl.firstChild);
6622     return body;
6623 };
6624 /** @private */
6625 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6626     var td = document.createElement("td");
6627     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6628     //stripEl.appendChild(td);
6629     if(closable){
6630         td.className = "x-tabs-closable";
6631         if(!this.closeTpl){
6632             this.closeTpl = new Roo.Template(
6633                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6634                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6635                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6636             );
6637         }
6638         var el = this.closeTpl.overwrite(td, {"text": text});
6639         var close = el.getElementsByTagName("div")[0];
6640         var inner = el.getElementsByTagName("em")[0];
6641         return {"el": el, "close": close, "inner": inner};
6642     } else {
6643         if(!this.tabTpl){
6644             this.tabTpl = new Roo.Template(
6645                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6646                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6647             );
6648         }
6649         var el = this.tabTpl.overwrite(td, {"text": text});
6650         var inner = el.getElementsByTagName("em")[0];
6651         return {"el": el, "inner": inner};
6652     }
6653 };/*
6654  * Based on:
6655  * Ext JS Library 1.1.1
6656  * Copyright(c) 2006-2007, Ext JS, LLC.
6657  *
6658  * Originally Released Under LGPL - original licence link has changed is not relivant.
6659  *
6660  * Fork - LGPL
6661  * <script type="text/javascript">
6662  */
6663
6664 /**
6665  * @class Roo.Button
6666  * @extends Roo.util.Observable
6667  * Simple Button class
6668  * @cfg {String} text The button text
6669  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6670  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6671  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6672  * @cfg {Object} scope The scope of the handler
6673  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6674  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6675  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6676  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6677  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6678  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6679    applies if enableToggle = true)
6680  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6681  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6682   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6683  * @constructor
6684  * Create a new button
6685  * @param {Object} config The config object
6686  */
6687 Roo.Button = function(renderTo, config)
6688 {
6689     if (!config) {
6690         config = renderTo;
6691         renderTo = config.renderTo || false;
6692     }
6693     
6694     Roo.apply(this, config);
6695     this.addEvents({
6696         /**
6697              * @event click
6698              * Fires when this button is clicked
6699              * @param {Button} this
6700              * @param {EventObject} e The click event
6701              */
6702             "click" : true,
6703         /**
6704              * @event toggle
6705              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6706              * @param {Button} this
6707              * @param {Boolean} pressed
6708              */
6709             "toggle" : true,
6710         /**
6711              * @event mouseover
6712              * Fires when the mouse hovers over the button
6713              * @param {Button} this
6714              * @param {Event} e The event object
6715              */
6716         'mouseover' : true,
6717         /**
6718              * @event mouseout
6719              * Fires when the mouse exits the button
6720              * @param {Button} this
6721              * @param {Event} e The event object
6722              */
6723         'mouseout': true,
6724          /**
6725              * @event render
6726              * Fires when the button is rendered
6727              * @param {Button} this
6728              */
6729         'render': true
6730     });
6731     if(this.menu){
6732         this.menu = Roo.menu.MenuMgr.get(this.menu);
6733     }
6734     // register listeners first!!  - so render can be captured..
6735     Roo.util.Observable.call(this);
6736     if(renderTo){
6737         this.render(renderTo);
6738     }
6739     
6740   
6741 };
6742
6743 Roo.extend(Roo.Button, Roo.util.Observable, {
6744     /**
6745      * 
6746      */
6747     
6748     /**
6749      * Read-only. True if this button is hidden
6750      * @type Boolean
6751      */
6752     hidden : false,
6753     /**
6754      * Read-only. True if this button is disabled
6755      * @type Boolean
6756      */
6757     disabled : false,
6758     /**
6759      * Read-only. True if this button is pressed (only if enableToggle = true)
6760      * @type Boolean
6761      */
6762     pressed : false,
6763
6764     /**
6765      * @cfg {Number} tabIndex 
6766      * The DOM tabIndex for this button (defaults to undefined)
6767      */
6768     tabIndex : undefined,
6769
6770     /**
6771      * @cfg {Boolean} enableToggle
6772      * True to enable pressed/not pressed toggling (defaults to false)
6773      */
6774     enableToggle: false,
6775     /**
6776      * @cfg {Mixed} menu
6777      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6778      */
6779     menu : undefined,
6780     /**
6781      * @cfg {String} menuAlign
6782      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6783      */
6784     menuAlign : "tl-bl?",
6785
6786     /**
6787      * @cfg {String} iconCls
6788      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6789      */
6790     iconCls : undefined,
6791     /**
6792      * @cfg {String} type
6793      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6794      */
6795     type : 'button',
6796
6797     // private
6798     menuClassTarget: 'tr',
6799
6800     /**
6801      * @cfg {String} clickEvent
6802      * The type of event to map to the button's event handler (defaults to 'click')
6803      */
6804     clickEvent : 'click',
6805
6806     /**
6807      * @cfg {Boolean} handleMouseEvents
6808      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6809      */
6810     handleMouseEvents : true,
6811
6812     /**
6813      * @cfg {String} tooltipType
6814      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6815      */
6816     tooltipType : 'qtip',
6817
6818     /**
6819      * @cfg {String} cls
6820      * A CSS class to apply to the button's main element.
6821      */
6822     
6823     /**
6824      * @cfg {Roo.Template} template (Optional)
6825      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6826      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6827      * require code modifications if required elements (e.g. a button) aren't present.
6828      */
6829
6830     // private
6831     render : function(renderTo){
6832         var btn;
6833         if(this.hideParent){
6834             this.parentEl = Roo.get(renderTo);
6835         }
6836         if(!this.dhconfig){
6837             if(!this.template){
6838                 if(!Roo.Button.buttonTemplate){
6839                     // hideous table template
6840                     Roo.Button.buttonTemplate = new Roo.Template(
6841                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6842                         '<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>',
6843                         "</tr></tbody></table>");
6844                 }
6845                 this.template = Roo.Button.buttonTemplate;
6846             }
6847             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6848             var btnEl = btn.child("button:first");
6849             btnEl.on('focus', this.onFocus, this);
6850             btnEl.on('blur', this.onBlur, this);
6851             if(this.cls){
6852                 btn.addClass(this.cls);
6853             }
6854             if(this.icon){
6855                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6856             }
6857             if(this.iconCls){
6858                 btnEl.addClass(this.iconCls);
6859                 if(!this.cls){
6860                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6861                 }
6862             }
6863             if(this.tabIndex !== undefined){
6864                 btnEl.dom.tabIndex = this.tabIndex;
6865             }
6866             if(this.tooltip){
6867                 if(typeof this.tooltip == 'object'){
6868                     Roo.QuickTips.tips(Roo.apply({
6869                           target: btnEl.id
6870                     }, this.tooltip));
6871                 } else {
6872                     btnEl.dom[this.tooltipType] = this.tooltip;
6873                 }
6874             }
6875         }else{
6876             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6877         }
6878         this.el = btn;
6879         if(this.id){
6880             this.el.dom.id = this.el.id = this.id;
6881         }
6882         if(this.menu){
6883             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6884             this.menu.on("show", this.onMenuShow, this);
6885             this.menu.on("hide", this.onMenuHide, this);
6886         }
6887         btn.addClass("x-btn");
6888         if(Roo.isIE && !Roo.isIE7){
6889             this.autoWidth.defer(1, this);
6890         }else{
6891             this.autoWidth();
6892         }
6893         if(this.handleMouseEvents){
6894             btn.on("mouseover", this.onMouseOver, this);
6895             btn.on("mouseout", this.onMouseOut, this);
6896             btn.on("mousedown", this.onMouseDown, this);
6897         }
6898         btn.on(this.clickEvent, this.onClick, this);
6899         //btn.on("mouseup", this.onMouseUp, this);
6900         if(this.hidden){
6901             this.hide();
6902         }
6903         if(this.disabled){
6904             this.disable();
6905         }
6906         Roo.ButtonToggleMgr.register(this);
6907         if(this.pressed){
6908             this.el.addClass("x-btn-pressed");
6909         }
6910         if(this.repeat){
6911             var repeater = new Roo.util.ClickRepeater(btn,
6912                 typeof this.repeat == "object" ? this.repeat : {}
6913             );
6914             repeater.on("click", this.onClick,  this);
6915         }
6916         
6917         this.fireEvent('render', this);
6918         
6919     },
6920     /**
6921      * Returns the button's underlying element
6922      * @return {Roo.Element} The element
6923      */
6924     getEl : function(){
6925         return this.el;  
6926     },
6927     
6928     /**
6929      * Destroys this Button and removes any listeners.
6930      */
6931     destroy : function(){
6932         Roo.ButtonToggleMgr.unregister(this);
6933         this.el.removeAllListeners();
6934         this.purgeListeners();
6935         this.el.remove();
6936     },
6937
6938     // private
6939     autoWidth : function(){
6940         if(this.el){
6941             this.el.setWidth("auto");
6942             if(Roo.isIE7 && Roo.isStrict){
6943                 var ib = this.el.child('button');
6944                 if(ib && ib.getWidth() > 20){
6945                     ib.clip();
6946                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6947                 }
6948             }
6949             if(this.minWidth){
6950                 if(this.hidden){
6951                     this.el.beginMeasure();
6952                 }
6953                 if(this.el.getWidth() < this.minWidth){
6954                     this.el.setWidth(this.minWidth);
6955                 }
6956                 if(this.hidden){
6957                     this.el.endMeasure();
6958                 }
6959             }
6960         }
6961     },
6962
6963     /**
6964      * Assigns this button's click handler
6965      * @param {Function} handler The function to call when the button is clicked
6966      * @param {Object} scope (optional) Scope for the function passed in
6967      */
6968     setHandler : function(handler, scope){
6969         this.handler = handler;
6970         this.scope = scope;  
6971     },
6972     
6973     /**
6974      * Sets this button's text
6975      * @param {String} text The button text
6976      */
6977     setText : function(text){
6978         this.text = text;
6979         if(this.el){
6980             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6981         }
6982         this.autoWidth();
6983     },
6984     
6985     /**
6986      * Gets the text for this button
6987      * @return {String} The button text
6988      */
6989     getText : function(){
6990         return this.text;  
6991     },
6992     
6993     /**
6994      * Show this button
6995      */
6996     show: function(){
6997         this.hidden = false;
6998         if(this.el){
6999             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7000         }
7001     },
7002     
7003     /**
7004      * Hide this button
7005      */
7006     hide: function(){
7007         this.hidden = true;
7008         if(this.el){
7009             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7010         }
7011     },
7012     
7013     /**
7014      * Convenience function for boolean show/hide
7015      * @param {Boolean} visible True to show, false to hide
7016      */
7017     setVisible: function(visible){
7018         if(visible) {
7019             this.show();
7020         }else{
7021             this.hide();
7022         }
7023     },
7024     
7025     /**
7026      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7027      * @param {Boolean} state (optional) Force a particular state
7028      */
7029     toggle : function(state){
7030         state = state === undefined ? !this.pressed : state;
7031         if(state != this.pressed){
7032             if(state){
7033                 this.el.addClass("x-btn-pressed");
7034                 this.pressed = true;
7035                 this.fireEvent("toggle", this, true);
7036             }else{
7037                 this.el.removeClass("x-btn-pressed");
7038                 this.pressed = false;
7039                 this.fireEvent("toggle", this, false);
7040             }
7041             if(this.toggleHandler){
7042                 this.toggleHandler.call(this.scope || this, this, state);
7043             }
7044         }
7045     },
7046     
7047     /**
7048      * Focus the button
7049      */
7050     focus : function(){
7051         this.el.child('button:first').focus();
7052     },
7053     
7054     /**
7055      * Disable this button
7056      */
7057     disable : function(){
7058         if(this.el){
7059             this.el.addClass("x-btn-disabled");
7060         }
7061         this.disabled = true;
7062     },
7063     
7064     /**
7065      * Enable this button
7066      */
7067     enable : function(){
7068         if(this.el){
7069             this.el.removeClass("x-btn-disabled");
7070         }
7071         this.disabled = false;
7072     },
7073
7074     /**
7075      * Convenience function for boolean enable/disable
7076      * @param {Boolean} enabled True to enable, false to disable
7077      */
7078     setDisabled : function(v){
7079         this[v !== true ? "enable" : "disable"]();
7080     },
7081
7082     // private
7083     onClick : function(e)
7084     {
7085         if(e){
7086             e.preventDefault();
7087         }
7088         if(e.button != 0){
7089             return;
7090         }
7091         if(!this.disabled){
7092             if(this.enableToggle){
7093                 this.toggle();
7094             }
7095             if(this.menu && !this.menu.isVisible()){
7096                 this.menu.show(this.el, this.menuAlign);
7097             }
7098             this.fireEvent("click", this, e);
7099             if(this.handler){
7100                 this.el.removeClass("x-btn-over");
7101                 this.handler.call(this.scope || this, this, e);
7102             }
7103         }
7104     },
7105     // private
7106     onMouseOver : function(e){
7107         if(!this.disabled){
7108             this.el.addClass("x-btn-over");
7109             this.fireEvent('mouseover', this, e);
7110         }
7111     },
7112     // private
7113     onMouseOut : function(e){
7114         if(!e.within(this.el,  true)){
7115             this.el.removeClass("x-btn-over");
7116             this.fireEvent('mouseout', this, e);
7117         }
7118     },
7119     // private
7120     onFocus : function(e){
7121         if(!this.disabled){
7122             this.el.addClass("x-btn-focus");
7123         }
7124     },
7125     // private
7126     onBlur : function(e){
7127         this.el.removeClass("x-btn-focus");
7128     },
7129     // private
7130     onMouseDown : function(e){
7131         if(!this.disabled && e.button == 0){
7132             this.el.addClass("x-btn-click");
7133             Roo.get(document).on('mouseup', this.onMouseUp, this);
7134         }
7135     },
7136     // private
7137     onMouseUp : function(e){
7138         if(e.button == 0){
7139             this.el.removeClass("x-btn-click");
7140             Roo.get(document).un('mouseup', this.onMouseUp, this);
7141         }
7142     },
7143     // private
7144     onMenuShow : function(e){
7145         this.el.addClass("x-btn-menu-active");
7146     },
7147     // private
7148     onMenuHide : function(e){
7149         this.el.removeClass("x-btn-menu-active");
7150     }   
7151 });
7152
7153 // Private utility class used by Button
7154 Roo.ButtonToggleMgr = function(){
7155    var groups = {};
7156    
7157    function toggleGroup(btn, state){
7158        if(state){
7159            var g = groups[btn.toggleGroup];
7160            for(var i = 0, l = g.length; i < l; i++){
7161                if(g[i] != btn){
7162                    g[i].toggle(false);
7163                }
7164            }
7165        }
7166    }
7167    
7168    return {
7169        register : function(btn){
7170            if(!btn.toggleGroup){
7171                return;
7172            }
7173            var g = groups[btn.toggleGroup];
7174            if(!g){
7175                g = groups[btn.toggleGroup] = [];
7176            }
7177            g.push(btn);
7178            btn.on("toggle", toggleGroup);
7179        },
7180        
7181        unregister : function(btn){
7182            if(!btn.toggleGroup){
7183                return;
7184            }
7185            var g = groups[btn.toggleGroup];
7186            if(g){
7187                g.remove(btn);
7188                btn.un("toggle", toggleGroup);
7189            }
7190        }
7191    };
7192 }();/*
7193  * Based on:
7194  * Ext JS Library 1.1.1
7195  * Copyright(c) 2006-2007, Ext JS, LLC.
7196  *
7197  * Originally Released Under LGPL - original licence link has changed is not relivant.
7198  *
7199  * Fork - LGPL
7200  * <script type="text/javascript">
7201  */
7202  
7203 /**
7204  * @class Roo.SplitButton
7205  * @extends Roo.Button
7206  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7207  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7208  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7209  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7210  * @cfg {String} arrowTooltip The title attribute of the arrow
7211  * @constructor
7212  * Create a new menu button
7213  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7214  * @param {Object} config The config object
7215  */
7216 Roo.SplitButton = function(renderTo, config){
7217     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7218     /**
7219      * @event arrowclick
7220      * Fires when this button's arrow is clicked
7221      * @param {SplitButton} this
7222      * @param {EventObject} e The click event
7223      */
7224     this.addEvents({"arrowclick":true});
7225 };
7226
7227 Roo.extend(Roo.SplitButton, Roo.Button, {
7228     render : function(renderTo){
7229         // this is one sweet looking template!
7230         var tpl = new Roo.Template(
7231             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7232             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7233             '<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>',
7234             "</tbody></table></td><td>",
7235             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7236             '<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>',
7237             "</tbody></table></td></tr></table>"
7238         );
7239         var btn = tpl.append(renderTo, [this.text, this.type], true);
7240         var btnEl = btn.child("button");
7241         if(this.cls){
7242             btn.addClass(this.cls);
7243         }
7244         if(this.icon){
7245             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7246         }
7247         if(this.iconCls){
7248             btnEl.addClass(this.iconCls);
7249             if(!this.cls){
7250                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7251             }
7252         }
7253         this.el = btn;
7254         if(this.handleMouseEvents){
7255             btn.on("mouseover", this.onMouseOver, this);
7256             btn.on("mouseout", this.onMouseOut, this);
7257             btn.on("mousedown", this.onMouseDown, this);
7258             btn.on("mouseup", this.onMouseUp, this);
7259         }
7260         btn.on(this.clickEvent, this.onClick, this);
7261         if(this.tooltip){
7262             if(typeof this.tooltip == 'object'){
7263                 Roo.QuickTips.tips(Roo.apply({
7264                       target: btnEl.id
7265                 }, this.tooltip));
7266             } else {
7267                 btnEl.dom[this.tooltipType] = this.tooltip;
7268             }
7269         }
7270         if(this.arrowTooltip){
7271             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7272         }
7273         if(this.hidden){
7274             this.hide();
7275         }
7276         if(this.disabled){
7277             this.disable();
7278         }
7279         if(this.pressed){
7280             this.el.addClass("x-btn-pressed");
7281         }
7282         if(Roo.isIE && !Roo.isIE7){
7283             this.autoWidth.defer(1, this);
7284         }else{
7285             this.autoWidth();
7286         }
7287         if(this.menu){
7288             this.menu.on("show", this.onMenuShow, this);
7289             this.menu.on("hide", this.onMenuHide, this);
7290         }
7291         this.fireEvent('render', this);
7292     },
7293
7294     // private
7295     autoWidth : function(){
7296         if(this.el){
7297             var tbl = this.el.child("table:first");
7298             var tbl2 = this.el.child("table:last");
7299             this.el.setWidth("auto");
7300             tbl.setWidth("auto");
7301             if(Roo.isIE7 && Roo.isStrict){
7302                 var ib = this.el.child('button:first');
7303                 if(ib && ib.getWidth() > 20){
7304                     ib.clip();
7305                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7306                 }
7307             }
7308             if(this.minWidth){
7309                 if(this.hidden){
7310                     this.el.beginMeasure();
7311                 }
7312                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7313                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7314                 }
7315                 if(this.hidden){
7316                     this.el.endMeasure();
7317                 }
7318             }
7319             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7320         } 
7321     },
7322     /**
7323      * Sets this button's click handler
7324      * @param {Function} handler The function to call when the button is clicked
7325      * @param {Object} scope (optional) Scope for the function passed above
7326      */
7327     setHandler : function(handler, scope){
7328         this.handler = handler;
7329         this.scope = scope;  
7330     },
7331     
7332     /**
7333      * Sets this button's arrow click handler
7334      * @param {Function} handler The function to call when the arrow is clicked
7335      * @param {Object} scope (optional) Scope for the function passed above
7336      */
7337     setArrowHandler : function(handler, scope){
7338         this.arrowHandler = handler;
7339         this.scope = scope;  
7340     },
7341     
7342     /**
7343      * Focus the button
7344      */
7345     focus : function(){
7346         if(this.el){
7347             this.el.child("button:first").focus();
7348         }
7349     },
7350
7351     // private
7352     onClick : function(e){
7353         e.preventDefault();
7354         if(!this.disabled){
7355             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7356                 if(this.menu && !this.menu.isVisible()){
7357                     this.menu.show(this.el, this.menuAlign);
7358                 }
7359                 this.fireEvent("arrowclick", this, e);
7360                 if(this.arrowHandler){
7361                     this.arrowHandler.call(this.scope || this, this, e);
7362                 }
7363             }else{
7364                 this.fireEvent("click", this, e);
7365                 if(this.handler){
7366                     this.handler.call(this.scope || this, this, e);
7367                 }
7368             }
7369         }
7370     },
7371     // private
7372     onMouseDown : function(e){
7373         if(!this.disabled){
7374             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7375         }
7376     },
7377     // private
7378     onMouseUp : function(e){
7379         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7380     }   
7381 });
7382
7383
7384 // backwards compat
7385 Roo.MenuButton = Roo.SplitButton;/*
7386  * Based on:
7387  * Ext JS Library 1.1.1
7388  * Copyright(c) 2006-2007, Ext JS, LLC.
7389  *
7390  * Originally Released Under LGPL - original licence link has changed is not relivant.
7391  *
7392  * Fork - LGPL
7393  * <script type="text/javascript">
7394  */
7395
7396 /**
7397  * @class Roo.Toolbar
7398  * Basic Toolbar class.
7399  * @constructor
7400  * Creates a new Toolbar
7401  * @param {Object} container The config object
7402  */ 
7403 Roo.Toolbar = function(container, buttons, config)
7404 {
7405     /// old consturctor format still supported..
7406     if(container instanceof Array){ // omit the container for later rendering
7407         buttons = container;
7408         config = buttons;
7409         container = null;
7410     }
7411     if (typeof(container) == 'object' && container.xtype) {
7412         config = container;
7413         container = config.container;
7414         buttons = config.buttons || []; // not really - use items!!
7415     }
7416     var xitems = [];
7417     if (config && config.items) {
7418         xitems = config.items;
7419         delete config.items;
7420     }
7421     Roo.apply(this, config);
7422     this.buttons = buttons;
7423     
7424     if(container){
7425         this.render(container);
7426     }
7427     this.xitems = xitems;
7428     Roo.each(xitems, function(b) {
7429         this.add(b);
7430     }, this);
7431     
7432 };
7433
7434 Roo.Toolbar.prototype = {
7435     /**
7436      * @cfg {Array} items
7437      * array of button configs or elements to add (will be converted to a MixedCollection)
7438      */
7439     
7440     /**
7441      * @cfg {String/HTMLElement/Element} container
7442      * The id or element that will contain the toolbar
7443      */
7444     // private
7445     render : function(ct){
7446         this.el = Roo.get(ct);
7447         if(this.cls){
7448             this.el.addClass(this.cls);
7449         }
7450         // using a table allows for vertical alignment
7451         // 100% width is needed by Safari...
7452         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7453         this.tr = this.el.child("tr", true);
7454         var autoId = 0;
7455         this.items = new Roo.util.MixedCollection(false, function(o){
7456             return o.id || ("item" + (++autoId));
7457         });
7458         if(this.buttons){
7459             this.add.apply(this, this.buttons);
7460             delete this.buttons;
7461         }
7462     },
7463
7464     /**
7465      * Adds element(s) to the toolbar -- this function takes a variable number of 
7466      * arguments of mixed type and adds them to the toolbar.
7467      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7468      * <ul>
7469      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7470      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7471      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7472      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7473      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7474      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7475      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7476      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7477      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7478      * </ul>
7479      * @param {Mixed} arg2
7480      * @param {Mixed} etc.
7481      */
7482     add : function(){
7483         var a = arguments, l = a.length;
7484         for(var i = 0; i < l; i++){
7485             this._add(a[i]);
7486         }
7487     },
7488     // private..
7489     _add : function(el) {
7490         
7491         if (el.xtype) {
7492             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7493         }
7494         
7495         if (el.applyTo){ // some kind of form field
7496             return this.addField(el);
7497         } 
7498         if (el.render){ // some kind of Toolbar.Item
7499             return this.addItem(el);
7500         }
7501         if (typeof el == "string"){ // string
7502             if(el == "separator" || el == "-"){
7503                 return this.addSeparator();
7504             }
7505             if (el == " "){
7506                 return this.addSpacer();
7507             }
7508             if(el == "->"){
7509                 return this.addFill();
7510             }
7511             return this.addText(el);
7512             
7513         }
7514         if(el.tagName){ // element
7515             return this.addElement(el);
7516         }
7517         if(typeof el == "object"){ // must be button config?
7518             return this.addButton(el);
7519         }
7520         // and now what?!?!
7521         return false;
7522         
7523     },
7524     
7525     /**
7526      * Add an Xtype element
7527      * @param {Object} xtype Xtype Object
7528      * @return {Object} created Object
7529      */
7530     addxtype : function(e){
7531         return this.add(e);  
7532     },
7533     
7534     /**
7535      * Returns the Element for this toolbar.
7536      * @return {Roo.Element}
7537      */
7538     getEl : function(){
7539         return this.el;  
7540     },
7541     
7542     /**
7543      * Adds a separator
7544      * @return {Roo.Toolbar.Item} The separator item
7545      */
7546     addSeparator : function(){
7547         return this.addItem(new Roo.Toolbar.Separator());
7548     },
7549
7550     /**
7551      * Adds a spacer element
7552      * @return {Roo.Toolbar.Spacer} The spacer item
7553      */
7554     addSpacer : function(){
7555         return this.addItem(new Roo.Toolbar.Spacer());
7556     },
7557
7558     /**
7559      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7560      * @return {Roo.Toolbar.Fill} The fill item
7561      */
7562     addFill : function(){
7563         return this.addItem(new Roo.Toolbar.Fill());
7564     },
7565
7566     /**
7567      * Adds any standard HTML element to the toolbar
7568      * @param {String/HTMLElement/Element} el The element or id of the element to add
7569      * @return {Roo.Toolbar.Item} The element's item
7570      */
7571     addElement : function(el){
7572         return this.addItem(new Roo.Toolbar.Item(el));
7573     },
7574     /**
7575      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7576      * @type Roo.util.MixedCollection  
7577      */
7578     items : false,
7579      
7580     /**
7581      * Adds any Toolbar.Item or subclass
7582      * @param {Roo.Toolbar.Item} item
7583      * @return {Roo.Toolbar.Item} The item
7584      */
7585     addItem : function(item){
7586         var td = this.nextBlock();
7587         item.render(td);
7588         this.items.add(item);
7589         return item;
7590     },
7591     
7592     /**
7593      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7594      * @param {Object/Array} config A button config or array of configs
7595      * @return {Roo.Toolbar.Button/Array}
7596      */
7597     addButton : function(config){
7598         if(config instanceof Array){
7599             var buttons = [];
7600             for(var i = 0, len = config.length; i < len; i++) {
7601                 buttons.push(this.addButton(config[i]));
7602             }
7603             return buttons;
7604         }
7605         var b = config;
7606         if(!(config instanceof Roo.Toolbar.Button)){
7607             b = config.split ?
7608                 new Roo.Toolbar.SplitButton(config) :
7609                 new Roo.Toolbar.Button(config);
7610         }
7611         var td = this.nextBlock();
7612         b.render(td);
7613         this.items.add(b);
7614         return b;
7615     },
7616     
7617     /**
7618      * Adds text to the toolbar
7619      * @param {String} text The text to add
7620      * @return {Roo.Toolbar.Item} The element's item
7621      */
7622     addText : function(text){
7623         return this.addItem(new Roo.Toolbar.TextItem(text));
7624     },
7625     
7626     /**
7627      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7628      * @param {Number} index The index where the item is to be inserted
7629      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7630      * @return {Roo.Toolbar.Button/Item}
7631      */
7632     insertButton : function(index, item){
7633         if(item instanceof Array){
7634             var buttons = [];
7635             for(var i = 0, len = item.length; i < len; i++) {
7636                buttons.push(this.insertButton(index + i, item[i]));
7637             }
7638             return buttons;
7639         }
7640         if (!(item instanceof Roo.Toolbar.Button)){
7641            item = new Roo.Toolbar.Button(item);
7642         }
7643         var td = document.createElement("td");
7644         this.tr.insertBefore(td, this.tr.childNodes[index]);
7645         item.render(td);
7646         this.items.insert(index, item);
7647         return item;
7648     },
7649     
7650     /**
7651      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7652      * @param {Object} config
7653      * @return {Roo.Toolbar.Item} The element's item
7654      */
7655     addDom : function(config, returnEl){
7656         var td = this.nextBlock();
7657         Roo.DomHelper.overwrite(td, config);
7658         var ti = new Roo.Toolbar.Item(td.firstChild);
7659         ti.render(td);
7660         this.items.add(ti);
7661         return ti;
7662     },
7663
7664     /**
7665      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7666      * @type Roo.util.MixedCollection  
7667      */
7668     fields : false,
7669     
7670     /**
7671      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7672      * Note: the field should not have been rendered yet. For a field that has already been
7673      * rendered, use {@link #addElement}.
7674      * @param {Roo.form.Field} field
7675      * @return {Roo.ToolbarItem}
7676      */
7677      
7678       
7679     addField : function(field) {
7680         if (!this.fields) {
7681             var autoId = 0;
7682             this.fields = new Roo.util.MixedCollection(false, function(o){
7683                 return o.id || ("item" + (++autoId));
7684             });
7685
7686         }
7687         
7688         var td = this.nextBlock();
7689         field.render(td);
7690         var ti = new Roo.Toolbar.Item(td.firstChild);
7691         ti.render(td);
7692         this.items.add(ti);
7693         this.fields.add(field);
7694         return ti;
7695     },
7696     /**
7697      * Hide the toolbar
7698      * @method hide
7699      */
7700      
7701       
7702     hide : function()
7703     {
7704         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7705         this.el.child('div').hide();
7706     },
7707     /**
7708      * Show the toolbar
7709      * @method show
7710      */
7711     show : function()
7712     {
7713         this.el.child('div').show();
7714     },
7715       
7716     // private
7717     nextBlock : function(){
7718         var td = document.createElement("td");
7719         this.tr.appendChild(td);
7720         return td;
7721     },
7722
7723     // private
7724     destroy : function(){
7725         if(this.items){ // rendered?
7726             Roo.destroy.apply(Roo, this.items.items);
7727         }
7728         if(this.fields){ // rendered?
7729             Roo.destroy.apply(Roo, this.fields.items);
7730         }
7731         Roo.Element.uncache(this.el, this.tr);
7732     }
7733 };
7734
7735 /**
7736  * @class Roo.Toolbar.Item
7737  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7738  * @constructor
7739  * Creates a new Item
7740  * @param {HTMLElement} el 
7741  */
7742 Roo.Toolbar.Item = function(el){
7743     var cfg = {};
7744     if (typeof (el.xtype) != 'undefined') {
7745         cfg = el;
7746         el = cfg.el;
7747     }
7748     
7749     this.el = Roo.getDom(el);
7750     this.id = Roo.id(this.el);
7751     this.hidden = false;
7752     
7753     this.addEvents({
7754          /**
7755              * @event render
7756              * Fires when the button is rendered
7757              * @param {Button} this
7758              */
7759         'render': true
7760     });
7761     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7762 };
7763 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7764 //Roo.Toolbar.Item.prototype = {
7765     
7766     /**
7767      * Get this item's HTML Element
7768      * @return {HTMLElement}
7769      */
7770     getEl : function(){
7771        return this.el;  
7772     },
7773
7774     // private
7775     render : function(td){
7776         
7777          this.td = td;
7778         td.appendChild(this.el);
7779         
7780         this.fireEvent('render', this);
7781     },
7782     
7783     /**
7784      * Removes and destroys this item.
7785      */
7786     destroy : function(){
7787         this.td.parentNode.removeChild(this.td);
7788     },
7789     
7790     /**
7791      * Shows this item.
7792      */
7793     show: function(){
7794         this.hidden = false;
7795         this.td.style.display = "";
7796     },
7797     
7798     /**
7799      * Hides this item.
7800      */
7801     hide: function(){
7802         this.hidden = true;
7803         this.td.style.display = "none";
7804     },
7805     
7806     /**
7807      * Convenience function for boolean show/hide.
7808      * @param {Boolean} visible true to show/false to hide
7809      */
7810     setVisible: function(visible){
7811         if(visible) {
7812             this.show();
7813         }else{
7814             this.hide();
7815         }
7816     },
7817     
7818     /**
7819      * Try to focus this item.
7820      */
7821     focus : function(){
7822         Roo.fly(this.el).focus();
7823     },
7824     
7825     /**
7826      * Disables this item.
7827      */
7828     disable : function(){
7829         Roo.fly(this.td).addClass("x-item-disabled");
7830         this.disabled = true;
7831         this.el.disabled = true;
7832     },
7833     
7834     /**
7835      * Enables this item.
7836      */
7837     enable : function(){
7838         Roo.fly(this.td).removeClass("x-item-disabled");
7839         this.disabled = false;
7840         this.el.disabled = false;
7841     }
7842 });
7843
7844
7845 /**
7846  * @class Roo.Toolbar.Separator
7847  * @extends Roo.Toolbar.Item
7848  * A simple toolbar separator class
7849  * @constructor
7850  * Creates a new Separator
7851  */
7852 Roo.Toolbar.Separator = function(cfg){
7853     
7854     var s = document.createElement("span");
7855     s.className = "ytb-sep";
7856     if (cfg) {
7857         cfg.el = s;
7858     }
7859     
7860     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7861 };
7862 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7863     enable:Roo.emptyFn,
7864     disable:Roo.emptyFn,
7865     focus:Roo.emptyFn
7866 });
7867
7868 /**
7869  * @class Roo.Toolbar.Spacer
7870  * @extends Roo.Toolbar.Item
7871  * A simple element that adds extra horizontal space to a toolbar.
7872  * @constructor
7873  * Creates a new Spacer
7874  */
7875 Roo.Toolbar.Spacer = function(cfg){
7876     var s = document.createElement("div");
7877     s.className = "ytb-spacer";
7878     if (cfg) {
7879         cfg.el = s;
7880     }
7881     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7882 };
7883 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7884     enable:Roo.emptyFn,
7885     disable:Roo.emptyFn,
7886     focus:Roo.emptyFn
7887 });
7888
7889 /**
7890  * @class Roo.Toolbar.Fill
7891  * @extends Roo.Toolbar.Spacer
7892  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7893  * @constructor
7894  * Creates a new Spacer
7895  */
7896 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7897     // private
7898     render : function(td){
7899         td.style.width = '100%';
7900         Roo.Toolbar.Fill.superclass.render.call(this, td);
7901     }
7902 });
7903
7904 /**
7905  * @class Roo.Toolbar.TextItem
7906  * @extends Roo.Toolbar.Item
7907  * A simple class that renders text directly into a toolbar.
7908  * @constructor
7909  * Creates a new TextItem
7910  * @param {String} text
7911  */
7912 Roo.Toolbar.TextItem = function(cfg){
7913     var  text = cfg || "";
7914     if (typeof(cfg) == 'object') {
7915         text = cfg.text || "";
7916     }  else {
7917         cfg = null;
7918     }
7919     var s = document.createElement("span");
7920     s.className = "ytb-text";
7921     s.innerHTML = text;
7922     if (cfg) {
7923         cfg.el  = s;
7924     }
7925     
7926     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7927 };
7928 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7929     
7930      
7931     enable:Roo.emptyFn,
7932     disable:Roo.emptyFn,
7933     focus:Roo.emptyFn
7934 });
7935
7936 /**
7937  * @class Roo.Toolbar.Button
7938  * @extends Roo.Button
7939  * A button that renders into a toolbar.
7940  * @constructor
7941  * Creates a new Button
7942  * @param {Object} config A standard {@link Roo.Button} config object
7943  */
7944 Roo.Toolbar.Button = function(config){
7945     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7946 };
7947 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7948     render : function(td){
7949         this.td = td;
7950         Roo.Toolbar.Button.superclass.render.call(this, td);
7951     },
7952     
7953     /**
7954      * Removes and destroys this button
7955      */
7956     destroy : function(){
7957         Roo.Toolbar.Button.superclass.destroy.call(this);
7958         this.td.parentNode.removeChild(this.td);
7959     },
7960     
7961     /**
7962      * Shows this button
7963      */
7964     show: function(){
7965         this.hidden = false;
7966         this.td.style.display = "";
7967     },
7968     
7969     /**
7970      * Hides this button
7971      */
7972     hide: function(){
7973         this.hidden = true;
7974         this.td.style.display = "none";
7975     },
7976
7977     /**
7978      * Disables this item
7979      */
7980     disable : function(){
7981         Roo.fly(this.td).addClass("x-item-disabled");
7982         this.disabled = true;
7983     },
7984
7985     /**
7986      * Enables this item
7987      */
7988     enable : function(){
7989         Roo.fly(this.td).removeClass("x-item-disabled");
7990         this.disabled = false;
7991     }
7992 });
7993 // backwards compat
7994 Roo.ToolbarButton = Roo.Toolbar.Button;
7995
7996 /**
7997  * @class Roo.Toolbar.SplitButton
7998  * @extends Roo.SplitButton
7999  * A menu button that renders into a toolbar.
8000  * @constructor
8001  * Creates a new SplitButton
8002  * @param {Object} config A standard {@link Roo.SplitButton} config object
8003  */
8004 Roo.Toolbar.SplitButton = function(config){
8005     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8006 };
8007 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8008     render : function(td){
8009         this.td = td;
8010         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8011     },
8012     
8013     /**
8014      * Removes and destroys this button
8015      */
8016     destroy : function(){
8017         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8018         this.td.parentNode.removeChild(this.td);
8019     },
8020     
8021     /**
8022      * Shows this button
8023      */
8024     show: function(){
8025         this.hidden = false;
8026         this.td.style.display = "";
8027     },
8028     
8029     /**
8030      * Hides this button
8031      */
8032     hide: function(){
8033         this.hidden = true;
8034         this.td.style.display = "none";
8035     }
8036 });
8037
8038 // backwards compat
8039 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8040  * Based on:
8041  * Ext JS Library 1.1.1
8042  * Copyright(c) 2006-2007, Ext JS, LLC.
8043  *
8044  * Originally Released Under LGPL - original licence link has changed is not relivant.
8045  *
8046  * Fork - LGPL
8047  * <script type="text/javascript">
8048  */
8049  
8050 /**
8051  * @class Roo.PagingToolbar
8052  * @extends Roo.Toolbar
8053  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8054  * @constructor
8055  * Create a new PagingToolbar
8056  * @param {Object} config The config object
8057  */
8058 Roo.PagingToolbar = function(el, ds, config)
8059 {
8060     // old args format still supported... - xtype is prefered..
8061     if (typeof(el) == 'object' && el.xtype) {
8062         // created from xtype...
8063         config = el;
8064         ds = el.dataSource;
8065         el = config.container;
8066     }
8067     var items = [];
8068     if (config.items) {
8069         items = config.items;
8070         config.items = [];
8071     }
8072     
8073     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8074     this.ds = ds;
8075     this.cursor = 0;
8076     this.renderButtons(this.el);
8077     this.bind(ds);
8078     
8079     // supprot items array.
8080    
8081     Roo.each(items, function(e) {
8082         this.add(Roo.factory(e));
8083     },this);
8084     
8085 };
8086
8087 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8088     /**
8089      * @cfg {Roo.data.Store} dataSource
8090      * The underlying data store providing the paged data
8091      */
8092     /**
8093      * @cfg {String/HTMLElement/Element} container
8094      * container The id or element that will contain the toolbar
8095      */
8096     /**
8097      * @cfg {Boolean} displayInfo
8098      * True to display the displayMsg (defaults to false)
8099      */
8100     /**
8101      * @cfg {Number} pageSize
8102      * The number of records to display per page (defaults to 20)
8103      */
8104     pageSize: 20,
8105     /**
8106      * @cfg {String} displayMsg
8107      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8108      */
8109     displayMsg : 'Displaying {0} - {1} of {2}',
8110     /**
8111      * @cfg {String} emptyMsg
8112      * The message to display when no records are found (defaults to "No data to display")
8113      */
8114     emptyMsg : 'No data to display',
8115     /**
8116      * Customizable piece of the default paging text (defaults to "Page")
8117      * @type String
8118      */
8119     beforePageText : "Page",
8120     /**
8121      * Customizable piece of the default paging text (defaults to "of %0")
8122      * @type String
8123      */
8124     afterPageText : "of {0}",
8125     /**
8126      * Customizable piece of the default paging text (defaults to "First Page")
8127      * @type String
8128      */
8129     firstText : "First Page",
8130     /**
8131      * Customizable piece of the default paging text (defaults to "Previous Page")
8132      * @type String
8133      */
8134     prevText : "Previous Page",
8135     /**
8136      * Customizable piece of the default paging text (defaults to "Next Page")
8137      * @type String
8138      */
8139     nextText : "Next Page",
8140     /**
8141      * Customizable piece of the default paging text (defaults to "Last Page")
8142      * @type String
8143      */
8144     lastText : "Last Page",
8145     /**
8146      * Customizable piece of the default paging text (defaults to "Refresh")
8147      * @type String
8148      */
8149     refreshText : "Refresh",
8150
8151     // private
8152     renderButtons : function(el){
8153         Roo.PagingToolbar.superclass.render.call(this, el);
8154         this.first = this.addButton({
8155             tooltip: this.firstText,
8156             cls: "x-btn-icon x-grid-page-first",
8157             disabled: true,
8158             handler: this.onClick.createDelegate(this, ["first"])
8159         });
8160         this.prev = this.addButton({
8161             tooltip: this.prevText,
8162             cls: "x-btn-icon x-grid-page-prev",
8163             disabled: true,
8164             handler: this.onClick.createDelegate(this, ["prev"])
8165         });
8166         //this.addSeparator();
8167         this.add(this.beforePageText);
8168         this.field = Roo.get(this.addDom({
8169            tag: "input",
8170            type: "text",
8171            size: "3",
8172            value: "1",
8173            cls: "x-grid-page-number"
8174         }).el);
8175         this.field.on("keydown", this.onPagingKeydown, this);
8176         this.field.on("focus", function(){this.dom.select();});
8177         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8178         this.field.setHeight(18);
8179         //this.addSeparator();
8180         this.next = this.addButton({
8181             tooltip: this.nextText,
8182             cls: "x-btn-icon x-grid-page-next",
8183             disabled: true,
8184             handler: this.onClick.createDelegate(this, ["next"])
8185         });
8186         this.last = this.addButton({
8187             tooltip: this.lastText,
8188             cls: "x-btn-icon x-grid-page-last",
8189             disabled: true,
8190             handler: this.onClick.createDelegate(this, ["last"])
8191         });
8192         //this.addSeparator();
8193         this.loading = this.addButton({
8194             tooltip: this.refreshText,
8195             cls: "x-btn-icon x-grid-loading",
8196             handler: this.onClick.createDelegate(this, ["refresh"])
8197         });
8198
8199         if(this.displayInfo){
8200             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8201         }
8202     },
8203
8204     // private
8205     updateInfo : function(){
8206         if(this.displayEl){
8207             var count = this.ds.getCount();
8208             var msg = count == 0 ?
8209                 this.emptyMsg :
8210                 String.format(
8211                     this.displayMsg,
8212                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8213                 );
8214             this.displayEl.update(msg);
8215         }
8216     },
8217
8218     // private
8219     onLoad : function(ds, r, o){
8220        this.cursor = o.params ? o.params.start : 0;
8221        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8222
8223        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8224        this.field.dom.value = ap;
8225        this.first.setDisabled(ap == 1);
8226        this.prev.setDisabled(ap == 1);
8227        this.next.setDisabled(ap == ps);
8228        this.last.setDisabled(ap == ps);
8229        this.loading.enable();
8230        this.updateInfo();
8231     },
8232
8233     // private
8234     getPageData : function(){
8235         var total = this.ds.getTotalCount();
8236         return {
8237             total : total,
8238             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8239             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8240         };
8241     },
8242
8243     // private
8244     onLoadError : function(){
8245         this.loading.enable();
8246     },
8247
8248     // private
8249     onPagingKeydown : function(e){
8250         var k = e.getKey();
8251         var d = this.getPageData();
8252         if(k == e.RETURN){
8253             var v = this.field.dom.value, pageNum;
8254             if(!v || isNaN(pageNum = parseInt(v, 10))){
8255                 this.field.dom.value = d.activePage;
8256                 return;
8257             }
8258             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8259             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8260             e.stopEvent();
8261         }
8262         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))
8263         {
8264           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8265           this.field.dom.value = pageNum;
8266           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8267           e.stopEvent();
8268         }
8269         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8270         {
8271           var v = this.field.dom.value, pageNum; 
8272           var increment = (e.shiftKey) ? 10 : 1;
8273           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8274             increment *= -1;
8275           }
8276           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8277             this.field.dom.value = d.activePage;
8278             return;
8279           }
8280           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8281           {
8282             this.field.dom.value = parseInt(v, 10) + increment;
8283             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8284             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8285           }
8286           e.stopEvent();
8287         }
8288     },
8289
8290     // private
8291     beforeLoad : function(){
8292         if(this.loading){
8293             this.loading.disable();
8294         }
8295     },
8296
8297     // private
8298     onClick : function(which){
8299         var ds = this.ds;
8300         switch(which){
8301             case "first":
8302                 ds.load({params:{start: 0, limit: this.pageSize}});
8303             break;
8304             case "prev":
8305                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8306             break;
8307             case "next":
8308                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8309             break;
8310             case "last":
8311                 var total = ds.getTotalCount();
8312                 var extra = total % this.pageSize;
8313                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8314                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8315             break;
8316             case "refresh":
8317                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8318             break;
8319         }
8320     },
8321
8322     /**
8323      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8324      * @param {Roo.data.Store} store The data store to unbind
8325      */
8326     unbind : function(ds){
8327         ds.un("beforeload", this.beforeLoad, this);
8328         ds.un("load", this.onLoad, this);
8329         ds.un("loadexception", this.onLoadError, this);
8330         ds.un("remove", this.updateInfo, this);
8331         ds.un("add", this.updateInfo, this);
8332         this.ds = undefined;
8333     },
8334
8335     /**
8336      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8337      * @param {Roo.data.Store} store The data store to bind
8338      */
8339     bind : function(ds){
8340         ds.on("beforeload", this.beforeLoad, this);
8341         ds.on("load", this.onLoad, this);
8342         ds.on("loadexception", this.onLoadError, this);
8343         ds.on("remove", this.updateInfo, this);
8344         ds.on("add", this.updateInfo, this);
8345         this.ds = ds;
8346     }
8347 });/*
8348  * Based on:
8349  * Ext JS Library 1.1.1
8350  * Copyright(c) 2006-2007, Ext JS, LLC.
8351  *
8352  * Originally Released Under LGPL - original licence link has changed is not relivant.
8353  *
8354  * Fork - LGPL
8355  * <script type="text/javascript">
8356  */
8357
8358 /**
8359  * @class Roo.Resizable
8360  * @extends Roo.util.Observable
8361  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8362  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8363  * 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
8364  * the element will be wrapped for you automatically.</p>
8365  * <p>Here is the list of valid resize handles:</p>
8366  * <pre>
8367 Value   Description
8368 ------  -------------------
8369  'n'     north
8370  's'     south
8371  'e'     east
8372  'w'     west
8373  'nw'    northwest
8374  'sw'    southwest
8375  'se'    southeast
8376  'ne'    northeast
8377  'hd'    horizontal drag
8378  'all'   all
8379 </pre>
8380  * <p>Here's an example showing the creation of a typical Resizable:</p>
8381  * <pre><code>
8382 var resizer = new Roo.Resizable("element-id", {
8383     handles: 'all',
8384     minWidth: 200,
8385     minHeight: 100,
8386     maxWidth: 500,
8387     maxHeight: 400,
8388     pinned: true
8389 });
8390 resizer.on("resize", myHandler);
8391 </code></pre>
8392  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8393  * resizer.east.setDisplayed(false);</p>
8394  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8395  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8396  * resize operation's new size (defaults to [0, 0])
8397  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8398  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8399  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8400  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8401  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8402  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8403  * @cfg {Number} width The width of the element in pixels (defaults to null)
8404  * @cfg {Number} height The height of the element in pixels (defaults to null)
8405  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8406  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8407  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8408  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8409  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8410  * in favor of the handles config option (defaults to false)
8411  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8412  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8413  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8414  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8415  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8416  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8417  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8418  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8419  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8420  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8421  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8422  * @constructor
8423  * Create a new resizable component
8424  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8425  * @param {Object} config configuration options
8426   */
8427 Roo.Resizable = function(el, config)
8428 {
8429     this.el = Roo.get(el);
8430
8431     if(config && config.wrap){
8432         config.resizeChild = this.el;
8433         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8434         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8435         this.el.setStyle("overflow", "hidden");
8436         this.el.setPositioning(config.resizeChild.getPositioning());
8437         config.resizeChild.clearPositioning();
8438         if(!config.width || !config.height){
8439             var csize = config.resizeChild.getSize();
8440             this.el.setSize(csize.width, csize.height);
8441         }
8442         if(config.pinned && !config.adjustments){
8443             config.adjustments = "auto";
8444         }
8445     }
8446
8447     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8448     this.proxy.unselectable();
8449     this.proxy.enableDisplayMode('block');
8450
8451     Roo.apply(this, config);
8452
8453     if(this.pinned){
8454         this.disableTrackOver = true;
8455         this.el.addClass("x-resizable-pinned");
8456     }
8457     // if the element isn't positioned, make it relative
8458     var position = this.el.getStyle("position");
8459     if(position != "absolute" && position != "fixed"){
8460         this.el.setStyle("position", "relative");
8461     }
8462     if(!this.handles){ // no handles passed, must be legacy style
8463         this.handles = 's,e,se';
8464         if(this.multiDirectional){
8465             this.handles += ',n,w';
8466         }
8467     }
8468     if(this.handles == "all"){
8469         this.handles = "n s e w ne nw se sw";
8470     }
8471     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8472     var ps = Roo.Resizable.positions;
8473     for(var i = 0, len = hs.length; i < len; i++){
8474         if(hs[i] && ps[hs[i]]){
8475             var pos = ps[hs[i]];
8476             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8477         }
8478     }
8479     // legacy
8480     this.corner = this.southeast;
8481     
8482     // updateBox = the box can move..
8483     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8484         this.updateBox = true;
8485     }
8486
8487     this.activeHandle = null;
8488
8489     if(this.resizeChild){
8490         if(typeof this.resizeChild == "boolean"){
8491             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8492         }else{
8493             this.resizeChild = Roo.get(this.resizeChild, true);
8494         }
8495     }
8496     
8497     if(this.adjustments == "auto"){
8498         var rc = this.resizeChild;
8499         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8500         if(rc && (hw || hn)){
8501             rc.position("relative");
8502             rc.setLeft(hw ? hw.el.getWidth() : 0);
8503             rc.setTop(hn ? hn.el.getHeight() : 0);
8504         }
8505         this.adjustments = [
8506             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8507             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8508         ];
8509     }
8510
8511     if(this.draggable){
8512         this.dd = this.dynamic ?
8513             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8514         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8515     }
8516
8517     // public events
8518     this.addEvents({
8519         /**
8520          * @event beforeresize
8521          * Fired before resize is allowed. Set enabled to false to cancel resize.
8522          * @param {Roo.Resizable} this
8523          * @param {Roo.EventObject} e The mousedown event
8524          */
8525         "beforeresize" : true,
8526         /**
8527          * @event resizing
8528          * Fired a resizing.
8529          * @param {Roo.Resizable} this
8530          * @param {Number} x The new x position
8531          * @param {Number} y The new y position
8532          * @param {Number} w The new w width
8533          * @param {Number} h The new h hight
8534          * @param {Roo.EventObject} e The mouseup event
8535          */
8536         "resizing" : true,
8537         /**
8538          * @event resize
8539          * Fired after a resize.
8540          * @param {Roo.Resizable} this
8541          * @param {Number} width The new width
8542          * @param {Number} height The new height
8543          * @param {Roo.EventObject} e The mouseup event
8544          */
8545         "resize" : true
8546     });
8547
8548     if(this.width !== null && this.height !== null){
8549         this.resizeTo(this.width, this.height);
8550     }else{
8551         this.updateChildSize();
8552     }
8553     if(Roo.isIE){
8554         this.el.dom.style.zoom = 1;
8555     }
8556     Roo.Resizable.superclass.constructor.call(this);
8557 };
8558
8559 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8560         resizeChild : false,
8561         adjustments : [0, 0],
8562         minWidth : 5,
8563         minHeight : 5,
8564         maxWidth : 10000,
8565         maxHeight : 10000,
8566         enabled : true,
8567         animate : false,
8568         duration : .35,
8569         dynamic : false,
8570         handles : false,
8571         multiDirectional : false,
8572         disableTrackOver : false,
8573         easing : 'easeOutStrong',
8574         widthIncrement : 0,
8575         heightIncrement : 0,
8576         pinned : false,
8577         width : null,
8578         height : null,
8579         preserveRatio : false,
8580         transparent: false,
8581         minX: 0,
8582         minY: 0,
8583         draggable: false,
8584
8585         /**
8586          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8587          */
8588         constrainTo: undefined,
8589         /**
8590          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8591          */
8592         resizeRegion: undefined,
8593
8594
8595     /**
8596      * Perform a manual resize
8597      * @param {Number} width
8598      * @param {Number} height
8599      */
8600     resizeTo : function(width, height){
8601         this.el.setSize(width, height);
8602         this.updateChildSize();
8603         this.fireEvent("resize", this, width, height, null);
8604     },
8605
8606     // private
8607     startSizing : function(e, handle){
8608         this.fireEvent("beforeresize", this, e);
8609         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8610
8611             if(!this.overlay){
8612                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8613                 this.overlay.unselectable();
8614                 this.overlay.enableDisplayMode("block");
8615                 this.overlay.on("mousemove", this.onMouseMove, this);
8616                 this.overlay.on("mouseup", this.onMouseUp, this);
8617             }
8618             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8619
8620             this.resizing = true;
8621             this.startBox = this.el.getBox();
8622             this.startPoint = e.getXY();
8623             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8624                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8625
8626             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8627             this.overlay.show();
8628
8629             if(this.constrainTo) {
8630                 var ct = Roo.get(this.constrainTo);
8631                 this.resizeRegion = ct.getRegion().adjust(
8632                     ct.getFrameWidth('t'),
8633                     ct.getFrameWidth('l'),
8634                     -ct.getFrameWidth('b'),
8635                     -ct.getFrameWidth('r')
8636                 );
8637             }
8638
8639             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8640             this.proxy.show();
8641             this.proxy.setBox(this.startBox);
8642             if(!this.dynamic){
8643                 this.proxy.setStyle('visibility', 'visible');
8644             }
8645         }
8646     },
8647
8648     // private
8649     onMouseDown : function(handle, e){
8650         if(this.enabled){
8651             e.stopEvent();
8652             this.activeHandle = handle;
8653             this.startSizing(e, handle);
8654         }
8655     },
8656
8657     // private
8658     onMouseUp : function(e){
8659         var size = this.resizeElement();
8660         this.resizing = false;
8661         this.handleOut();
8662         this.overlay.hide();
8663         this.proxy.hide();
8664         this.fireEvent("resize", this, size.width, size.height, e);
8665     },
8666
8667     // private
8668     updateChildSize : function(){
8669         
8670         if(this.resizeChild){
8671             var el = this.el;
8672             var child = this.resizeChild;
8673             var adj = this.adjustments;
8674             if(el.dom.offsetWidth){
8675                 var b = el.getSize(true);
8676                 child.setSize(b.width+adj[0], b.height+adj[1]);
8677             }
8678             // Second call here for IE
8679             // The first call enables instant resizing and
8680             // the second call corrects scroll bars if they
8681             // exist
8682             if(Roo.isIE){
8683                 setTimeout(function(){
8684                     if(el.dom.offsetWidth){
8685                         var b = el.getSize(true);
8686                         child.setSize(b.width+adj[0], b.height+adj[1]);
8687                     }
8688                 }, 10);
8689             }
8690         }
8691     },
8692
8693     // private
8694     snap : function(value, inc, min){
8695         if(!inc || !value) {
8696             return value;
8697         }
8698         var newValue = value;
8699         var m = value % inc;
8700         if(m > 0){
8701             if(m > (inc/2)){
8702                 newValue = value + (inc-m);
8703             }else{
8704                 newValue = value - m;
8705             }
8706         }
8707         return Math.max(min, newValue);
8708     },
8709
8710     // private
8711     resizeElement : function(){
8712         var box = this.proxy.getBox();
8713         if(this.updateBox){
8714             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8715         }else{
8716             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8717         }
8718         this.updateChildSize();
8719         if(!this.dynamic){
8720             this.proxy.hide();
8721         }
8722         return box;
8723     },
8724
8725     // private
8726     constrain : function(v, diff, m, mx){
8727         if(v - diff < m){
8728             diff = v - m;
8729         }else if(v - diff > mx){
8730             diff = mx - v;
8731         }
8732         return diff;
8733     },
8734
8735     // private
8736     onMouseMove : function(e){
8737         
8738         if(this.enabled){
8739             try{// try catch so if something goes wrong the user doesn't get hung
8740
8741             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8742                 return;
8743             }
8744
8745             //var curXY = this.startPoint;
8746             var curSize = this.curSize || this.startBox;
8747             var x = this.startBox.x, y = this.startBox.y;
8748             var ox = x, oy = y;
8749             var w = curSize.width, h = curSize.height;
8750             var ow = w, oh = h;
8751             var mw = this.minWidth, mh = this.minHeight;
8752             var mxw = this.maxWidth, mxh = this.maxHeight;
8753             var wi = this.widthIncrement;
8754             var hi = this.heightIncrement;
8755
8756             var eventXY = e.getXY();
8757             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8758             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8759
8760             var pos = this.activeHandle.position;
8761
8762             switch(pos){
8763                 case "east":
8764                     w += diffX;
8765                     w = Math.min(Math.max(mw, w), mxw);
8766                     break;
8767              
8768                 case "south":
8769                     h += diffY;
8770                     h = Math.min(Math.max(mh, h), mxh);
8771                     break;
8772                 case "southeast":
8773                     w += diffX;
8774                     h += diffY;
8775                     w = Math.min(Math.max(mw, w), mxw);
8776                     h = Math.min(Math.max(mh, h), mxh);
8777                     break;
8778                 case "north":
8779                     diffY = this.constrain(h, diffY, mh, mxh);
8780                     y += diffY;
8781                     h -= diffY;
8782                     break;
8783                 case "hdrag":
8784                     
8785                     if (wi) {
8786                         var adiffX = Math.abs(diffX);
8787                         var sub = (adiffX % wi); // how much 
8788                         if (sub > (wi/2)) { // far enough to snap
8789                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8790                         } else {
8791                             // remove difference.. 
8792                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8793                         }
8794                     }
8795                     x += diffX;
8796                     x = Math.max(this.minX, x);
8797                     break;
8798                 case "west":
8799                     diffX = this.constrain(w, diffX, mw, mxw);
8800                     x += diffX;
8801                     w -= diffX;
8802                     break;
8803                 case "northeast":
8804                     w += diffX;
8805                     w = Math.min(Math.max(mw, w), mxw);
8806                     diffY = this.constrain(h, diffY, mh, mxh);
8807                     y += diffY;
8808                     h -= diffY;
8809                     break;
8810                 case "northwest":
8811                     diffX = this.constrain(w, diffX, mw, mxw);
8812                     diffY = this.constrain(h, diffY, mh, mxh);
8813                     y += diffY;
8814                     h -= diffY;
8815                     x += diffX;
8816                     w -= diffX;
8817                     break;
8818                case "southwest":
8819                     diffX = this.constrain(w, diffX, mw, mxw);
8820                     h += diffY;
8821                     h = Math.min(Math.max(mh, h), mxh);
8822                     x += diffX;
8823                     w -= diffX;
8824                     break;
8825             }
8826
8827             var sw = this.snap(w, wi, mw);
8828             var sh = this.snap(h, hi, mh);
8829             if(sw != w || sh != h){
8830                 switch(pos){
8831                     case "northeast":
8832                         y -= sh - h;
8833                     break;
8834                     case "north":
8835                         y -= sh - h;
8836                         break;
8837                     case "southwest":
8838                         x -= sw - w;
8839                     break;
8840                     case "west":
8841                         x -= sw - w;
8842                         break;
8843                     case "northwest":
8844                         x -= sw - w;
8845                         y -= sh - h;
8846                     break;
8847                 }
8848                 w = sw;
8849                 h = sh;
8850             }
8851
8852             if(this.preserveRatio){
8853                 switch(pos){
8854                     case "southeast":
8855                     case "east":
8856                         h = oh * (w/ow);
8857                         h = Math.min(Math.max(mh, h), mxh);
8858                         w = ow * (h/oh);
8859                        break;
8860                     case "south":
8861                         w = ow * (h/oh);
8862                         w = Math.min(Math.max(mw, w), mxw);
8863                         h = oh * (w/ow);
8864                         break;
8865                     case "northeast":
8866                         w = ow * (h/oh);
8867                         w = Math.min(Math.max(mw, w), mxw);
8868                         h = oh * (w/ow);
8869                     break;
8870                     case "north":
8871                         var tw = w;
8872                         w = ow * (h/oh);
8873                         w = Math.min(Math.max(mw, w), mxw);
8874                         h = oh * (w/ow);
8875                         x += (tw - w) / 2;
8876                         break;
8877                     case "southwest":
8878                         h = oh * (w/ow);
8879                         h = Math.min(Math.max(mh, h), mxh);
8880                         var tw = w;
8881                         w = ow * (h/oh);
8882                         x += tw - w;
8883                         break;
8884                     case "west":
8885                         var th = h;
8886                         h = oh * (w/ow);
8887                         h = Math.min(Math.max(mh, h), mxh);
8888                         y += (th - h) / 2;
8889                         var tw = w;
8890                         w = ow * (h/oh);
8891                         x += tw - w;
8892                        break;
8893                     case "northwest":
8894                         var tw = w;
8895                         var th = h;
8896                         h = oh * (w/ow);
8897                         h = Math.min(Math.max(mh, h), mxh);
8898                         w = ow * (h/oh);
8899                         y += th - h;
8900                         x += tw - w;
8901                        break;
8902
8903                 }
8904             }
8905             if (pos == 'hdrag') {
8906                 w = ow;
8907             }
8908             this.proxy.setBounds(x, y, w, h);
8909             if(this.dynamic){
8910                 this.resizeElement();
8911             }
8912             }catch(e){}
8913         }
8914         this.fireEvent("resizing", this, x, y, w, h, e);
8915     },
8916
8917     // private
8918     handleOver : function(){
8919         if(this.enabled){
8920             this.el.addClass("x-resizable-over");
8921         }
8922     },
8923
8924     // private
8925     handleOut : function(){
8926         if(!this.resizing){
8927             this.el.removeClass("x-resizable-over");
8928         }
8929     },
8930
8931     /**
8932      * Returns the element this component is bound to.
8933      * @return {Roo.Element}
8934      */
8935     getEl : function(){
8936         return this.el;
8937     },
8938
8939     /**
8940      * Returns the resizeChild element (or null).
8941      * @return {Roo.Element}
8942      */
8943     getResizeChild : function(){
8944         return this.resizeChild;
8945     },
8946     groupHandler : function()
8947     {
8948         
8949     },
8950     /**
8951      * Destroys this resizable. If the element was wrapped and
8952      * removeEl is not true then the element remains.
8953      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8954      */
8955     destroy : function(removeEl){
8956         this.proxy.remove();
8957         if(this.overlay){
8958             this.overlay.removeAllListeners();
8959             this.overlay.remove();
8960         }
8961         var ps = Roo.Resizable.positions;
8962         for(var k in ps){
8963             if(typeof ps[k] != "function" && this[ps[k]]){
8964                 var h = this[ps[k]];
8965                 h.el.removeAllListeners();
8966                 h.el.remove();
8967             }
8968         }
8969         if(removeEl){
8970             this.el.update("");
8971             this.el.remove();
8972         }
8973     }
8974 });
8975
8976 // private
8977 // hash to map config positions to true positions
8978 Roo.Resizable.positions = {
8979     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8980     hd: "hdrag"
8981 };
8982
8983 // private
8984 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8985     if(!this.tpl){
8986         // only initialize the template if resizable is used
8987         var tpl = Roo.DomHelper.createTemplate(
8988             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8989         );
8990         tpl.compile();
8991         Roo.Resizable.Handle.prototype.tpl = tpl;
8992     }
8993     this.position = pos;
8994     this.rz = rz;
8995     // show north drag fro topdra
8996     var handlepos = pos == 'hdrag' ? 'north' : pos;
8997     
8998     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8999     if (pos == 'hdrag') {
9000         this.el.setStyle('cursor', 'pointer');
9001     }
9002     this.el.unselectable();
9003     if(transparent){
9004         this.el.setOpacity(0);
9005     }
9006     this.el.on("mousedown", this.onMouseDown, this);
9007     if(!disableTrackOver){
9008         this.el.on("mouseover", this.onMouseOver, this);
9009         this.el.on("mouseout", this.onMouseOut, this);
9010     }
9011 };
9012
9013 // private
9014 Roo.Resizable.Handle.prototype = {
9015     afterResize : function(rz){
9016         Roo.log('after?');
9017         // do nothing
9018     },
9019     // private
9020     onMouseDown : function(e){
9021         this.rz.onMouseDown(this, e);
9022     },
9023     // private
9024     onMouseOver : function(e){
9025         this.rz.handleOver(this, e);
9026     },
9027     // private
9028     onMouseOut : function(e){
9029         this.rz.handleOut(this, e);
9030     }
9031 };/*
9032  * Based on:
9033  * Ext JS Library 1.1.1
9034  * Copyright(c) 2006-2007, Ext JS, LLC.
9035  *
9036  * Originally Released Under LGPL - original licence link has changed is not relivant.
9037  *
9038  * Fork - LGPL
9039  * <script type="text/javascript">
9040  */
9041
9042 /**
9043  * @class Roo.Editor
9044  * @extends Roo.Component
9045  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9046  * @constructor
9047  * Create a new Editor
9048  * @param {Roo.form.Field} field The Field object (or descendant)
9049  * @param {Object} config The config object
9050  */
9051 Roo.Editor = function(field, config){
9052     Roo.Editor.superclass.constructor.call(this, config);
9053     this.field = field;
9054     this.addEvents({
9055         /**
9056              * @event beforestartedit
9057              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9058              * false from the handler of this event.
9059              * @param {Editor} this
9060              * @param {Roo.Element} boundEl The underlying element bound to this editor
9061              * @param {Mixed} value The field value being set
9062              */
9063         "beforestartedit" : true,
9064         /**
9065              * @event startedit
9066              * Fires when this editor is displayed
9067              * @param {Roo.Element} boundEl The underlying element bound to this editor
9068              * @param {Mixed} value The starting field value
9069              */
9070         "startedit" : true,
9071         /**
9072              * @event beforecomplete
9073              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9074              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9075              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9076              * event will not fire since no edit actually occurred.
9077              * @param {Editor} this
9078              * @param {Mixed} value The current field value
9079              * @param {Mixed} startValue The original field value
9080              */
9081         "beforecomplete" : true,
9082         /**
9083              * @event complete
9084              * Fires after editing is complete and any changed value has been written to the underlying field.
9085              * @param {Editor} this
9086              * @param {Mixed} value The current field value
9087              * @param {Mixed} startValue The original field value
9088              */
9089         "complete" : true,
9090         /**
9091          * @event specialkey
9092          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9093          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9094          * @param {Roo.form.Field} this
9095          * @param {Roo.EventObject} e The event object
9096          */
9097         "specialkey" : true
9098     });
9099 };
9100
9101 Roo.extend(Roo.Editor, Roo.Component, {
9102     /**
9103      * @cfg {Boolean/String} autosize
9104      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9105      * or "height" to adopt the height only (defaults to false)
9106      */
9107     /**
9108      * @cfg {Boolean} revertInvalid
9109      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9110      * validation fails (defaults to true)
9111      */
9112     /**
9113      * @cfg {Boolean} ignoreNoChange
9114      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9115      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9116      * will never be ignored.
9117      */
9118     /**
9119      * @cfg {Boolean} hideEl
9120      * False to keep the bound element visible while the editor is displayed (defaults to true)
9121      */
9122     /**
9123      * @cfg {Mixed} value
9124      * The data value of the underlying field (defaults to "")
9125      */
9126     value : "",
9127     /**
9128      * @cfg {String} alignment
9129      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9130      */
9131     alignment: "c-c?",
9132     /**
9133      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9134      * for bottom-right shadow (defaults to "frame")
9135      */
9136     shadow : "frame",
9137     /**
9138      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9139      */
9140     constrain : false,
9141     /**
9142      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9143      */
9144     completeOnEnter : false,
9145     /**
9146      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9147      */
9148     cancelOnEsc : false,
9149     /**
9150      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9151      */
9152     updateEl : false,
9153
9154     // private
9155     onRender : function(ct, position){
9156         this.el = new Roo.Layer({
9157             shadow: this.shadow,
9158             cls: "x-editor",
9159             parentEl : ct,
9160             shim : this.shim,
9161             shadowOffset:4,
9162             id: this.id,
9163             constrain: this.constrain
9164         });
9165         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9166         if(this.field.msgTarget != 'title'){
9167             this.field.msgTarget = 'qtip';
9168         }
9169         this.field.render(this.el);
9170         if(Roo.isGecko){
9171             this.field.el.dom.setAttribute('autocomplete', 'off');
9172         }
9173         this.field.on("specialkey", this.onSpecialKey, this);
9174         if(this.swallowKeys){
9175             this.field.el.swallowEvent(['keydown','keypress']);
9176         }
9177         this.field.show();
9178         this.field.on("blur", this.onBlur, this);
9179         if(this.field.grow){
9180             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9181         }
9182     },
9183
9184     onSpecialKey : function(field, e)
9185     {
9186         //Roo.log('editor onSpecialKey');
9187         if(this.completeOnEnter && e.getKey() == e.ENTER){
9188             e.stopEvent();
9189             this.completeEdit();
9190             return;
9191         }
9192         // do not fire special key otherwise it might hide close the editor...
9193         if(e.getKey() == e.ENTER){    
9194             return;
9195         }
9196         if(this.cancelOnEsc && e.getKey() == e.ESC){
9197             this.cancelEdit();
9198             return;
9199         } 
9200         this.fireEvent('specialkey', field, e);
9201     
9202     },
9203
9204     /**
9205      * Starts the editing process and shows the editor.
9206      * @param {String/HTMLElement/Element} el The element to edit
9207      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9208       * to the innerHTML of el.
9209      */
9210     startEdit : function(el, value){
9211         if(this.editing){
9212             this.completeEdit();
9213         }
9214         this.boundEl = Roo.get(el);
9215         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9216         if(!this.rendered){
9217             this.render(this.parentEl || document.body);
9218         }
9219         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9220             return;
9221         }
9222         this.startValue = v;
9223         this.field.setValue(v);
9224         if(this.autoSize){
9225             var sz = this.boundEl.getSize();
9226             switch(this.autoSize){
9227                 case "width":
9228                 this.setSize(sz.width,  "");
9229                 break;
9230                 case "height":
9231                 this.setSize("",  sz.height);
9232                 break;
9233                 default:
9234                 this.setSize(sz.width,  sz.height);
9235             }
9236         }
9237         this.el.alignTo(this.boundEl, this.alignment);
9238         this.editing = true;
9239         if(Roo.QuickTips){
9240             Roo.QuickTips.disable();
9241         }
9242         this.show();
9243     },
9244
9245     /**
9246      * Sets the height and width of this editor.
9247      * @param {Number} width The new width
9248      * @param {Number} height The new height
9249      */
9250     setSize : function(w, h){
9251         this.field.setSize(w, h);
9252         if(this.el){
9253             this.el.sync();
9254         }
9255     },
9256
9257     /**
9258      * Realigns the editor to the bound field based on the current alignment config value.
9259      */
9260     realign : function(){
9261         this.el.alignTo(this.boundEl, this.alignment);
9262     },
9263
9264     /**
9265      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9266      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9267      */
9268     completeEdit : function(remainVisible){
9269         if(!this.editing){
9270             return;
9271         }
9272         var v = this.getValue();
9273         if(this.revertInvalid !== false && !this.field.isValid()){
9274             v = this.startValue;
9275             this.cancelEdit(true);
9276         }
9277         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9278             this.editing = false;
9279             this.hide();
9280             return;
9281         }
9282         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9283             this.editing = false;
9284             if(this.updateEl && this.boundEl){
9285                 this.boundEl.update(v);
9286             }
9287             if(remainVisible !== true){
9288                 this.hide();
9289             }
9290             this.fireEvent("complete", this, v, this.startValue);
9291         }
9292     },
9293
9294     // private
9295     onShow : function(){
9296         this.el.show();
9297         if(this.hideEl !== false){
9298             this.boundEl.hide();
9299         }
9300         this.field.show();
9301         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9302             this.fixIEFocus = true;
9303             this.deferredFocus.defer(50, this);
9304         }else{
9305             this.field.focus();
9306         }
9307         this.fireEvent("startedit", this.boundEl, this.startValue);
9308     },
9309
9310     deferredFocus : function(){
9311         if(this.editing){
9312             this.field.focus();
9313         }
9314     },
9315
9316     /**
9317      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9318      * reverted to the original starting value.
9319      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9320      * cancel (defaults to false)
9321      */
9322     cancelEdit : function(remainVisible){
9323         if(this.editing){
9324             this.setValue(this.startValue);
9325             if(remainVisible !== true){
9326                 this.hide();
9327             }
9328         }
9329     },
9330
9331     // private
9332     onBlur : function(){
9333         if(this.allowBlur !== true && this.editing){
9334             this.completeEdit();
9335         }
9336     },
9337
9338     // private
9339     onHide : function(){
9340         if(this.editing){
9341             this.completeEdit();
9342             return;
9343         }
9344         this.field.blur();
9345         if(this.field.collapse){
9346             this.field.collapse();
9347         }
9348         this.el.hide();
9349         if(this.hideEl !== false){
9350             this.boundEl.show();
9351         }
9352         if(Roo.QuickTips){
9353             Roo.QuickTips.enable();
9354         }
9355     },
9356
9357     /**
9358      * Sets the data value of the editor
9359      * @param {Mixed} value Any valid value supported by the underlying field
9360      */
9361     setValue : function(v){
9362         this.field.setValue(v);
9363     },
9364
9365     /**
9366      * Gets the data value of the editor
9367      * @return {Mixed} The data value
9368      */
9369     getValue : function(){
9370         return this.field.getValue();
9371     }
9372 });/*
9373  * Based on:
9374  * Ext JS Library 1.1.1
9375  * Copyright(c) 2006-2007, Ext JS, LLC.
9376  *
9377  * Originally Released Under LGPL - original licence link has changed is not relivant.
9378  *
9379  * Fork - LGPL
9380  * <script type="text/javascript">
9381  */
9382  
9383 /**
9384  * @class Roo.BasicDialog
9385  * @extends Roo.util.Observable
9386  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9387  * <pre><code>
9388 var dlg = new Roo.BasicDialog("my-dlg", {
9389     height: 200,
9390     width: 300,
9391     minHeight: 100,
9392     minWidth: 150,
9393     modal: true,
9394     proxyDrag: true,
9395     shadow: true
9396 });
9397 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9398 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9399 dlg.addButton('Cancel', dlg.hide, dlg);
9400 dlg.show();
9401 </code></pre>
9402   <b>A Dialog should always be a direct child of the body element.</b>
9403  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9404  * @cfg {String} title Default text to display in the title bar (defaults to null)
9405  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9406  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9407  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9408  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9409  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9410  * (defaults to null with no animation)
9411  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9412  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9413  * property for valid values (defaults to 'all')
9414  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9415  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9416  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9417  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9418  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9419  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9420  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9421  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9422  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9423  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9424  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9425  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9426  * draggable = true (defaults to false)
9427  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9428  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9429  * shadow (defaults to false)
9430  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9431  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9432  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9433  * @cfg {Array} buttons Array of buttons
9434  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9435  * @constructor
9436  * Create a new BasicDialog.
9437  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9438  * @param {Object} config Configuration options
9439  */
9440 Roo.BasicDialog = function(el, config){
9441     this.el = Roo.get(el);
9442     var dh = Roo.DomHelper;
9443     if(!this.el && config && config.autoCreate){
9444         if(typeof config.autoCreate == "object"){
9445             if(!config.autoCreate.id){
9446                 config.autoCreate.id = el;
9447             }
9448             this.el = dh.append(document.body,
9449                         config.autoCreate, true);
9450         }else{
9451             this.el = dh.append(document.body,
9452                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9453         }
9454     }
9455     el = this.el;
9456     el.setDisplayed(true);
9457     el.hide = this.hideAction;
9458     this.id = el.id;
9459     el.addClass("x-dlg");
9460
9461     Roo.apply(this, config);
9462
9463     this.proxy = el.createProxy("x-dlg-proxy");
9464     this.proxy.hide = this.hideAction;
9465     this.proxy.setOpacity(.5);
9466     this.proxy.hide();
9467
9468     if(config.width){
9469         el.setWidth(config.width);
9470     }
9471     if(config.height){
9472         el.setHeight(config.height);
9473     }
9474     this.size = el.getSize();
9475     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9476         this.xy = [config.x,config.y];
9477     }else{
9478         this.xy = el.getCenterXY(true);
9479     }
9480     /** The header element @type Roo.Element */
9481     this.header = el.child("> .x-dlg-hd");
9482     /** The body element @type Roo.Element */
9483     this.body = el.child("> .x-dlg-bd");
9484     /** The footer element @type Roo.Element */
9485     this.footer = el.child("> .x-dlg-ft");
9486
9487     if(!this.header){
9488         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9489     }
9490     if(!this.body){
9491         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9492     }
9493
9494     this.header.unselectable();
9495     if(this.title){
9496         this.header.update(this.title);
9497     }
9498     // this element allows the dialog to be focused for keyboard event
9499     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9500     this.focusEl.swallowEvent("click", true);
9501
9502     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9503
9504     // wrap the body and footer for special rendering
9505     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9506     if(this.footer){
9507         this.bwrap.dom.appendChild(this.footer.dom);
9508     }
9509
9510     this.bg = this.el.createChild({
9511         tag: "div", cls:"x-dlg-bg",
9512         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9513     });
9514     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9515
9516
9517     if(this.autoScroll !== false && !this.autoTabs){
9518         this.body.setStyle("overflow", "auto");
9519     }
9520
9521     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9522
9523     if(this.closable !== false){
9524         this.el.addClass("x-dlg-closable");
9525         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9526         this.close.on("click", this.closeClick, this);
9527         this.close.addClassOnOver("x-dlg-close-over");
9528     }
9529     if(this.collapsible !== false){
9530         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9531         this.collapseBtn.on("click", this.collapseClick, this);
9532         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9533         this.header.on("dblclick", this.collapseClick, this);
9534     }
9535     if(this.resizable !== false){
9536         this.el.addClass("x-dlg-resizable");
9537         this.resizer = new Roo.Resizable(el, {
9538             minWidth: this.minWidth || 80,
9539             minHeight:this.minHeight || 80,
9540             handles: this.resizeHandles || "all",
9541             pinned: true
9542         });
9543         this.resizer.on("beforeresize", this.beforeResize, this);
9544         this.resizer.on("resize", this.onResize, this);
9545     }
9546     if(this.draggable !== false){
9547         el.addClass("x-dlg-draggable");
9548         if (!this.proxyDrag) {
9549             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9550         }
9551         else {
9552             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9553         }
9554         dd.setHandleElId(this.header.id);
9555         dd.endDrag = this.endMove.createDelegate(this);
9556         dd.startDrag = this.startMove.createDelegate(this);
9557         dd.onDrag = this.onDrag.createDelegate(this);
9558         dd.scroll = false;
9559         this.dd = dd;
9560     }
9561     if(this.modal){
9562         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9563         this.mask.enableDisplayMode("block");
9564         this.mask.hide();
9565         this.el.addClass("x-dlg-modal");
9566     }
9567     if(this.shadow){
9568         this.shadow = new Roo.Shadow({
9569             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9570             offset : this.shadowOffset
9571         });
9572     }else{
9573         this.shadowOffset = 0;
9574     }
9575     if(Roo.useShims && this.shim !== false){
9576         this.shim = this.el.createShim();
9577         this.shim.hide = this.hideAction;
9578         this.shim.hide();
9579     }else{
9580         this.shim = false;
9581     }
9582     if(this.autoTabs){
9583         this.initTabs();
9584     }
9585     if (this.buttons) { 
9586         var bts= this.buttons;
9587         this.buttons = [];
9588         Roo.each(bts, function(b) {
9589             this.addButton(b);
9590         }, this);
9591     }
9592     
9593     
9594     this.addEvents({
9595         /**
9596          * @event keydown
9597          * Fires when a key is pressed
9598          * @param {Roo.BasicDialog} this
9599          * @param {Roo.EventObject} e
9600          */
9601         "keydown" : true,
9602         /**
9603          * @event move
9604          * Fires when this dialog is moved by the user.
9605          * @param {Roo.BasicDialog} this
9606          * @param {Number} x The new page X
9607          * @param {Number} y The new page Y
9608          */
9609         "move" : true,
9610         /**
9611          * @event resize
9612          * Fires when this dialog is resized by the user.
9613          * @param {Roo.BasicDialog} this
9614          * @param {Number} width The new width
9615          * @param {Number} height The new height
9616          */
9617         "resize" : true,
9618         /**
9619          * @event beforehide
9620          * Fires before this dialog is hidden.
9621          * @param {Roo.BasicDialog} this
9622          */
9623         "beforehide" : true,
9624         /**
9625          * @event hide
9626          * Fires when this dialog is hidden.
9627          * @param {Roo.BasicDialog} this
9628          */
9629         "hide" : true,
9630         /**
9631          * @event beforeshow
9632          * Fires before this dialog is shown.
9633          * @param {Roo.BasicDialog} this
9634          */
9635         "beforeshow" : true,
9636         /**
9637          * @event show
9638          * Fires when this dialog is shown.
9639          * @param {Roo.BasicDialog} this
9640          */
9641         "show" : true
9642     });
9643     el.on("keydown", this.onKeyDown, this);
9644     el.on("mousedown", this.toFront, this);
9645     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9646     this.el.hide();
9647     Roo.DialogManager.register(this);
9648     Roo.BasicDialog.superclass.constructor.call(this);
9649 };
9650
9651 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9652     shadowOffset: Roo.isIE ? 6 : 5,
9653     minHeight: 80,
9654     minWidth: 200,
9655     minButtonWidth: 75,
9656     defaultButton: null,
9657     buttonAlign: "right",
9658     tabTag: 'div',
9659     firstShow: true,
9660
9661     /**
9662      * Sets the dialog title text
9663      * @param {String} text The title text to display
9664      * @return {Roo.BasicDialog} this
9665      */
9666     setTitle : function(text){
9667         this.header.update(text);
9668         return this;
9669     },
9670
9671     // private
9672     closeClick : function(){
9673         this.hide();
9674     },
9675
9676     // private
9677     collapseClick : function(){
9678         this[this.collapsed ? "expand" : "collapse"]();
9679     },
9680
9681     /**
9682      * Collapses the dialog to its minimized state (only the title bar is visible).
9683      * Equivalent to the user clicking the collapse dialog button.
9684      */
9685     collapse : function(){
9686         if(!this.collapsed){
9687             this.collapsed = true;
9688             this.el.addClass("x-dlg-collapsed");
9689             this.restoreHeight = this.el.getHeight();
9690             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9691         }
9692     },
9693
9694     /**
9695      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9696      * clicking the expand dialog button.
9697      */
9698     expand : function(){
9699         if(this.collapsed){
9700             this.collapsed = false;
9701             this.el.removeClass("x-dlg-collapsed");
9702             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9703         }
9704     },
9705
9706     /**
9707      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9708      * @return {Roo.TabPanel} The tabs component
9709      */
9710     initTabs : function(){
9711         var tabs = this.getTabs();
9712         while(tabs.getTab(0)){
9713             tabs.removeTab(0);
9714         }
9715         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9716             var dom = el.dom;
9717             tabs.addTab(Roo.id(dom), dom.title);
9718             dom.title = "";
9719         });
9720         tabs.activate(0);
9721         return tabs;
9722     },
9723
9724     // private
9725     beforeResize : function(){
9726         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9727     },
9728
9729     // private
9730     onResize : function(){
9731         this.refreshSize();
9732         this.syncBodyHeight();
9733         this.adjustAssets();
9734         this.focus();
9735         this.fireEvent("resize", this, this.size.width, this.size.height);
9736     },
9737
9738     // private
9739     onKeyDown : function(e){
9740         if(this.isVisible()){
9741             this.fireEvent("keydown", this, e);
9742         }
9743     },
9744
9745     /**
9746      * Resizes the dialog.
9747      * @param {Number} width
9748      * @param {Number} height
9749      * @return {Roo.BasicDialog} this
9750      */
9751     resizeTo : function(width, height){
9752         this.el.setSize(width, height);
9753         this.size = {width: width, height: height};
9754         this.syncBodyHeight();
9755         if(this.fixedcenter){
9756             this.center();
9757         }
9758         if(this.isVisible()){
9759             this.constrainXY();
9760             this.adjustAssets();
9761         }
9762         this.fireEvent("resize", this, width, height);
9763         return this;
9764     },
9765
9766
9767     /**
9768      * Resizes the dialog to fit the specified content size.
9769      * @param {Number} width
9770      * @param {Number} height
9771      * @return {Roo.BasicDialog} this
9772      */
9773     setContentSize : function(w, h){
9774         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9775         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9776         //if(!this.el.isBorderBox()){
9777             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9778             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9779         //}
9780         if(this.tabs){
9781             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9782             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9783         }
9784         this.resizeTo(w, h);
9785         return this;
9786     },
9787
9788     /**
9789      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9790      * executed in response to a particular key being pressed while the dialog is active.
9791      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9792      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9793      * @param {Function} fn The function to call
9794      * @param {Object} scope (optional) The scope of the function
9795      * @return {Roo.BasicDialog} this
9796      */
9797     addKeyListener : function(key, fn, scope){
9798         var keyCode, shift, ctrl, alt;
9799         if(typeof key == "object" && !(key instanceof Array)){
9800             keyCode = key["key"];
9801             shift = key["shift"];
9802             ctrl = key["ctrl"];
9803             alt = key["alt"];
9804         }else{
9805             keyCode = key;
9806         }
9807         var handler = function(dlg, e){
9808             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9809                 var k = e.getKey();
9810                 if(keyCode instanceof Array){
9811                     for(var i = 0, len = keyCode.length; i < len; i++){
9812                         if(keyCode[i] == k){
9813                           fn.call(scope || window, dlg, k, e);
9814                           return;
9815                         }
9816                     }
9817                 }else{
9818                     if(k == keyCode){
9819                         fn.call(scope || window, dlg, k, e);
9820                     }
9821                 }
9822             }
9823         };
9824         this.on("keydown", handler);
9825         return this;
9826     },
9827
9828     /**
9829      * Returns the TabPanel component (creates it if it doesn't exist).
9830      * Note: If you wish to simply check for the existence of tabs without creating them,
9831      * check for a null 'tabs' property.
9832      * @return {Roo.TabPanel} The tabs component
9833      */
9834     getTabs : function(){
9835         if(!this.tabs){
9836             this.el.addClass("x-dlg-auto-tabs");
9837             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9838             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9839         }
9840         return this.tabs;
9841     },
9842
9843     /**
9844      * Adds a button to the footer section of the dialog.
9845      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9846      * object or a valid Roo.DomHelper element config
9847      * @param {Function} handler The function called when the button is clicked
9848      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9849      * @return {Roo.Button} The new button
9850      */
9851     addButton : function(config, handler, scope){
9852         var dh = Roo.DomHelper;
9853         if(!this.footer){
9854             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9855         }
9856         if(!this.btnContainer){
9857             var tb = this.footer.createChild({
9858
9859                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9860                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9861             }, null, true);
9862             this.btnContainer = tb.firstChild.firstChild.firstChild;
9863         }
9864         var bconfig = {
9865             handler: handler,
9866             scope: scope,
9867             minWidth: this.minButtonWidth,
9868             hideParent:true
9869         };
9870         if(typeof config == "string"){
9871             bconfig.text = config;
9872         }else{
9873             if(config.tag){
9874                 bconfig.dhconfig = config;
9875             }else{
9876                 Roo.apply(bconfig, config);
9877             }
9878         }
9879         var fc = false;
9880         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9881             bconfig.position = Math.max(0, bconfig.position);
9882             fc = this.btnContainer.childNodes[bconfig.position];
9883         }
9884          
9885         var btn = new Roo.Button(
9886             fc ? 
9887                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9888                 : this.btnContainer.appendChild(document.createElement("td")),
9889             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9890             bconfig
9891         );
9892         this.syncBodyHeight();
9893         if(!this.buttons){
9894             /**
9895              * Array of all the buttons that have been added to this dialog via addButton
9896              * @type Array
9897              */
9898             this.buttons = [];
9899         }
9900         this.buttons.push(btn);
9901         return btn;
9902     },
9903
9904     /**
9905      * Sets the default button to be focused when the dialog is displayed.
9906      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9907      * @return {Roo.BasicDialog} this
9908      */
9909     setDefaultButton : function(btn){
9910         this.defaultButton = btn;
9911         return this;
9912     },
9913
9914     // private
9915     getHeaderFooterHeight : function(safe){
9916         var height = 0;
9917         if(this.header){
9918            height += this.header.getHeight();
9919         }
9920         if(this.footer){
9921            var fm = this.footer.getMargins();
9922             height += (this.footer.getHeight()+fm.top+fm.bottom);
9923         }
9924         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9925         height += this.centerBg.getPadding("tb");
9926         return height;
9927     },
9928
9929     // private
9930     syncBodyHeight : function()
9931     {
9932         var bd = this.body, // the text
9933             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9934             bw = this.bwrap;
9935         var height = this.size.height - this.getHeaderFooterHeight(false);
9936         bd.setHeight(height-bd.getMargins("tb"));
9937         var hh = this.header.getHeight();
9938         var h = this.size.height-hh;
9939         cb.setHeight(h);
9940         
9941         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9942         bw.setHeight(h-cb.getPadding("tb"));
9943         
9944         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9945         bd.setWidth(bw.getWidth(true));
9946         if(this.tabs){
9947             this.tabs.syncHeight();
9948             if(Roo.isIE){
9949                 this.tabs.el.repaint();
9950             }
9951         }
9952     },
9953
9954     /**
9955      * Restores the previous state of the dialog if Roo.state is configured.
9956      * @return {Roo.BasicDialog} this
9957      */
9958     restoreState : function(){
9959         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9960         if(box && box.width){
9961             this.xy = [box.x, box.y];
9962             this.resizeTo(box.width, box.height);
9963         }
9964         return this;
9965     },
9966
9967     // private
9968     beforeShow : function(){
9969         this.expand();
9970         if(this.fixedcenter){
9971             this.xy = this.el.getCenterXY(true);
9972         }
9973         if(this.modal){
9974             Roo.get(document.body).addClass("x-body-masked");
9975             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9976             this.mask.show();
9977         }
9978         this.constrainXY();
9979     },
9980
9981     // private
9982     animShow : function(){
9983         var b = Roo.get(this.animateTarget).getBox();
9984         this.proxy.setSize(b.width, b.height);
9985         this.proxy.setLocation(b.x, b.y);
9986         this.proxy.show();
9987         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9988                     true, .35, this.showEl.createDelegate(this));
9989     },
9990
9991     /**
9992      * Shows the dialog.
9993      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9994      * @return {Roo.BasicDialog} this
9995      */
9996     show : function(animateTarget){
9997         if (this.fireEvent("beforeshow", this) === false){
9998             return;
9999         }
10000         if(this.syncHeightBeforeShow){
10001             this.syncBodyHeight();
10002         }else if(this.firstShow){
10003             this.firstShow = false;
10004             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10005         }
10006         this.animateTarget = animateTarget || this.animateTarget;
10007         if(!this.el.isVisible()){
10008             this.beforeShow();
10009             if(this.animateTarget && Roo.get(this.animateTarget)){
10010                 this.animShow();
10011             }else{
10012                 this.showEl();
10013             }
10014         }
10015         return this;
10016     },
10017
10018     // private
10019     showEl : function(){
10020         this.proxy.hide();
10021         this.el.setXY(this.xy);
10022         this.el.show();
10023         this.adjustAssets(true);
10024         this.toFront();
10025         this.focus();
10026         // IE peekaboo bug - fix found by Dave Fenwick
10027         if(Roo.isIE){
10028             this.el.repaint();
10029         }
10030         this.fireEvent("show", this);
10031     },
10032
10033     /**
10034      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10035      * dialog itself will receive focus.
10036      */
10037     focus : function(){
10038         if(this.defaultButton){
10039             this.defaultButton.focus();
10040         }else{
10041             this.focusEl.focus();
10042         }
10043     },
10044
10045     // private
10046     constrainXY : function(){
10047         if(this.constraintoviewport !== false){
10048             if(!this.viewSize){
10049                 if(this.container){
10050                     var s = this.container.getSize();
10051                     this.viewSize = [s.width, s.height];
10052                 }else{
10053                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10054                 }
10055             }
10056             var s = Roo.get(this.container||document).getScroll();
10057
10058             var x = this.xy[0], y = this.xy[1];
10059             var w = this.size.width, h = this.size.height;
10060             var vw = this.viewSize[0], vh = this.viewSize[1];
10061             // only move it if it needs it
10062             var moved = false;
10063             // first validate right/bottom
10064             if(x + w > vw+s.left){
10065                 x = vw - w;
10066                 moved = true;
10067             }
10068             if(y + h > vh+s.top){
10069                 y = vh - h;
10070                 moved = true;
10071             }
10072             // then make sure top/left isn't negative
10073             if(x < s.left){
10074                 x = s.left;
10075                 moved = true;
10076             }
10077             if(y < s.top){
10078                 y = s.top;
10079                 moved = true;
10080             }
10081             if(moved){
10082                 // cache xy
10083                 this.xy = [x, y];
10084                 if(this.isVisible()){
10085                     this.el.setLocation(x, y);
10086                     this.adjustAssets();
10087                 }
10088             }
10089         }
10090     },
10091
10092     // private
10093     onDrag : function(){
10094         if(!this.proxyDrag){
10095             this.xy = this.el.getXY();
10096             this.adjustAssets();
10097         }
10098     },
10099
10100     // private
10101     adjustAssets : function(doShow){
10102         var x = this.xy[0], y = this.xy[1];
10103         var w = this.size.width, h = this.size.height;
10104         if(doShow === true){
10105             if(this.shadow){
10106                 this.shadow.show(this.el);
10107             }
10108             if(this.shim){
10109                 this.shim.show();
10110             }
10111         }
10112         if(this.shadow && this.shadow.isVisible()){
10113             this.shadow.show(this.el);
10114         }
10115         if(this.shim && this.shim.isVisible()){
10116             this.shim.setBounds(x, y, w, h);
10117         }
10118     },
10119
10120     // private
10121     adjustViewport : function(w, h){
10122         if(!w || !h){
10123             w = Roo.lib.Dom.getViewWidth();
10124             h = Roo.lib.Dom.getViewHeight();
10125         }
10126         // cache the size
10127         this.viewSize = [w, h];
10128         if(this.modal && this.mask.isVisible()){
10129             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10130             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10131         }
10132         if(this.isVisible()){
10133             this.constrainXY();
10134         }
10135     },
10136
10137     /**
10138      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10139      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10140      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10141      */
10142     destroy : function(removeEl){
10143         if(this.isVisible()){
10144             this.animateTarget = null;
10145             this.hide();
10146         }
10147         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10148         if(this.tabs){
10149             this.tabs.destroy(removeEl);
10150         }
10151         Roo.destroy(
10152              this.shim,
10153              this.proxy,
10154              this.resizer,
10155              this.close,
10156              this.mask
10157         );
10158         if(this.dd){
10159             this.dd.unreg();
10160         }
10161         if(this.buttons){
10162            for(var i = 0, len = this.buttons.length; i < len; i++){
10163                this.buttons[i].destroy();
10164            }
10165         }
10166         this.el.removeAllListeners();
10167         if(removeEl === true){
10168             this.el.update("");
10169             this.el.remove();
10170         }
10171         Roo.DialogManager.unregister(this);
10172     },
10173
10174     // private
10175     startMove : function(){
10176         if(this.proxyDrag){
10177             this.proxy.show();
10178         }
10179         if(this.constraintoviewport !== false){
10180             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10181         }
10182     },
10183
10184     // private
10185     endMove : function(){
10186         if(!this.proxyDrag){
10187             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10188         }else{
10189             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10190             this.proxy.hide();
10191         }
10192         this.refreshSize();
10193         this.adjustAssets();
10194         this.focus();
10195         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10196     },
10197
10198     /**
10199      * Brings this dialog to the front of any other visible dialogs
10200      * @return {Roo.BasicDialog} this
10201      */
10202     toFront : function(){
10203         Roo.DialogManager.bringToFront(this);
10204         return this;
10205     },
10206
10207     /**
10208      * Sends this dialog to the back (under) of any other visible dialogs
10209      * @return {Roo.BasicDialog} this
10210      */
10211     toBack : function(){
10212         Roo.DialogManager.sendToBack(this);
10213         return this;
10214     },
10215
10216     /**
10217      * Centers this dialog in the viewport
10218      * @return {Roo.BasicDialog} this
10219      */
10220     center : function(){
10221         var xy = this.el.getCenterXY(true);
10222         this.moveTo(xy[0], xy[1]);
10223         return this;
10224     },
10225
10226     /**
10227      * Moves the dialog's top-left corner to the specified point
10228      * @param {Number} x
10229      * @param {Number} y
10230      * @return {Roo.BasicDialog} this
10231      */
10232     moveTo : function(x, y){
10233         this.xy = [x,y];
10234         if(this.isVisible()){
10235             this.el.setXY(this.xy);
10236             this.adjustAssets();
10237         }
10238         return this;
10239     },
10240
10241     /**
10242      * Aligns the dialog to the specified element
10243      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10244      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10245      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10246      * @return {Roo.BasicDialog} this
10247      */
10248     alignTo : function(element, position, offsets){
10249         this.xy = this.el.getAlignToXY(element, position, offsets);
10250         if(this.isVisible()){
10251             this.el.setXY(this.xy);
10252             this.adjustAssets();
10253         }
10254         return this;
10255     },
10256
10257     /**
10258      * Anchors an element to another element and realigns it when the window is resized.
10259      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10260      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10261      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10262      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10263      * is a number, it is used as the buffer delay (defaults to 50ms).
10264      * @return {Roo.BasicDialog} this
10265      */
10266     anchorTo : function(el, alignment, offsets, monitorScroll){
10267         var action = function(){
10268             this.alignTo(el, alignment, offsets);
10269         };
10270         Roo.EventManager.onWindowResize(action, this);
10271         var tm = typeof monitorScroll;
10272         if(tm != 'undefined'){
10273             Roo.EventManager.on(window, 'scroll', action, this,
10274                 {buffer: tm == 'number' ? monitorScroll : 50});
10275         }
10276         action.call(this);
10277         return this;
10278     },
10279
10280     /**
10281      * Returns true if the dialog is visible
10282      * @return {Boolean}
10283      */
10284     isVisible : function(){
10285         return this.el.isVisible();
10286     },
10287
10288     // private
10289     animHide : function(callback){
10290         var b = Roo.get(this.animateTarget).getBox();
10291         this.proxy.show();
10292         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10293         this.el.hide();
10294         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10295                     this.hideEl.createDelegate(this, [callback]));
10296     },
10297
10298     /**
10299      * Hides the dialog.
10300      * @param {Function} callback (optional) Function to call when the dialog is hidden
10301      * @return {Roo.BasicDialog} this
10302      */
10303     hide : function(callback){
10304         if (this.fireEvent("beforehide", this) === false){
10305             return;
10306         }
10307         if(this.shadow){
10308             this.shadow.hide();
10309         }
10310         if(this.shim) {
10311           this.shim.hide();
10312         }
10313         // sometimes animateTarget seems to get set.. causing problems...
10314         // this just double checks..
10315         if(this.animateTarget && Roo.get(this.animateTarget)) {
10316            this.animHide(callback);
10317         }else{
10318             this.el.hide();
10319             this.hideEl(callback);
10320         }
10321         return this;
10322     },
10323
10324     // private
10325     hideEl : function(callback){
10326         this.proxy.hide();
10327         if(this.modal){
10328             this.mask.hide();
10329             Roo.get(document.body).removeClass("x-body-masked");
10330         }
10331         this.fireEvent("hide", this);
10332         if(typeof callback == "function"){
10333             callback();
10334         }
10335     },
10336
10337     // private
10338     hideAction : function(){
10339         this.setLeft("-10000px");
10340         this.setTop("-10000px");
10341         this.setStyle("visibility", "hidden");
10342     },
10343
10344     // private
10345     refreshSize : function(){
10346         this.size = this.el.getSize();
10347         this.xy = this.el.getXY();
10348         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10349     },
10350
10351     // private
10352     // z-index is managed by the DialogManager and may be overwritten at any time
10353     setZIndex : function(index){
10354         if(this.modal){
10355             this.mask.setStyle("z-index", index);
10356         }
10357         if(this.shim){
10358             this.shim.setStyle("z-index", ++index);
10359         }
10360         if(this.shadow){
10361             this.shadow.setZIndex(++index);
10362         }
10363         this.el.setStyle("z-index", ++index);
10364         if(this.proxy){
10365             this.proxy.setStyle("z-index", ++index);
10366         }
10367         if(this.resizer){
10368             this.resizer.proxy.setStyle("z-index", ++index);
10369         }
10370
10371         this.lastZIndex = index;
10372     },
10373
10374     /**
10375      * Returns the element for this dialog
10376      * @return {Roo.Element} The underlying dialog Element
10377      */
10378     getEl : function(){
10379         return this.el;
10380     }
10381 });
10382
10383 /**
10384  * @class Roo.DialogManager
10385  * Provides global access to BasicDialogs that have been created and
10386  * support for z-indexing (layering) multiple open dialogs.
10387  */
10388 Roo.DialogManager = function(){
10389     var list = {};
10390     var accessList = [];
10391     var front = null;
10392
10393     // private
10394     var sortDialogs = function(d1, d2){
10395         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10396     };
10397
10398     // private
10399     var orderDialogs = function(){
10400         accessList.sort(sortDialogs);
10401         var seed = Roo.DialogManager.zseed;
10402         for(var i = 0, len = accessList.length; i < len; i++){
10403             var dlg = accessList[i];
10404             if(dlg){
10405                 dlg.setZIndex(seed + (i*10));
10406             }
10407         }
10408     };
10409
10410     return {
10411         /**
10412          * The starting z-index for BasicDialogs (defaults to 9000)
10413          * @type Number The z-index value
10414          */
10415         zseed : 9000,
10416
10417         // private
10418         register : function(dlg){
10419             list[dlg.id] = dlg;
10420             accessList.push(dlg);
10421         },
10422
10423         // private
10424         unregister : function(dlg){
10425             delete list[dlg.id];
10426             var i=0;
10427             var len=0;
10428             if(!accessList.indexOf){
10429                 for(  i = 0, len = accessList.length; i < len; i++){
10430                     if(accessList[i] == dlg){
10431                         accessList.splice(i, 1);
10432                         return;
10433                     }
10434                 }
10435             }else{
10436                  i = accessList.indexOf(dlg);
10437                 if(i != -1){
10438                     accessList.splice(i, 1);
10439                 }
10440             }
10441         },
10442
10443         /**
10444          * Gets a registered dialog by id
10445          * @param {String/Object} id The id of the dialog or a dialog
10446          * @return {Roo.BasicDialog} this
10447          */
10448         get : function(id){
10449             return typeof id == "object" ? id : list[id];
10450         },
10451
10452         /**
10453          * Brings the specified dialog to the front
10454          * @param {String/Object} dlg The id of the dialog or a dialog
10455          * @return {Roo.BasicDialog} this
10456          */
10457         bringToFront : function(dlg){
10458             dlg = this.get(dlg);
10459             if(dlg != front){
10460                 front = dlg;
10461                 dlg._lastAccess = new Date().getTime();
10462                 orderDialogs();
10463             }
10464             return dlg;
10465         },
10466
10467         /**
10468          * Sends the specified dialog to the back
10469          * @param {String/Object} dlg The id of the dialog or a dialog
10470          * @return {Roo.BasicDialog} this
10471          */
10472         sendToBack : function(dlg){
10473             dlg = this.get(dlg);
10474             dlg._lastAccess = -(new Date().getTime());
10475             orderDialogs();
10476             return dlg;
10477         },
10478
10479         /**
10480          * Hides all dialogs
10481          */
10482         hideAll : function(){
10483             for(var id in list){
10484                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10485                     list[id].hide();
10486                 }
10487             }
10488         }
10489     };
10490 }();
10491
10492 /**
10493  * @class Roo.LayoutDialog
10494  * @extends Roo.BasicDialog
10495  * Dialog which provides adjustments for working with a layout in a Dialog.
10496  * Add your necessary layout config options to the dialog's config.<br>
10497  * Example usage (including a nested layout):
10498  * <pre><code>
10499 if(!dialog){
10500     dialog = new Roo.LayoutDialog("download-dlg", {
10501         modal: true,
10502         width:600,
10503         height:450,
10504         shadow:true,
10505         minWidth:500,
10506         minHeight:350,
10507         autoTabs:true,
10508         proxyDrag:true,
10509         // layout config merges with the dialog config
10510         center:{
10511             tabPosition: "top",
10512             alwaysShowTabs: true
10513         }
10514     });
10515     dialog.addKeyListener(27, dialog.hide, dialog);
10516     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10517     dialog.addButton("Build It!", this.getDownload, this);
10518
10519     // we can even add nested layouts
10520     var innerLayout = new Roo.BorderLayout("dl-inner", {
10521         east: {
10522             initialSize: 200,
10523             autoScroll:true,
10524             split:true
10525         },
10526         center: {
10527             autoScroll:true
10528         }
10529     });
10530     innerLayout.beginUpdate();
10531     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10532     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10533     innerLayout.endUpdate(true);
10534
10535     var layout = dialog.getLayout();
10536     layout.beginUpdate();
10537     layout.add("center", new Roo.ContentPanel("standard-panel",
10538                         {title: "Download the Source", fitToFrame:true}));
10539     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10540                {title: "Build your own roo.js"}));
10541     layout.getRegion("center").showPanel(sp);
10542     layout.endUpdate();
10543 }
10544 </code></pre>
10545     * @constructor
10546     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10547     * @param {Object} config configuration options
10548   */
10549 Roo.LayoutDialog = function(el, cfg){
10550     
10551     var config=  cfg;
10552     if (typeof(cfg) == 'undefined') {
10553         config = Roo.apply({}, el);
10554         // not sure why we use documentElement here.. - it should always be body.
10555         // IE7 borks horribly if we use documentElement.
10556         // webkit also does not like documentElement - it creates a body element...
10557         el = Roo.get( document.body || document.documentElement ).createChild();
10558         //config.autoCreate = true;
10559     }
10560     
10561     
10562     config.autoTabs = false;
10563     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10564     this.body.setStyle({overflow:"hidden", position:"relative"});
10565     this.layout = new Roo.BorderLayout(this.body.dom, config);
10566     this.layout.monitorWindowResize = false;
10567     this.el.addClass("x-dlg-auto-layout");
10568     // fix case when center region overwrites center function
10569     this.center = Roo.BasicDialog.prototype.center;
10570     this.on("show", this.layout.layout, this.layout, true);
10571     if (config.items) {
10572         var xitems = config.items;
10573         delete config.items;
10574         Roo.each(xitems, this.addxtype, this);
10575     }
10576     
10577     
10578 };
10579 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10580     /**
10581      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10582      * @deprecated
10583      */
10584     endUpdate : function(){
10585         this.layout.endUpdate();
10586     },
10587
10588     /**
10589      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10590      *  @deprecated
10591      */
10592     beginUpdate : function(){
10593         this.layout.beginUpdate();
10594     },
10595
10596     /**
10597      * Get the BorderLayout for this dialog
10598      * @return {Roo.BorderLayout}
10599      */
10600     getLayout : function(){
10601         return this.layout;
10602     },
10603
10604     showEl : function(){
10605         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10606         if(Roo.isIE7){
10607             this.layout.layout();
10608         }
10609     },
10610
10611     // private
10612     // Use the syncHeightBeforeShow config option to control this automatically
10613     syncBodyHeight : function(){
10614         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10615         if(this.layout){this.layout.layout();}
10616     },
10617     
10618       /**
10619      * Add an xtype element (actually adds to the layout.)
10620      * @return {Object} xdata xtype object data.
10621      */
10622     
10623     addxtype : function(c) {
10624         return this.layout.addxtype(c);
10625     }
10626 });/*
10627  * Based on:
10628  * Ext JS Library 1.1.1
10629  * Copyright(c) 2006-2007, Ext JS, LLC.
10630  *
10631  * Originally Released Under LGPL - original licence link has changed is not relivant.
10632  *
10633  * Fork - LGPL
10634  * <script type="text/javascript">
10635  */
10636  
10637 /**
10638  * @class Roo.MessageBox
10639  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10640  * Example usage:
10641  *<pre><code>
10642 // Basic alert:
10643 Roo.Msg.alert('Status', 'Changes saved successfully.');
10644
10645 // Prompt for user data:
10646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10647     if (btn == 'ok'){
10648         // process text value...
10649     }
10650 });
10651
10652 // Show a dialog using config options:
10653 Roo.Msg.show({
10654    title:'Save Changes?',
10655    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10656    buttons: Roo.Msg.YESNOCANCEL,
10657    fn: processResult,
10658    animEl: 'elId'
10659 });
10660 </code></pre>
10661  * @singleton
10662  */
10663 Roo.MessageBox = function(){
10664     var dlg, opt, mask, waitTimer;
10665     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10666     var buttons, activeTextEl, bwidth;
10667
10668     // private
10669     var handleButton = function(button){
10670         dlg.hide();
10671         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10672     };
10673
10674     // private
10675     var handleHide = function(){
10676         if(opt && opt.cls){
10677             dlg.el.removeClass(opt.cls);
10678         }
10679         if(waitTimer){
10680             Roo.TaskMgr.stop(waitTimer);
10681             waitTimer = null;
10682         }
10683     };
10684
10685     // private
10686     var updateButtons = function(b){
10687         var width = 0;
10688         if(!b){
10689             buttons["ok"].hide();
10690             buttons["cancel"].hide();
10691             buttons["yes"].hide();
10692             buttons["no"].hide();
10693             dlg.footer.dom.style.display = 'none';
10694             return width;
10695         }
10696         dlg.footer.dom.style.display = '';
10697         for(var k in buttons){
10698             if(typeof buttons[k] != "function"){
10699                 if(b[k]){
10700                     buttons[k].show();
10701                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10702                     width += buttons[k].el.getWidth()+15;
10703                 }else{
10704                     buttons[k].hide();
10705                 }
10706             }
10707         }
10708         return width;
10709     };
10710
10711     // private
10712     var handleEsc = function(d, k, e){
10713         if(opt && opt.closable !== false){
10714             dlg.hide();
10715         }
10716         if(e){
10717             e.stopEvent();
10718         }
10719     };
10720
10721     return {
10722         /**
10723          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10724          * @return {Roo.BasicDialog} The BasicDialog element
10725          */
10726         getDialog : function(){
10727            if(!dlg){
10728                 dlg = new Roo.BasicDialog("x-msg-box", {
10729                     autoCreate : true,
10730                     shadow: true,
10731                     draggable: true,
10732                     resizable:false,
10733                     constraintoviewport:false,
10734                     fixedcenter:true,
10735                     collapsible : false,
10736                     shim:true,
10737                     modal: true,
10738                     width:400, height:100,
10739                     buttonAlign:"center",
10740                     closeClick : function(){
10741                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10742                             handleButton("no");
10743                         }else{
10744                             handleButton("cancel");
10745                         }
10746                     }
10747                 });
10748                 dlg.on("hide", handleHide);
10749                 mask = dlg.mask;
10750                 dlg.addKeyListener(27, handleEsc);
10751                 buttons = {};
10752                 var bt = this.buttonText;
10753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10757                 bodyEl = dlg.body.createChild({
10758
10759                     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>'
10760                 });
10761                 msgEl = bodyEl.dom.firstChild;
10762                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10763                 textboxEl.enableDisplayMode();
10764                 textboxEl.addKeyListener([10,13], function(){
10765                     if(dlg.isVisible() && opt && opt.buttons){
10766                         if(opt.buttons.ok){
10767                             handleButton("ok");
10768                         }else if(opt.buttons.yes){
10769                             handleButton("yes");
10770                         }
10771                     }
10772                 });
10773                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10774                 textareaEl.enableDisplayMode();
10775                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10776                 progressEl.enableDisplayMode();
10777                 var pf = progressEl.dom.firstChild;
10778                 if (pf) {
10779                     pp = Roo.get(pf.firstChild);
10780                     pp.setHeight(pf.offsetHeight);
10781                 }
10782                 
10783             }
10784             return dlg;
10785         },
10786
10787         /**
10788          * Updates the message box body text
10789          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10790          * the XHTML-compliant non-breaking space character '&amp;#160;')
10791          * @return {Roo.MessageBox} This message box
10792          */
10793         updateText : function(text){
10794             if(!dlg.isVisible() && !opt.width){
10795                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10796             }
10797             msgEl.innerHTML = text || '&#160;';
10798       
10799             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10800             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10801             var w = Math.max(
10802                     Math.min(opt.width || cw , this.maxWidth), 
10803                     Math.max(opt.minWidth || this.minWidth, bwidth)
10804             );
10805             if(opt.prompt){
10806                 activeTextEl.setWidth(w);
10807             }
10808             if(dlg.isVisible()){
10809                 dlg.fixedcenter = false;
10810             }
10811             // to big, make it scroll. = But as usual stupid IE does not support
10812             // !important..
10813             
10814             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10815                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10816                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10817             } else {
10818                 bodyEl.dom.style.height = '';
10819                 bodyEl.dom.style.overflowY = '';
10820             }
10821             if (cw > w) {
10822                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10823             } else {
10824                 bodyEl.dom.style.overflowX = '';
10825             }
10826             
10827             dlg.setContentSize(w, bodyEl.getHeight());
10828             if(dlg.isVisible()){
10829                 dlg.fixedcenter = true;
10830             }
10831             return this;
10832         },
10833
10834         /**
10835          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10836          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10837          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10838          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10839          * @return {Roo.MessageBox} This message box
10840          */
10841         updateProgress : function(value, text){
10842             if(text){
10843                 this.updateText(text);
10844             }
10845             if (pp) { // weird bug on my firefox - for some reason this is not defined
10846                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10847             }
10848             return this;
10849         },        
10850
10851         /**
10852          * Returns true if the message box is currently displayed
10853          * @return {Boolean} True if the message box is visible, else false
10854          */
10855         isVisible : function(){
10856             return dlg && dlg.isVisible();  
10857         },
10858
10859         /**
10860          * Hides the message box if it is displayed
10861          */
10862         hide : function(){
10863             if(this.isVisible()){
10864                 dlg.hide();
10865             }  
10866         },
10867
10868         /**
10869          * Displays a new message box, or reinitializes an existing message box, based on the config options
10870          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10871          * The following config object properties are supported:
10872          * <pre>
10873 Property    Type             Description
10874 ----------  ---------------  ------------------------------------------------------------------------------------
10875 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10876                                    closes (defaults to undefined)
10877 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10878                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10879 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10880                                    progress and wait dialogs will ignore this property and always hide the
10881                                    close button as they can only be closed programmatically.
10882 cls               String           A custom CSS class to apply to the message box element
10883 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10884                                    displayed (defaults to 75)
10885 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10886                                    function will be btn (the name of the button that was clicked, if applicable,
10887                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10888                                    Progress and wait dialogs will ignore this option since they do not respond to
10889                                    user actions and can only be closed programmatically, so any required function
10890                                    should be called by the same code after it closes the dialog.
10891 icon              String           A CSS class that provides a background image to be used as an icon for
10892                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10893 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10894 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10895 modal             Boolean          False to allow user interaction with the page while the message box is
10896                                    displayed (defaults to true)
10897 msg               String           A string that will replace the existing message box body text (defaults
10898                                    to the XHTML-compliant non-breaking space character '&#160;')
10899 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10900 progress          Boolean          True to display a progress bar (defaults to false)
10901 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10902 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10903 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10904 title             String           The title text
10905 value             String           The string value to set into the active textbox element if displayed
10906 wait              Boolean          True to display a progress bar (defaults to false)
10907 width             Number           The width of the dialog in pixels
10908 </pre>
10909          *
10910          * Example usage:
10911          * <pre><code>
10912 Roo.Msg.show({
10913    title: 'Address',
10914    msg: 'Please enter your address:',
10915    width: 300,
10916    buttons: Roo.MessageBox.OKCANCEL,
10917    multiline: true,
10918    fn: saveAddress,
10919    animEl: 'addAddressBtn'
10920 });
10921 </code></pre>
10922          * @param {Object} config Configuration options
10923          * @return {Roo.MessageBox} This message box
10924          */
10925         show : function(options)
10926         {
10927             
10928             // this causes nightmares if you show one dialog after another
10929             // especially on callbacks..
10930              
10931             if(this.isVisible()){
10932                 
10933                 this.hide();
10934                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10935                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10936                 Roo.log("New Dialog Message:" +  options.msg )
10937                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10938                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10939                 
10940             }
10941             var d = this.getDialog();
10942             opt = options;
10943             d.setTitle(opt.title || "&#160;");
10944             d.close.setDisplayed(opt.closable !== false);
10945             activeTextEl = textboxEl;
10946             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10947             if(opt.prompt){
10948                 if(opt.multiline){
10949                     textboxEl.hide();
10950                     textareaEl.show();
10951                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10952                         opt.multiline : this.defaultTextHeight);
10953                     activeTextEl = textareaEl;
10954                 }else{
10955                     textboxEl.show();
10956                     textareaEl.hide();
10957                 }
10958             }else{
10959                 textboxEl.hide();
10960                 textareaEl.hide();
10961             }
10962             progressEl.setDisplayed(opt.progress === true);
10963             this.updateProgress(0);
10964             activeTextEl.dom.value = opt.value || "";
10965             if(opt.prompt){
10966                 dlg.setDefaultButton(activeTextEl);
10967             }else{
10968                 var bs = opt.buttons;
10969                 var db = null;
10970                 if(bs && bs.ok){
10971                     db = buttons["ok"];
10972                 }else if(bs && bs.yes){
10973                     db = buttons["yes"];
10974                 }
10975                 dlg.setDefaultButton(db);
10976             }
10977             bwidth = updateButtons(opt.buttons);
10978             this.updateText(opt.msg);
10979             if(opt.cls){
10980                 d.el.addClass(opt.cls);
10981             }
10982             d.proxyDrag = opt.proxyDrag === true;
10983             d.modal = opt.modal !== false;
10984             d.mask = opt.modal !== false ? mask : false;
10985             if(!d.isVisible()){
10986                 // force it to the end of the z-index stack so it gets a cursor in FF
10987                 document.body.appendChild(dlg.el.dom);
10988                 d.animateTarget = null;
10989                 d.show(options.animEl);
10990             }
10991             return this;
10992         },
10993
10994         /**
10995          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10996          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10997          * and closing the message box when the process is complete.
10998          * @param {String} title The title bar text
10999          * @param {String} msg The message box body text
11000          * @return {Roo.MessageBox} This message box
11001          */
11002         progress : function(title, msg){
11003             this.show({
11004                 title : title,
11005                 msg : msg,
11006                 buttons: false,
11007                 progress:true,
11008                 closable:false,
11009                 minWidth: this.minProgressWidth,
11010                 modal : true
11011             });
11012             return this;
11013         },
11014
11015         /**
11016          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11017          * If a callback function is passed it will be called after the user clicks the button, and the
11018          * id of the button that was clicked will be passed as the only parameter to the callback
11019          * (could also be the top-right close button).
11020          * @param {String} title The title bar text
11021          * @param {String} msg The message box body text
11022          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11023          * @param {Object} scope (optional) The scope of the callback function
11024          * @return {Roo.MessageBox} This message box
11025          */
11026         alert : function(title, msg, fn, scope){
11027             this.show({
11028                 title : title,
11029                 msg : msg,
11030                 buttons: this.OK,
11031                 fn: fn,
11032                 scope : scope,
11033                 modal : true
11034             });
11035             return this;
11036         },
11037
11038         /**
11039          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11040          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11041          * You are responsible for closing the message box when the process is complete.
11042          * @param {String} msg The message box body text
11043          * @param {String} title (optional) The title bar text
11044          * @return {Roo.MessageBox} This message box
11045          */
11046         wait : function(msg, title){
11047             this.show({
11048                 title : title,
11049                 msg : msg,
11050                 buttons: false,
11051                 closable:false,
11052                 progress:true,
11053                 modal:true,
11054                 width:300,
11055                 wait:true
11056             });
11057             waitTimer = Roo.TaskMgr.start({
11058                 run: function(i){
11059                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11060                 },
11061                 interval: 1000
11062             });
11063             return this;
11064         },
11065
11066         /**
11067          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11068          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11069          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11070          * @param {String} title The title bar text
11071          * @param {String} msg The message box body text
11072          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11073          * @param {Object} scope (optional) The scope of the callback function
11074          * @return {Roo.MessageBox} This message box
11075          */
11076         confirm : function(title, msg, fn, scope){
11077             this.show({
11078                 title : title,
11079                 msg : msg,
11080                 buttons: this.YESNO,
11081                 fn: fn,
11082                 scope : scope,
11083                 modal : true
11084             });
11085             return this;
11086         },
11087
11088         /**
11089          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11090          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11091          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11092          * (could also be the top-right close button) and the text that was entered will be passed as the two
11093          * parameters to the callback.
11094          * @param {String} title The title bar text
11095          * @param {String} msg The message box body text
11096          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11097          * @param {Object} scope (optional) The scope of the callback function
11098          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11099          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11100          * @return {Roo.MessageBox} This message box
11101          */
11102         prompt : function(title, msg, fn, scope, multiline){
11103             this.show({
11104                 title : title,
11105                 msg : msg,
11106                 buttons: this.OKCANCEL,
11107                 fn: fn,
11108                 minWidth:250,
11109                 scope : scope,
11110                 prompt:true,
11111                 multiline: multiline,
11112                 modal : true
11113             });
11114             return this;
11115         },
11116
11117         /**
11118          * Button config that displays a single OK button
11119          * @type Object
11120          */
11121         OK : {ok:true},
11122         /**
11123          * Button config that displays Yes and No buttons
11124          * @type Object
11125          */
11126         YESNO : {yes:true, no:true},
11127         /**
11128          * Button config that displays OK and Cancel buttons
11129          * @type Object
11130          */
11131         OKCANCEL : {ok:true, cancel:true},
11132         /**
11133          * Button config that displays Yes, No and Cancel buttons
11134          * @type Object
11135          */
11136         YESNOCANCEL : {yes:true, no:true, cancel:true},
11137
11138         /**
11139          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11140          * @type Number
11141          */
11142         defaultTextHeight : 75,
11143         /**
11144          * The maximum width in pixels of the message box (defaults to 600)
11145          * @type Number
11146          */
11147         maxWidth : 600,
11148         /**
11149          * The minimum width in pixels of the message box (defaults to 100)
11150          * @type Number
11151          */
11152         minWidth : 100,
11153         /**
11154          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11155          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11156          * @type Number
11157          */
11158         minProgressWidth : 250,
11159         /**
11160          * An object containing the default button text strings that can be overriden for localized language support.
11161          * Supported properties are: ok, cancel, yes and no.
11162          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11163          * @type Object
11164          */
11165         buttonText : {
11166             ok : "OK",
11167             cancel : "Cancel",
11168             yes : "Yes",
11169             no : "No"
11170         }
11171     };
11172 }();
11173
11174 /**
11175  * Shorthand for {@link Roo.MessageBox}
11176  */
11177 Roo.Msg = Roo.MessageBox;/*
11178  * Based on:
11179  * Ext JS Library 1.1.1
11180  * Copyright(c) 2006-2007, Ext JS, LLC.
11181  *
11182  * Originally Released Under LGPL - original licence link has changed is not relivant.
11183  *
11184  * Fork - LGPL
11185  * <script type="text/javascript">
11186  */
11187 /**
11188  * @class Roo.QuickTips
11189  * Provides attractive and customizable tooltips for any element.
11190  * @singleton
11191  */
11192 Roo.QuickTips = function(){
11193     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11194     var ce, bd, xy, dd;
11195     var visible = false, disabled = true, inited = false;
11196     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11197     
11198     var onOver = function(e){
11199         if(disabled){
11200             return;
11201         }
11202         var t = e.getTarget();
11203         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11204             return;
11205         }
11206         if(ce && t == ce.el){
11207             clearTimeout(hideProc);
11208             return;
11209         }
11210         if(t && tagEls[t.id]){
11211             tagEls[t.id].el = t;
11212             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11213             return;
11214         }
11215         var ttp, et = Roo.fly(t);
11216         var ns = cfg.namespace;
11217         if(tm.interceptTitles && t.title){
11218             ttp = t.title;
11219             t.qtip = ttp;
11220             t.removeAttribute("title");
11221             e.preventDefault();
11222         }else{
11223             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11224         }
11225         if(ttp){
11226             showProc = show.defer(tm.showDelay, tm, [{
11227                 el: t, 
11228                 text: ttp, 
11229                 width: et.getAttributeNS(ns, cfg.width),
11230                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11231                 title: et.getAttributeNS(ns, cfg.title),
11232                     cls: et.getAttributeNS(ns, cfg.cls)
11233             }]);
11234         }
11235     };
11236     
11237     var onOut = function(e){
11238         clearTimeout(showProc);
11239         var t = e.getTarget();
11240         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11241             hideProc = setTimeout(hide, tm.hideDelay);
11242         }
11243     };
11244     
11245     var onMove = function(e){
11246         if(disabled){
11247             return;
11248         }
11249         xy = e.getXY();
11250         xy[1] += 18;
11251         if(tm.trackMouse && ce){
11252             el.setXY(xy);
11253         }
11254     };
11255     
11256     var onDown = function(e){
11257         clearTimeout(showProc);
11258         clearTimeout(hideProc);
11259         if(!e.within(el)){
11260             if(tm.hideOnClick){
11261                 hide();
11262                 tm.disable();
11263                 tm.enable.defer(100, tm);
11264             }
11265         }
11266     };
11267     
11268     var getPad = function(){
11269         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11270     };
11271
11272     var show = function(o){
11273         if(disabled){
11274             return;
11275         }
11276         clearTimeout(dismissProc);
11277         ce = o;
11278         if(removeCls){ // in case manually hidden
11279             el.removeClass(removeCls);
11280             removeCls = null;
11281         }
11282         if(ce.cls){
11283             el.addClass(ce.cls);
11284             removeCls = ce.cls;
11285         }
11286         if(ce.title){
11287             tipTitle.update(ce.title);
11288             tipTitle.show();
11289         }else{
11290             tipTitle.update('');
11291             tipTitle.hide();
11292         }
11293         el.dom.style.width  = tm.maxWidth+'px';
11294         //tipBody.dom.style.width = '';
11295         tipBodyText.update(o.text);
11296         var p = getPad(), w = ce.width;
11297         if(!w){
11298             var td = tipBodyText.dom;
11299             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11300             if(aw > tm.maxWidth){
11301                 w = tm.maxWidth;
11302             }else if(aw < tm.minWidth){
11303                 w = tm.minWidth;
11304             }else{
11305                 w = aw;
11306             }
11307         }
11308         //tipBody.setWidth(w);
11309         el.setWidth(parseInt(w, 10) + p);
11310         if(ce.autoHide === false){
11311             close.setDisplayed(true);
11312             if(dd){
11313                 dd.unlock();
11314             }
11315         }else{
11316             close.setDisplayed(false);
11317             if(dd){
11318                 dd.lock();
11319             }
11320         }
11321         if(xy){
11322             el.avoidY = xy[1]-18;
11323             el.setXY(xy);
11324         }
11325         if(tm.animate){
11326             el.setOpacity(.1);
11327             el.setStyle("visibility", "visible");
11328             el.fadeIn({callback: afterShow});
11329         }else{
11330             afterShow();
11331         }
11332     };
11333     
11334     var afterShow = function(){
11335         if(ce){
11336             el.show();
11337             esc.enable();
11338             if(tm.autoDismiss && ce.autoHide !== false){
11339                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11340             }
11341         }
11342     };
11343     
11344     var hide = function(noanim){
11345         clearTimeout(dismissProc);
11346         clearTimeout(hideProc);
11347         ce = null;
11348         if(el.isVisible()){
11349             esc.disable();
11350             if(noanim !== true && tm.animate){
11351                 el.fadeOut({callback: afterHide});
11352             }else{
11353                 afterHide();
11354             } 
11355         }
11356     };
11357     
11358     var afterHide = function(){
11359         el.hide();
11360         if(removeCls){
11361             el.removeClass(removeCls);
11362             removeCls = null;
11363         }
11364     };
11365     
11366     return {
11367         /**
11368         * @cfg {Number} minWidth
11369         * The minimum width of the quick tip (defaults to 40)
11370         */
11371        minWidth : 40,
11372         /**
11373         * @cfg {Number} maxWidth
11374         * The maximum width of the quick tip (defaults to 300)
11375         */
11376        maxWidth : 300,
11377         /**
11378         * @cfg {Boolean} interceptTitles
11379         * True to automatically use the element's DOM title value if available (defaults to false)
11380         */
11381        interceptTitles : false,
11382         /**
11383         * @cfg {Boolean} trackMouse
11384         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11385         */
11386        trackMouse : false,
11387         /**
11388         * @cfg {Boolean} hideOnClick
11389         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11390         */
11391        hideOnClick : true,
11392         /**
11393         * @cfg {Number} showDelay
11394         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11395         */
11396        showDelay : 500,
11397         /**
11398         * @cfg {Number} hideDelay
11399         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11400         */
11401        hideDelay : 200,
11402         /**
11403         * @cfg {Boolean} autoHide
11404         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11405         * Used in conjunction with hideDelay.
11406         */
11407        autoHide : true,
11408         /**
11409         * @cfg {Boolean}
11410         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11411         * (defaults to true).  Used in conjunction with autoDismissDelay.
11412         */
11413        autoDismiss : true,
11414         /**
11415         * @cfg {Number}
11416         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11417         */
11418        autoDismissDelay : 5000,
11419        /**
11420         * @cfg {Boolean} animate
11421         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11422         */
11423        animate : false,
11424
11425        /**
11426         * @cfg {String} title
11427         * Title text to display (defaults to '').  This can be any valid HTML markup.
11428         */
11429         title: '',
11430        /**
11431         * @cfg {String} text
11432         * Body text to display (defaults to '').  This can be any valid HTML markup.
11433         */
11434         text : '',
11435        /**
11436         * @cfg {String} cls
11437         * A CSS class to apply to the base quick tip element (defaults to '').
11438         */
11439         cls : '',
11440        /**
11441         * @cfg {Number} width
11442         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11443         * minWidth or maxWidth.
11444         */
11445         width : null,
11446
11447     /**
11448      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11449      * or display QuickTips in a page.
11450      */
11451        init : function(){
11452           tm = Roo.QuickTips;
11453           cfg = tm.tagConfig;
11454           if(!inited){
11455               if(!Roo.isReady){ // allow calling of init() before onReady
11456                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11457                   return;
11458               }
11459               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11460               el.fxDefaults = {stopFx: true};
11461               // maximum custom styling
11462               //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>');
11463               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>');              
11464               tipTitle = el.child('h3');
11465               tipTitle.enableDisplayMode("block");
11466               tipBody = el.child('div.x-tip-bd');
11467               tipBodyText = el.child('div.x-tip-bd-inner');
11468               //bdLeft = el.child('div.x-tip-bd-left');
11469               //bdRight = el.child('div.x-tip-bd-right');
11470               close = el.child('div.x-tip-close');
11471               close.enableDisplayMode("block");
11472               close.on("click", hide);
11473               var d = Roo.get(document);
11474               d.on("mousedown", onDown);
11475               d.on("mouseover", onOver);
11476               d.on("mouseout", onOut);
11477               d.on("mousemove", onMove);
11478               esc = d.addKeyListener(27, hide);
11479               esc.disable();
11480               if(Roo.dd.DD){
11481                   dd = el.initDD("default", null, {
11482                       onDrag : function(){
11483                           el.sync();  
11484                       }
11485                   });
11486                   dd.setHandleElId(tipTitle.id);
11487                   dd.lock();
11488               }
11489               inited = true;
11490           }
11491           this.enable(); 
11492        },
11493
11494     /**
11495      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11496      * are supported:
11497      * <pre>
11498 Property    Type                   Description
11499 ----------  ---------------------  ------------------------------------------------------------------------
11500 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11501      * </ul>
11502      * @param {Object} config The config object
11503      */
11504        register : function(config){
11505            var cs = config instanceof Array ? config : arguments;
11506            for(var i = 0, len = cs.length; i < len; i++) {
11507                var c = cs[i];
11508                var target = c.target;
11509                if(target){
11510                    if(target instanceof Array){
11511                        for(var j = 0, jlen = target.length; j < jlen; j++){
11512                            tagEls[target[j]] = c;
11513                        }
11514                    }else{
11515                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11516                    }
11517                }
11518            }
11519        },
11520
11521     /**
11522      * Removes this quick tip from its element and destroys it.
11523      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11524      */
11525        unregister : function(el){
11526            delete tagEls[Roo.id(el)];
11527        },
11528
11529     /**
11530      * Enable this quick tip.
11531      */
11532        enable : function(){
11533            if(inited && disabled){
11534                locks.pop();
11535                if(locks.length < 1){
11536                    disabled = false;
11537                }
11538            }
11539        },
11540
11541     /**
11542      * Disable this quick tip.
11543      */
11544        disable : function(){
11545           disabled = true;
11546           clearTimeout(showProc);
11547           clearTimeout(hideProc);
11548           clearTimeout(dismissProc);
11549           if(ce){
11550               hide(true);
11551           }
11552           locks.push(1);
11553        },
11554
11555     /**
11556      * Returns true if the quick tip is enabled, else false.
11557      */
11558        isEnabled : function(){
11559             return !disabled;
11560        },
11561
11562         // private
11563        tagConfig : {
11564            namespace : "roo", // was ext?? this may break..
11565            alt_namespace : "ext",
11566            attribute : "qtip",
11567            width : "width",
11568            target : "target",
11569            title : "qtitle",
11570            hide : "hide",
11571            cls : "qclass"
11572        }
11573    };
11574 }();
11575
11576 // backwards compat
11577 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11578  * Based on:
11579  * Ext JS Library 1.1.1
11580  * Copyright(c) 2006-2007, Ext JS, LLC.
11581  *
11582  * Originally Released Under LGPL - original licence link has changed is not relivant.
11583  *
11584  * Fork - LGPL
11585  * <script type="text/javascript">
11586  */
11587  
11588
11589 /**
11590  * @class Roo.tree.TreePanel
11591  * @extends Roo.data.Tree
11592
11593  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11594  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11595  * @cfg {Boolean} enableDD true to enable drag and drop
11596  * @cfg {Boolean} enableDrag true to enable just drag
11597  * @cfg {Boolean} enableDrop true to enable just drop
11598  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11599  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11600  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11601  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11602  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11603  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11604  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11605  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11606  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11607  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11608  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11609  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11610  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11611  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11612  * @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>
11613  * @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>
11614  * 
11615  * @constructor
11616  * @param {String/HTMLElement/Element} el The container element
11617  * @param {Object} config
11618  */
11619 Roo.tree.TreePanel = function(el, config){
11620     var root = false;
11621     var loader = false;
11622     if (config.root) {
11623         root = config.root;
11624         delete config.root;
11625     }
11626     if (config.loader) {
11627         loader = config.loader;
11628         delete config.loader;
11629     }
11630     
11631     Roo.apply(this, config);
11632     Roo.tree.TreePanel.superclass.constructor.call(this);
11633     this.el = Roo.get(el);
11634     this.el.addClass('x-tree');
11635     //console.log(root);
11636     if (root) {
11637         this.setRootNode( Roo.factory(root, Roo.tree));
11638     }
11639     if (loader) {
11640         this.loader = Roo.factory(loader, Roo.tree);
11641     }
11642    /**
11643     * Read-only. The id of the container element becomes this TreePanel's id.
11644     */
11645     this.id = this.el.id;
11646     this.addEvents({
11647         /**
11648         * @event beforeload
11649         * Fires before a node is loaded, return false to cancel
11650         * @param {Node} node The node being loaded
11651         */
11652         "beforeload" : true,
11653         /**
11654         * @event load
11655         * Fires when a node is loaded
11656         * @param {Node} node The node that was loaded
11657         */
11658         "load" : true,
11659         /**
11660         * @event textchange
11661         * Fires when the text for a node is changed
11662         * @param {Node} node The node
11663         * @param {String} text The new text
11664         * @param {String} oldText The old text
11665         */
11666         "textchange" : true,
11667         /**
11668         * @event beforeexpand
11669         * Fires before a node is expanded, return false to cancel.
11670         * @param {Node} node The node
11671         * @param {Boolean} deep
11672         * @param {Boolean} anim
11673         */
11674         "beforeexpand" : true,
11675         /**
11676         * @event beforecollapse
11677         * Fires before a node is collapsed, return false to cancel.
11678         * @param {Node} node The node
11679         * @param {Boolean} deep
11680         * @param {Boolean} anim
11681         */
11682         "beforecollapse" : true,
11683         /**
11684         * @event expand
11685         * Fires when a node is expanded
11686         * @param {Node} node The node
11687         */
11688         "expand" : true,
11689         /**
11690         * @event disabledchange
11691         * Fires when the disabled status of a node changes
11692         * @param {Node} node The node
11693         * @param {Boolean} disabled
11694         */
11695         "disabledchange" : true,
11696         /**
11697         * @event collapse
11698         * Fires when a node is collapsed
11699         * @param {Node} node The node
11700         */
11701         "collapse" : true,
11702         /**
11703         * @event beforeclick
11704         * Fires before click processing on a node. Return false to cancel the default action.
11705         * @param {Node} node The node
11706         * @param {Roo.EventObject} e The event object
11707         */
11708         "beforeclick":true,
11709         /**
11710         * @event checkchange
11711         * Fires when a node with a checkbox's checked property changes
11712         * @param {Node} this This node
11713         * @param {Boolean} checked
11714         */
11715         "checkchange":true,
11716         /**
11717         * @event click
11718         * Fires when a node is clicked
11719         * @param {Node} node The node
11720         * @param {Roo.EventObject} e The event object
11721         */
11722         "click":true,
11723         /**
11724         * @event dblclick
11725         * Fires when a node is double clicked
11726         * @param {Node} node The node
11727         * @param {Roo.EventObject} e The event object
11728         */
11729         "dblclick":true,
11730         /**
11731         * @event contextmenu
11732         * Fires when a node is right clicked
11733         * @param {Node} node The node
11734         * @param {Roo.EventObject} e The event object
11735         */
11736         "contextmenu":true,
11737         /**
11738         * @event beforechildrenrendered
11739         * Fires right before the child nodes for a node are rendered
11740         * @param {Node} node The node
11741         */
11742         "beforechildrenrendered":true,
11743         /**
11744         * @event startdrag
11745         * Fires when a node starts being dragged
11746         * @param {Roo.tree.TreePanel} this
11747         * @param {Roo.tree.TreeNode} node
11748         * @param {event} e The raw browser event
11749         */ 
11750        "startdrag" : true,
11751        /**
11752         * @event enddrag
11753         * Fires when a drag operation is complete
11754         * @param {Roo.tree.TreePanel} this
11755         * @param {Roo.tree.TreeNode} node
11756         * @param {event} e The raw browser event
11757         */
11758        "enddrag" : true,
11759        /**
11760         * @event dragdrop
11761         * Fires when a dragged node is dropped on a valid DD target
11762         * @param {Roo.tree.TreePanel} this
11763         * @param {Roo.tree.TreeNode} node
11764         * @param {DD} dd The dd it was dropped on
11765         * @param {event} e The raw browser event
11766         */
11767        "dragdrop" : true,
11768        /**
11769         * @event beforenodedrop
11770         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11771         * passed to handlers has the following properties:<br />
11772         * <ul style="padding:5px;padding-left:16px;">
11773         * <li>tree - The TreePanel</li>
11774         * <li>target - The node being targeted for the drop</li>
11775         * <li>data - The drag data from the drag source</li>
11776         * <li>point - The point of the drop - append, above or below</li>
11777         * <li>source - The drag source</li>
11778         * <li>rawEvent - Raw mouse event</li>
11779         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11780         * to be inserted by setting them on this object.</li>
11781         * <li>cancel - Set this to true to cancel the drop.</li>
11782         * </ul>
11783         * @param {Object} dropEvent
11784         */
11785        "beforenodedrop" : true,
11786        /**
11787         * @event nodedrop
11788         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11789         * passed to handlers has the following properties:<br />
11790         * <ul style="padding:5px;padding-left:16px;">
11791         * <li>tree - The TreePanel</li>
11792         * <li>target - The node being targeted for the drop</li>
11793         * <li>data - The drag data from the drag source</li>
11794         * <li>point - The point of the drop - append, above or below</li>
11795         * <li>source - The drag source</li>
11796         * <li>rawEvent - Raw mouse event</li>
11797         * <li>dropNode - Dropped node(s).</li>
11798         * </ul>
11799         * @param {Object} dropEvent
11800         */
11801        "nodedrop" : true,
11802         /**
11803         * @event nodedragover
11804         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11805         * passed to handlers has the following properties:<br />
11806         * <ul style="padding:5px;padding-left:16px;">
11807         * <li>tree - The TreePanel</li>
11808         * <li>target - The node being targeted for the drop</li>
11809         * <li>data - The drag data from the drag source</li>
11810         * <li>point - The point of the drop - append, above or below</li>
11811         * <li>source - The drag source</li>
11812         * <li>rawEvent - Raw mouse event</li>
11813         * <li>dropNode - Drop node(s) provided by the source.</li>
11814         * <li>cancel - Set this to true to signal drop not allowed.</li>
11815         * </ul>
11816         * @param {Object} dragOverEvent
11817         */
11818        "nodedragover" : true
11819         
11820     });
11821     if(this.singleExpand){
11822        this.on("beforeexpand", this.restrictExpand, this);
11823     }
11824     if (this.editor) {
11825         this.editor.tree = this;
11826         this.editor = Roo.factory(this.editor, Roo.tree);
11827     }
11828     
11829     if (this.selModel) {
11830         this.selModel = Roo.factory(this.selModel, Roo.tree);
11831     }
11832    
11833 };
11834 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11835     rootVisible : true,
11836     animate: Roo.enableFx,
11837     lines : true,
11838     enableDD : false,
11839     hlDrop : Roo.enableFx,
11840   
11841     renderer: false,
11842     
11843     rendererTip: false,
11844     // private
11845     restrictExpand : function(node){
11846         var p = node.parentNode;
11847         if(p){
11848             if(p.expandedChild && p.expandedChild.parentNode == p){
11849                 p.expandedChild.collapse();
11850             }
11851             p.expandedChild = node;
11852         }
11853     },
11854
11855     // private override
11856     setRootNode : function(node){
11857         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11858         if(!this.rootVisible){
11859             node.ui = new Roo.tree.RootTreeNodeUI(node);
11860         }
11861         return node;
11862     },
11863
11864     /**
11865      * Returns the container element for this TreePanel
11866      */
11867     getEl : function(){
11868         return this.el;
11869     },
11870
11871     /**
11872      * Returns the default TreeLoader for this TreePanel
11873      */
11874     getLoader : function(){
11875         return this.loader;
11876     },
11877
11878     /**
11879      * Expand all nodes
11880      */
11881     expandAll : function(){
11882         this.root.expand(true);
11883     },
11884
11885     /**
11886      * Collapse all nodes
11887      */
11888     collapseAll : function(){
11889         this.root.collapse(true);
11890     },
11891
11892     /**
11893      * Returns the selection model used by this TreePanel
11894      */
11895     getSelectionModel : function(){
11896         if(!this.selModel){
11897             this.selModel = new Roo.tree.DefaultSelectionModel();
11898         }
11899         return this.selModel;
11900     },
11901
11902     /**
11903      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11904      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11905      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11906      * @return {Array}
11907      */
11908     getChecked : function(a, startNode){
11909         startNode = startNode || this.root;
11910         var r = [];
11911         var f = function(){
11912             if(this.attributes.checked){
11913                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11914             }
11915         }
11916         startNode.cascade(f);
11917         return r;
11918     },
11919
11920     /**
11921      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11922      * @param {String} path
11923      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11924      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11925      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11926      */
11927     expandPath : function(path, attr, callback){
11928         attr = attr || "id";
11929         var keys = path.split(this.pathSeparator);
11930         var curNode = this.root;
11931         if(curNode.attributes[attr] != keys[1]){ // invalid root
11932             if(callback){
11933                 callback(false, null);
11934             }
11935             return;
11936         }
11937         var index = 1;
11938         var f = function(){
11939             if(++index == keys.length){
11940                 if(callback){
11941                     callback(true, curNode);
11942                 }
11943                 return;
11944             }
11945             var c = curNode.findChild(attr, keys[index]);
11946             if(!c){
11947                 if(callback){
11948                     callback(false, curNode);
11949                 }
11950                 return;
11951             }
11952             curNode = c;
11953             c.expand(false, false, f);
11954         };
11955         curNode.expand(false, false, f);
11956     },
11957
11958     /**
11959      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11960      * @param {String} path
11961      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11962      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11963      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11964      */
11965     selectPath : function(path, attr, callback){
11966         attr = attr || "id";
11967         var keys = path.split(this.pathSeparator);
11968         var v = keys.pop();
11969         if(keys.length > 0){
11970             var f = function(success, node){
11971                 if(success && node){
11972                     var n = node.findChild(attr, v);
11973                     if(n){
11974                         n.select();
11975                         if(callback){
11976                             callback(true, n);
11977                         }
11978                     }else if(callback){
11979                         callback(false, n);
11980                     }
11981                 }else{
11982                     if(callback){
11983                         callback(false, n);
11984                     }
11985                 }
11986             };
11987             this.expandPath(keys.join(this.pathSeparator), attr, f);
11988         }else{
11989             this.root.select();
11990             if(callback){
11991                 callback(true, this.root);
11992             }
11993         }
11994     },
11995
11996     getTreeEl : function(){
11997         return this.el;
11998     },
11999
12000     /**
12001      * Trigger rendering of this TreePanel
12002      */
12003     render : function(){
12004         if (this.innerCt) {
12005             return this; // stop it rendering more than once!!
12006         }
12007         
12008         this.innerCt = this.el.createChild({tag:"ul",
12009                cls:"x-tree-root-ct " +
12010                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12011
12012         if(this.containerScroll){
12013             Roo.dd.ScrollManager.register(this.el);
12014         }
12015         if((this.enableDD || this.enableDrop) && !this.dropZone){
12016            /**
12017             * The dropZone used by this tree if drop is enabled
12018             * @type Roo.tree.TreeDropZone
12019             */
12020              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12021                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12022            });
12023         }
12024         if((this.enableDD || this.enableDrag) && !this.dragZone){
12025            /**
12026             * The dragZone used by this tree if drag is enabled
12027             * @type Roo.tree.TreeDragZone
12028             */
12029             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12030                ddGroup: this.ddGroup || "TreeDD",
12031                scroll: this.ddScroll
12032            });
12033         }
12034         this.getSelectionModel().init(this);
12035         if (!this.root) {
12036             Roo.log("ROOT not set in tree");
12037             return this;
12038         }
12039         this.root.render();
12040         if(!this.rootVisible){
12041             this.root.renderChildren();
12042         }
12043         return this;
12044     }
12045 });/*
12046  * Based on:
12047  * Ext JS Library 1.1.1
12048  * Copyright(c) 2006-2007, Ext JS, LLC.
12049  *
12050  * Originally Released Under LGPL - original licence link has changed is not relivant.
12051  *
12052  * Fork - LGPL
12053  * <script type="text/javascript">
12054  */
12055  
12056
12057 /**
12058  * @class Roo.tree.DefaultSelectionModel
12059  * @extends Roo.util.Observable
12060  * The default single selection for a TreePanel.
12061  * @param {Object} cfg Configuration
12062  */
12063 Roo.tree.DefaultSelectionModel = function(cfg){
12064    this.selNode = null;
12065    
12066    
12067    
12068    this.addEvents({
12069        /**
12070         * @event selectionchange
12071         * Fires when the selected node changes
12072         * @param {DefaultSelectionModel} this
12073         * @param {TreeNode} node the new selection
12074         */
12075        "selectionchange" : true,
12076
12077        /**
12078         * @event beforeselect
12079         * Fires before the selected node changes, return false to cancel the change
12080         * @param {DefaultSelectionModel} this
12081         * @param {TreeNode} node the new selection
12082         * @param {TreeNode} node the old selection
12083         */
12084        "beforeselect" : true
12085    });
12086    
12087     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12088 };
12089
12090 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12091     init : function(tree){
12092         this.tree = tree;
12093         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12094         tree.on("click", this.onNodeClick, this);
12095     },
12096     
12097     onNodeClick : function(node, e){
12098         if (e.ctrlKey && this.selNode == node)  {
12099             this.unselect(node);
12100             return;
12101         }
12102         this.select(node);
12103     },
12104     
12105     /**
12106      * Select a node.
12107      * @param {TreeNode} node The node to select
12108      * @return {TreeNode} The selected node
12109      */
12110     select : function(node){
12111         var last = this.selNode;
12112         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12113             if(last){
12114                 last.ui.onSelectedChange(false);
12115             }
12116             this.selNode = node;
12117             node.ui.onSelectedChange(true);
12118             this.fireEvent("selectionchange", this, node, last);
12119         }
12120         return node;
12121     },
12122     
12123     /**
12124      * Deselect a node.
12125      * @param {TreeNode} node The node to unselect
12126      */
12127     unselect : function(node){
12128         if(this.selNode == node){
12129             this.clearSelections();
12130         }    
12131     },
12132     
12133     /**
12134      * Clear all selections
12135      */
12136     clearSelections : function(){
12137         var n = this.selNode;
12138         if(n){
12139             n.ui.onSelectedChange(false);
12140             this.selNode = null;
12141             this.fireEvent("selectionchange", this, null);
12142         }
12143         return n;
12144     },
12145     
12146     /**
12147      * Get the selected node
12148      * @return {TreeNode} The selected node
12149      */
12150     getSelectedNode : function(){
12151         return this.selNode;    
12152     },
12153     
12154     /**
12155      * Returns true if the node is selected
12156      * @param {TreeNode} node The node to check
12157      * @return {Boolean}
12158      */
12159     isSelected : function(node){
12160         return this.selNode == node;  
12161     },
12162
12163     /**
12164      * Selects the node above the selected node in the tree, intelligently walking the nodes
12165      * @return TreeNode The new selection
12166      */
12167     selectPrevious : function(){
12168         var s = this.selNode || this.lastSelNode;
12169         if(!s){
12170             return null;
12171         }
12172         var ps = s.previousSibling;
12173         if(ps){
12174             if(!ps.isExpanded() || ps.childNodes.length < 1){
12175                 return this.select(ps);
12176             } else{
12177                 var lc = ps.lastChild;
12178                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12179                     lc = lc.lastChild;
12180                 }
12181                 return this.select(lc);
12182             }
12183         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12184             return this.select(s.parentNode);
12185         }
12186         return null;
12187     },
12188
12189     /**
12190      * Selects the node above the selected node in the tree, intelligently walking the nodes
12191      * @return TreeNode The new selection
12192      */
12193     selectNext : function(){
12194         var s = this.selNode || this.lastSelNode;
12195         if(!s){
12196             return null;
12197         }
12198         if(s.firstChild && s.isExpanded()){
12199              return this.select(s.firstChild);
12200          }else if(s.nextSibling){
12201              return this.select(s.nextSibling);
12202          }else if(s.parentNode){
12203             var newS = null;
12204             s.parentNode.bubble(function(){
12205                 if(this.nextSibling){
12206                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12207                     return false;
12208                 }
12209             });
12210             return newS;
12211          }
12212         return null;
12213     },
12214
12215     onKeyDown : function(e){
12216         var s = this.selNode || this.lastSelNode;
12217         // undesirable, but required
12218         var sm = this;
12219         if(!s){
12220             return;
12221         }
12222         var k = e.getKey();
12223         switch(k){
12224              case e.DOWN:
12225                  e.stopEvent();
12226                  this.selectNext();
12227              break;
12228              case e.UP:
12229                  e.stopEvent();
12230                  this.selectPrevious();
12231              break;
12232              case e.RIGHT:
12233                  e.preventDefault();
12234                  if(s.hasChildNodes()){
12235                      if(!s.isExpanded()){
12236                          s.expand();
12237                      }else if(s.firstChild){
12238                          this.select(s.firstChild, e);
12239                      }
12240                  }
12241              break;
12242              case e.LEFT:
12243                  e.preventDefault();
12244                  if(s.hasChildNodes() && s.isExpanded()){
12245                      s.collapse();
12246                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12247                      this.select(s.parentNode, e);
12248                  }
12249              break;
12250         };
12251     }
12252 });
12253
12254 /**
12255  * @class Roo.tree.MultiSelectionModel
12256  * @extends Roo.util.Observable
12257  * Multi selection for a TreePanel.
12258  * @param {Object} cfg Configuration
12259  */
12260 Roo.tree.MultiSelectionModel = function(){
12261    this.selNodes = [];
12262    this.selMap = {};
12263    this.addEvents({
12264        /**
12265         * @event selectionchange
12266         * Fires when the selected nodes change
12267         * @param {MultiSelectionModel} this
12268         * @param {Array} nodes Array of the selected nodes
12269         */
12270        "selectionchange" : true
12271    });
12272    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12273    
12274 };
12275
12276 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12277     init : function(tree){
12278         this.tree = tree;
12279         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12280         tree.on("click", this.onNodeClick, this);
12281     },
12282     
12283     onNodeClick : function(node, e){
12284         this.select(node, e, e.ctrlKey);
12285     },
12286     
12287     /**
12288      * Select a node.
12289      * @param {TreeNode} node The node to select
12290      * @param {EventObject} e (optional) An event associated with the selection
12291      * @param {Boolean} keepExisting True to retain existing selections
12292      * @return {TreeNode} The selected node
12293      */
12294     select : function(node, e, keepExisting){
12295         if(keepExisting !== true){
12296             this.clearSelections(true);
12297         }
12298         if(this.isSelected(node)){
12299             this.lastSelNode = node;
12300             return node;
12301         }
12302         this.selNodes.push(node);
12303         this.selMap[node.id] = node;
12304         this.lastSelNode = node;
12305         node.ui.onSelectedChange(true);
12306         this.fireEvent("selectionchange", this, this.selNodes);
12307         return node;
12308     },
12309     
12310     /**
12311      * Deselect a node.
12312      * @param {TreeNode} node The node to unselect
12313      */
12314     unselect : function(node){
12315         if(this.selMap[node.id]){
12316             node.ui.onSelectedChange(false);
12317             var sn = this.selNodes;
12318             var index = -1;
12319             if(sn.indexOf){
12320                 index = sn.indexOf(node);
12321             }else{
12322                 for(var i = 0, len = sn.length; i < len; i++){
12323                     if(sn[i] == node){
12324                         index = i;
12325                         break;
12326                     }
12327                 }
12328             }
12329             if(index != -1){
12330                 this.selNodes.splice(index, 1);
12331             }
12332             delete this.selMap[node.id];
12333             this.fireEvent("selectionchange", this, this.selNodes);
12334         }
12335     },
12336     
12337     /**
12338      * Clear all selections
12339      */
12340     clearSelections : function(suppressEvent){
12341         var sn = this.selNodes;
12342         if(sn.length > 0){
12343             for(var i = 0, len = sn.length; i < len; i++){
12344                 sn[i].ui.onSelectedChange(false);
12345             }
12346             this.selNodes = [];
12347             this.selMap = {};
12348             if(suppressEvent !== true){
12349                 this.fireEvent("selectionchange", this, this.selNodes);
12350             }
12351         }
12352     },
12353     
12354     /**
12355      * Returns true if the node is selected
12356      * @param {TreeNode} node The node to check
12357      * @return {Boolean}
12358      */
12359     isSelected : function(node){
12360         return this.selMap[node.id] ? true : false;  
12361     },
12362     
12363     /**
12364      * Returns an array of the selected nodes
12365      * @return {Array}
12366      */
12367     getSelectedNodes : function(){
12368         return this.selNodes;    
12369     },
12370
12371     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12372
12373     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12374
12375     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12376 });/*
12377  * Based on:
12378  * Ext JS Library 1.1.1
12379  * Copyright(c) 2006-2007, Ext JS, LLC.
12380  *
12381  * Originally Released Under LGPL - original licence link has changed is not relivant.
12382  *
12383  * Fork - LGPL
12384  * <script type="text/javascript">
12385  */
12386  
12387 /**
12388  * @class Roo.tree.TreeNode
12389  * @extends Roo.data.Node
12390  * @cfg {String} text The text for this node
12391  * @cfg {Boolean} expanded true to start the node expanded
12392  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12393  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12394  * @cfg {Boolean} disabled true to start the node disabled
12395  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12396  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12397  * @cfg {String} cls A css class to be added to the node
12398  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12399  * @cfg {String} href URL of the link used for the node (defaults to #)
12400  * @cfg {String} hrefTarget target frame for the link
12401  * @cfg {String} qtip An Ext QuickTip for the node
12402  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12403  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12404  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12405  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12406  * (defaults to undefined with no checkbox rendered)
12407  * @constructor
12408  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12409  */
12410 Roo.tree.TreeNode = function(attributes){
12411     attributes = attributes || {};
12412     if(typeof attributes == "string"){
12413         attributes = {text: attributes};
12414     }
12415     this.childrenRendered = false;
12416     this.rendered = false;
12417     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12418     this.expanded = attributes.expanded === true;
12419     this.isTarget = attributes.isTarget !== false;
12420     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12421     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12422
12423     /**
12424      * Read-only. The text for this node. To change it use setText().
12425      * @type String
12426      */
12427     this.text = attributes.text;
12428     /**
12429      * True if this node is disabled.
12430      * @type Boolean
12431      */
12432     this.disabled = attributes.disabled === true;
12433
12434     this.addEvents({
12435         /**
12436         * @event textchange
12437         * Fires when the text for this node is changed
12438         * @param {Node} this This node
12439         * @param {String} text The new text
12440         * @param {String} oldText The old text
12441         */
12442         "textchange" : true,
12443         /**
12444         * @event beforeexpand
12445         * Fires before this node is expanded, return false to cancel.
12446         * @param {Node} this This node
12447         * @param {Boolean} deep
12448         * @param {Boolean} anim
12449         */
12450         "beforeexpand" : true,
12451         /**
12452         * @event beforecollapse
12453         * Fires before this node is collapsed, return false to cancel.
12454         * @param {Node} this This node
12455         * @param {Boolean} deep
12456         * @param {Boolean} anim
12457         */
12458         "beforecollapse" : true,
12459         /**
12460         * @event expand
12461         * Fires when this node is expanded
12462         * @param {Node} this This node
12463         */
12464         "expand" : true,
12465         /**
12466         * @event disabledchange
12467         * Fires when the disabled status of this node changes
12468         * @param {Node} this This node
12469         * @param {Boolean} disabled
12470         */
12471         "disabledchange" : true,
12472         /**
12473         * @event collapse
12474         * Fires when this node is collapsed
12475         * @param {Node} this This node
12476         */
12477         "collapse" : true,
12478         /**
12479         * @event beforeclick
12480         * Fires before click processing. Return false to cancel the default action.
12481         * @param {Node} this This node
12482         * @param {Roo.EventObject} e The event object
12483         */
12484         "beforeclick":true,
12485         /**
12486         * @event checkchange
12487         * Fires when a node with a checkbox's checked property changes
12488         * @param {Node} this This node
12489         * @param {Boolean} checked
12490         */
12491         "checkchange":true,
12492         /**
12493         * @event click
12494         * Fires when this node is clicked
12495         * @param {Node} this This node
12496         * @param {Roo.EventObject} e The event object
12497         */
12498         "click":true,
12499         /**
12500         * @event dblclick
12501         * Fires when this node is double clicked
12502         * @param {Node} this This node
12503         * @param {Roo.EventObject} e The event object
12504         */
12505         "dblclick":true,
12506         /**
12507         * @event contextmenu
12508         * Fires when this node is right clicked
12509         * @param {Node} this This node
12510         * @param {Roo.EventObject} e The event object
12511         */
12512         "contextmenu":true,
12513         /**
12514         * @event beforechildrenrendered
12515         * Fires right before the child nodes for this node are rendered
12516         * @param {Node} this This node
12517         */
12518         "beforechildrenrendered":true
12519     });
12520
12521     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12522
12523     /**
12524      * Read-only. The UI for this node
12525      * @type TreeNodeUI
12526      */
12527     this.ui = new uiClass(this);
12528     
12529     // finally support items[]
12530     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12531         return;
12532     }
12533     
12534     
12535     Roo.each(this.attributes.items, function(c) {
12536         this.appendChild(Roo.factory(c,Roo.Tree));
12537     }, this);
12538     delete this.attributes.items;
12539     
12540     
12541     
12542 };
12543 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12544     preventHScroll: true,
12545     /**
12546      * Returns true if this node is expanded
12547      * @return {Boolean}
12548      */
12549     isExpanded : function(){
12550         return this.expanded;
12551     },
12552
12553     /**
12554      * Returns the UI object for this node
12555      * @return {TreeNodeUI}
12556      */
12557     getUI : function(){
12558         return this.ui;
12559     },
12560
12561     // private override
12562     setFirstChild : function(node){
12563         var of = this.firstChild;
12564         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12565         if(this.childrenRendered && of && node != of){
12566             of.renderIndent(true, true);
12567         }
12568         if(this.rendered){
12569             this.renderIndent(true, true);
12570         }
12571     },
12572
12573     // private override
12574     setLastChild : function(node){
12575         var ol = this.lastChild;
12576         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12577         if(this.childrenRendered && ol && node != ol){
12578             ol.renderIndent(true, true);
12579         }
12580         if(this.rendered){
12581             this.renderIndent(true, true);
12582         }
12583     },
12584
12585     // these methods are overridden to provide lazy rendering support
12586     // private override
12587     appendChild : function()
12588     {
12589         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12590         if(node && this.childrenRendered){
12591             node.render();
12592         }
12593         this.ui.updateExpandIcon();
12594         return node;
12595     },
12596
12597     // private override
12598     removeChild : function(node){
12599         this.ownerTree.getSelectionModel().unselect(node);
12600         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12601         // if it's been rendered remove dom node
12602         if(this.childrenRendered){
12603             node.ui.remove();
12604         }
12605         if(this.childNodes.length < 1){
12606             this.collapse(false, false);
12607         }else{
12608             this.ui.updateExpandIcon();
12609         }
12610         if(!this.firstChild) {
12611             this.childrenRendered = false;
12612         }
12613         return node;
12614     },
12615
12616     // private override
12617     insertBefore : function(node, refNode){
12618         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12619         if(newNode && refNode && this.childrenRendered){
12620             node.render();
12621         }
12622         this.ui.updateExpandIcon();
12623         return newNode;
12624     },
12625
12626     /**
12627      * Sets the text for this node
12628      * @param {String} text
12629      */
12630     setText : function(text){
12631         var oldText = this.text;
12632         this.text = text;
12633         this.attributes.text = text;
12634         if(this.rendered){ // event without subscribing
12635             this.ui.onTextChange(this, text, oldText);
12636         }
12637         this.fireEvent("textchange", this, text, oldText);
12638     },
12639
12640     /**
12641      * Triggers selection of this node
12642      */
12643     select : function(){
12644         this.getOwnerTree().getSelectionModel().select(this);
12645     },
12646
12647     /**
12648      * Triggers deselection of this node
12649      */
12650     unselect : function(){
12651         this.getOwnerTree().getSelectionModel().unselect(this);
12652     },
12653
12654     /**
12655      * Returns true if this node is selected
12656      * @return {Boolean}
12657      */
12658     isSelected : function(){
12659         return this.getOwnerTree().getSelectionModel().isSelected(this);
12660     },
12661
12662     /**
12663      * Expand this node.
12664      * @param {Boolean} deep (optional) True to expand all children as well
12665      * @param {Boolean} anim (optional) false to cancel the default animation
12666      * @param {Function} callback (optional) A callback to be called when
12667      * expanding this node completes (does not wait for deep expand to complete).
12668      * Called with 1 parameter, this node.
12669      */
12670     expand : function(deep, anim, callback){
12671         if(!this.expanded){
12672             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12673                 return;
12674             }
12675             if(!this.childrenRendered){
12676                 this.renderChildren();
12677             }
12678             this.expanded = true;
12679             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12680                 this.ui.animExpand(function(){
12681                     this.fireEvent("expand", this);
12682                     if(typeof callback == "function"){
12683                         callback(this);
12684                     }
12685                     if(deep === true){
12686                         this.expandChildNodes(true);
12687                     }
12688                 }.createDelegate(this));
12689                 return;
12690             }else{
12691                 this.ui.expand();
12692                 this.fireEvent("expand", this);
12693                 if(typeof callback == "function"){
12694                     callback(this);
12695                 }
12696             }
12697         }else{
12698            if(typeof callback == "function"){
12699                callback(this);
12700            }
12701         }
12702         if(deep === true){
12703             this.expandChildNodes(true);
12704         }
12705     },
12706
12707     isHiddenRoot : function(){
12708         return this.isRoot && !this.getOwnerTree().rootVisible;
12709     },
12710
12711     /**
12712      * Collapse this node.
12713      * @param {Boolean} deep (optional) True to collapse all children as well
12714      * @param {Boolean} anim (optional) false to cancel the default animation
12715      */
12716     collapse : function(deep, anim){
12717         if(this.expanded && !this.isHiddenRoot()){
12718             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12719                 return;
12720             }
12721             this.expanded = false;
12722             if((this.getOwnerTree().animate && anim !== false) || anim){
12723                 this.ui.animCollapse(function(){
12724                     this.fireEvent("collapse", this);
12725                     if(deep === true){
12726                         this.collapseChildNodes(true);
12727                     }
12728                 }.createDelegate(this));
12729                 return;
12730             }else{
12731                 this.ui.collapse();
12732                 this.fireEvent("collapse", this);
12733             }
12734         }
12735         if(deep === true){
12736             var cs = this.childNodes;
12737             for(var i = 0, len = cs.length; i < len; i++) {
12738                 cs[i].collapse(true, false);
12739             }
12740         }
12741     },
12742
12743     // private
12744     delayedExpand : function(delay){
12745         if(!this.expandProcId){
12746             this.expandProcId = this.expand.defer(delay, this);
12747         }
12748     },
12749
12750     // private
12751     cancelExpand : function(){
12752         if(this.expandProcId){
12753             clearTimeout(this.expandProcId);
12754         }
12755         this.expandProcId = false;
12756     },
12757
12758     /**
12759      * Toggles expanded/collapsed state of the node
12760      */
12761     toggle : function(){
12762         if(this.expanded){
12763             this.collapse();
12764         }else{
12765             this.expand();
12766         }
12767     },
12768
12769     /**
12770      * Ensures all parent nodes are expanded
12771      */
12772     ensureVisible : function(callback){
12773         var tree = this.getOwnerTree();
12774         tree.expandPath(this.parentNode.getPath(), false, function(){
12775             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12776             Roo.callback(callback);
12777         }.createDelegate(this));
12778     },
12779
12780     /**
12781      * Expand all child nodes
12782      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12783      */
12784     expandChildNodes : function(deep){
12785         var cs = this.childNodes;
12786         for(var i = 0, len = cs.length; i < len; i++) {
12787                 cs[i].expand(deep);
12788         }
12789     },
12790
12791     /**
12792      * Collapse all child nodes
12793      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12794      */
12795     collapseChildNodes : function(deep){
12796         var cs = this.childNodes;
12797         for(var i = 0, len = cs.length; i < len; i++) {
12798                 cs[i].collapse(deep);
12799         }
12800     },
12801
12802     /**
12803      * Disables this node
12804      */
12805     disable : function(){
12806         this.disabled = true;
12807         this.unselect();
12808         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12809             this.ui.onDisableChange(this, true);
12810         }
12811         this.fireEvent("disabledchange", this, true);
12812     },
12813
12814     /**
12815      * Enables this node
12816      */
12817     enable : function(){
12818         this.disabled = false;
12819         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12820             this.ui.onDisableChange(this, false);
12821         }
12822         this.fireEvent("disabledchange", this, false);
12823     },
12824
12825     // private
12826     renderChildren : function(suppressEvent){
12827         if(suppressEvent !== false){
12828             this.fireEvent("beforechildrenrendered", this);
12829         }
12830         var cs = this.childNodes;
12831         for(var i = 0, len = cs.length; i < len; i++){
12832             cs[i].render(true);
12833         }
12834         this.childrenRendered = true;
12835     },
12836
12837     // private
12838     sort : function(fn, scope){
12839         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12840         if(this.childrenRendered){
12841             var cs = this.childNodes;
12842             for(var i = 0, len = cs.length; i < len; i++){
12843                 cs[i].render(true);
12844             }
12845         }
12846     },
12847
12848     // private
12849     render : function(bulkRender){
12850         this.ui.render(bulkRender);
12851         if(!this.rendered){
12852             this.rendered = true;
12853             if(this.expanded){
12854                 this.expanded = false;
12855                 this.expand(false, false);
12856             }
12857         }
12858     },
12859
12860     // private
12861     renderIndent : function(deep, refresh){
12862         if(refresh){
12863             this.ui.childIndent = null;
12864         }
12865         this.ui.renderIndent();
12866         if(deep === true && this.childrenRendered){
12867             var cs = this.childNodes;
12868             for(var i = 0, len = cs.length; i < len; i++){
12869                 cs[i].renderIndent(true, refresh);
12870             }
12871         }
12872     }
12873 });/*
12874  * Based on:
12875  * Ext JS Library 1.1.1
12876  * Copyright(c) 2006-2007, Ext JS, LLC.
12877  *
12878  * Originally Released Under LGPL - original licence link has changed is not relivant.
12879  *
12880  * Fork - LGPL
12881  * <script type="text/javascript">
12882  */
12883  
12884 /**
12885  * @class Roo.tree.AsyncTreeNode
12886  * @extends Roo.tree.TreeNode
12887  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12888  * @constructor
12889  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12890  */
12891  Roo.tree.AsyncTreeNode = function(config){
12892     this.loaded = false;
12893     this.loading = false;
12894     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12895     /**
12896     * @event beforeload
12897     * Fires before this node is loaded, return false to cancel
12898     * @param {Node} this This node
12899     */
12900     this.addEvents({'beforeload':true, 'load': true});
12901     /**
12902     * @event load
12903     * Fires when this node is loaded
12904     * @param {Node} this This node
12905     */
12906     /**
12907      * The loader used by this node (defaults to using the tree's defined loader)
12908      * @type TreeLoader
12909      * @property loader
12910      */
12911 };
12912 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12913     expand : function(deep, anim, callback){
12914         if(this.loading){ // if an async load is already running, waiting til it's done
12915             var timer;
12916             var f = function(){
12917                 if(!this.loading){ // done loading
12918                     clearInterval(timer);
12919                     this.expand(deep, anim, callback);
12920                 }
12921             }.createDelegate(this);
12922             timer = setInterval(f, 200);
12923             return;
12924         }
12925         if(!this.loaded){
12926             if(this.fireEvent("beforeload", this) === false){
12927                 return;
12928             }
12929             this.loading = true;
12930             this.ui.beforeLoad(this);
12931             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12932             if(loader){
12933                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12934                 return;
12935             }
12936         }
12937         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12938     },
12939     
12940     /**
12941      * Returns true if this node is currently loading
12942      * @return {Boolean}
12943      */
12944     isLoading : function(){
12945         return this.loading;  
12946     },
12947     
12948     loadComplete : function(deep, anim, callback){
12949         this.loading = false;
12950         this.loaded = true;
12951         this.ui.afterLoad(this);
12952         this.fireEvent("load", this);
12953         this.expand(deep, anim, callback);
12954     },
12955     
12956     /**
12957      * Returns true if this node has been loaded
12958      * @return {Boolean}
12959      */
12960     isLoaded : function(){
12961         return this.loaded;
12962     },
12963     
12964     hasChildNodes : function(){
12965         if(!this.isLeaf() && !this.loaded){
12966             return true;
12967         }else{
12968             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12969         }
12970     },
12971
12972     /**
12973      * Trigger a reload for this node
12974      * @param {Function} callback
12975      */
12976     reload : function(callback){
12977         this.collapse(false, false);
12978         while(this.firstChild){
12979             this.removeChild(this.firstChild);
12980         }
12981         this.childrenRendered = false;
12982         this.loaded = false;
12983         if(this.isHiddenRoot()){
12984             this.expanded = false;
12985         }
12986         this.expand(false, false, callback);
12987     }
12988 });/*
12989  * Based on:
12990  * Ext JS Library 1.1.1
12991  * Copyright(c) 2006-2007, Ext JS, LLC.
12992  *
12993  * Originally Released Under LGPL - original licence link has changed is not relivant.
12994  *
12995  * Fork - LGPL
12996  * <script type="text/javascript">
12997  */
12998  
12999 /**
13000  * @class Roo.tree.TreeNodeUI
13001  * @constructor
13002  * @param {Object} node The node to render
13003  * The TreeNode UI implementation is separate from the
13004  * tree implementation. Unless you are customizing the tree UI,
13005  * you should never have to use this directly.
13006  */
13007 Roo.tree.TreeNodeUI = function(node){
13008     this.node = node;
13009     this.rendered = false;
13010     this.animating = false;
13011     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13012 };
13013
13014 Roo.tree.TreeNodeUI.prototype = {
13015     removeChild : function(node){
13016         if(this.rendered){
13017             this.ctNode.removeChild(node.ui.getEl());
13018         }
13019     },
13020
13021     beforeLoad : function(){
13022          this.addClass("x-tree-node-loading");
13023     },
13024
13025     afterLoad : function(){
13026          this.removeClass("x-tree-node-loading");
13027     },
13028
13029     onTextChange : function(node, text, oldText){
13030         if(this.rendered){
13031             this.textNode.innerHTML = text;
13032         }
13033     },
13034
13035     onDisableChange : function(node, state){
13036         this.disabled = state;
13037         if(state){
13038             this.addClass("x-tree-node-disabled");
13039         }else{
13040             this.removeClass("x-tree-node-disabled");
13041         }
13042     },
13043
13044     onSelectedChange : function(state){
13045         if(state){
13046             this.focus();
13047             this.addClass("x-tree-selected");
13048         }else{
13049             //this.blur();
13050             this.removeClass("x-tree-selected");
13051         }
13052     },
13053
13054     onMove : function(tree, node, oldParent, newParent, index, refNode){
13055         this.childIndent = null;
13056         if(this.rendered){
13057             var targetNode = newParent.ui.getContainer();
13058             if(!targetNode){//target not rendered
13059                 this.holder = document.createElement("div");
13060                 this.holder.appendChild(this.wrap);
13061                 return;
13062             }
13063             var insertBefore = refNode ? refNode.ui.getEl() : null;
13064             if(insertBefore){
13065                 targetNode.insertBefore(this.wrap, insertBefore);
13066             }else{
13067                 targetNode.appendChild(this.wrap);
13068             }
13069             this.node.renderIndent(true);
13070         }
13071     },
13072
13073     addClass : function(cls){
13074         if(this.elNode){
13075             Roo.fly(this.elNode).addClass(cls);
13076         }
13077     },
13078
13079     removeClass : function(cls){
13080         if(this.elNode){
13081             Roo.fly(this.elNode).removeClass(cls);
13082         }
13083     },
13084
13085     remove : function(){
13086         if(this.rendered){
13087             this.holder = document.createElement("div");
13088             this.holder.appendChild(this.wrap);
13089         }
13090     },
13091
13092     fireEvent : function(){
13093         return this.node.fireEvent.apply(this.node, arguments);
13094     },
13095
13096     initEvents : function(){
13097         this.node.on("move", this.onMove, this);
13098         var E = Roo.EventManager;
13099         var a = this.anchor;
13100
13101         var el = Roo.fly(a, '_treeui');
13102
13103         if(Roo.isOpera){ // opera render bug ignores the CSS
13104             el.setStyle("text-decoration", "none");
13105         }
13106
13107         el.on("click", this.onClick, this);
13108         el.on("dblclick", this.onDblClick, this);
13109
13110         if(this.checkbox){
13111             Roo.EventManager.on(this.checkbox,
13112                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13113         }
13114
13115         el.on("contextmenu", this.onContextMenu, this);
13116
13117         var icon = Roo.fly(this.iconNode);
13118         icon.on("click", this.onClick, this);
13119         icon.on("dblclick", this.onDblClick, this);
13120         icon.on("contextmenu", this.onContextMenu, this);
13121         E.on(this.ecNode, "click", this.ecClick, this, true);
13122
13123         if(this.node.disabled){
13124             this.addClass("x-tree-node-disabled");
13125         }
13126         if(this.node.hidden){
13127             this.addClass("x-tree-node-disabled");
13128         }
13129         var ot = this.node.getOwnerTree();
13130         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13131         if(dd && (!this.node.isRoot || ot.rootVisible)){
13132             Roo.dd.Registry.register(this.elNode, {
13133                 node: this.node,
13134                 handles: this.getDDHandles(),
13135                 isHandle: false
13136             });
13137         }
13138     },
13139
13140     getDDHandles : function(){
13141         return [this.iconNode, this.textNode];
13142     },
13143
13144     hide : function(){
13145         if(this.rendered){
13146             this.wrap.style.display = "none";
13147         }
13148     },
13149
13150     show : function(){
13151         if(this.rendered){
13152             this.wrap.style.display = "";
13153         }
13154     },
13155
13156     onContextMenu : function(e){
13157         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13158             e.preventDefault();
13159             this.focus();
13160             this.fireEvent("contextmenu", this.node, e);
13161         }
13162     },
13163
13164     onClick : function(e){
13165         if(this.dropping){
13166             e.stopEvent();
13167             return;
13168         }
13169         if(this.fireEvent("beforeclick", this.node, e) !== false){
13170             if(!this.disabled && this.node.attributes.href){
13171                 this.fireEvent("click", this.node, e);
13172                 return;
13173             }
13174             e.preventDefault();
13175             if(this.disabled){
13176                 return;
13177             }
13178
13179             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13180                 this.node.toggle();
13181             }
13182
13183             this.fireEvent("click", this.node, e);
13184         }else{
13185             e.stopEvent();
13186         }
13187     },
13188
13189     onDblClick : function(e){
13190         e.preventDefault();
13191         if(this.disabled){
13192             return;
13193         }
13194         if(this.checkbox){
13195             this.toggleCheck();
13196         }
13197         if(!this.animating && this.node.hasChildNodes()){
13198             this.node.toggle();
13199         }
13200         this.fireEvent("dblclick", this.node, e);
13201     },
13202
13203     onCheckChange : function(){
13204         var checked = this.checkbox.checked;
13205         this.node.attributes.checked = checked;
13206         this.fireEvent('checkchange', this.node, checked);
13207     },
13208
13209     ecClick : function(e){
13210         if(!this.animating && this.node.hasChildNodes()){
13211             this.node.toggle();
13212         }
13213     },
13214
13215     startDrop : function(){
13216         this.dropping = true;
13217     },
13218
13219     // delayed drop so the click event doesn't get fired on a drop
13220     endDrop : function(){
13221        setTimeout(function(){
13222            this.dropping = false;
13223        }.createDelegate(this), 50);
13224     },
13225
13226     expand : function(){
13227         this.updateExpandIcon();
13228         this.ctNode.style.display = "";
13229     },
13230
13231     focus : function(){
13232         if(!this.node.preventHScroll){
13233             try{this.anchor.focus();
13234             }catch(e){}
13235         }else if(!Roo.isIE){
13236             try{
13237                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13238                 var l = noscroll.scrollLeft;
13239                 this.anchor.focus();
13240                 noscroll.scrollLeft = l;
13241             }catch(e){}
13242         }
13243     },
13244
13245     toggleCheck : function(value){
13246         var cb = this.checkbox;
13247         if(cb){
13248             cb.checked = (value === undefined ? !cb.checked : value);
13249         }
13250     },
13251
13252     blur : function(){
13253         try{
13254             this.anchor.blur();
13255         }catch(e){}
13256     },
13257
13258     animExpand : function(callback){
13259         var ct = Roo.get(this.ctNode);
13260         ct.stopFx();
13261         if(!this.node.hasChildNodes()){
13262             this.updateExpandIcon();
13263             this.ctNode.style.display = "";
13264             Roo.callback(callback);
13265             return;
13266         }
13267         this.animating = true;
13268         this.updateExpandIcon();
13269
13270         ct.slideIn('t', {
13271            callback : function(){
13272                this.animating = false;
13273                Roo.callback(callback);
13274             },
13275             scope: this,
13276             duration: this.node.ownerTree.duration || .25
13277         });
13278     },
13279
13280     highlight : function(){
13281         var tree = this.node.getOwnerTree();
13282         Roo.fly(this.wrap).highlight(
13283             tree.hlColor || "C3DAF9",
13284             {endColor: tree.hlBaseColor}
13285         );
13286     },
13287
13288     collapse : function(){
13289         this.updateExpandIcon();
13290         this.ctNode.style.display = "none";
13291     },
13292
13293     animCollapse : function(callback){
13294         var ct = Roo.get(this.ctNode);
13295         ct.enableDisplayMode('block');
13296         ct.stopFx();
13297
13298         this.animating = true;
13299         this.updateExpandIcon();
13300
13301         ct.slideOut('t', {
13302             callback : function(){
13303                this.animating = false;
13304                Roo.callback(callback);
13305             },
13306             scope: this,
13307             duration: this.node.ownerTree.duration || .25
13308         });
13309     },
13310
13311     getContainer : function(){
13312         return this.ctNode;
13313     },
13314
13315     getEl : function(){
13316         return this.wrap;
13317     },
13318
13319     appendDDGhost : function(ghostNode){
13320         ghostNode.appendChild(this.elNode.cloneNode(true));
13321     },
13322
13323     getDDRepairXY : function(){
13324         return Roo.lib.Dom.getXY(this.iconNode);
13325     },
13326
13327     onRender : function(){
13328         this.render();
13329     },
13330
13331     render : function(bulkRender){
13332         var n = this.node, a = n.attributes;
13333         var targetNode = n.parentNode ?
13334               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13335
13336         if(!this.rendered){
13337             this.rendered = true;
13338
13339             this.renderElements(n, a, targetNode, bulkRender);
13340
13341             if(a.qtip){
13342                if(this.textNode.setAttributeNS){
13343                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13344                    if(a.qtipTitle){
13345                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13346                    }
13347                }else{
13348                    this.textNode.setAttribute("ext:qtip", a.qtip);
13349                    if(a.qtipTitle){
13350                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13351                    }
13352                }
13353             }else if(a.qtipCfg){
13354                 a.qtipCfg.target = Roo.id(this.textNode);
13355                 Roo.QuickTips.register(a.qtipCfg);
13356             }
13357             this.initEvents();
13358             if(!this.node.expanded){
13359                 this.updateExpandIcon();
13360             }
13361         }else{
13362             if(bulkRender === true) {
13363                 targetNode.appendChild(this.wrap);
13364             }
13365         }
13366     },
13367
13368     renderElements : function(n, a, targetNode, bulkRender)
13369     {
13370         // add some indent caching, this helps performance when rendering a large tree
13371         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13372         var t = n.getOwnerTree();
13373         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13374         if (typeof(n.attributes.html) != 'undefined') {
13375             txt = n.attributes.html;
13376         }
13377         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13378         var cb = typeof a.checked == 'boolean';
13379         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13380         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13381             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13382             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13383             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13384             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13385             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13386              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13387                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13388             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13389             "</li>"];
13390
13391         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13392             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13393                                 n.nextSibling.ui.getEl(), buf.join(""));
13394         }else{
13395             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13396         }
13397
13398         this.elNode = this.wrap.childNodes[0];
13399         this.ctNode = this.wrap.childNodes[1];
13400         var cs = this.elNode.childNodes;
13401         this.indentNode = cs[0];
13402         this.ecNode = cs[1];
13403         this.iconNode = cs[2];
13404         var index = 3;
13405         if(cb){
13406             this.checkbox = cs[3];
13407             index++;
13408         }
13409         this.anchor = cs[index];
13410         this.textNode = cs[index].firstChild;
13411     },
13412
13413     getAnchor : function(){
13414         return this.anchor;
13415     },
13416
13417     getTextEl : function(){
13418         return this.textNode;
13419     },
13420
13421     getIconEl : function(){
13422         return this.iconNode;
13423     },
13424
13425     isChecked : function(){
13426         return this.checkbox ? this.checkbox.checked : false;
13427     },
13428
13429     updateExpandIcon : function(){
13430         if(this.rendered){
13431             var n = this.node, c1, c2;
13432             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13433             var hasChild = n.hasChildNodes();
13434             if(hasChild){
13435                 if(n.expanded){
13436                     cls += "-minus";
13437                     c1 = "x-tree-node-collapsed";
13438                     c2 = "x-tree-node-expanded";
13439                 }else{
13440                     cls += "-plus";
13441                     c1 = "x-tree-node-expanded";
13442                     c2 = "x-tree-node-collapsed";
13443                 }
13444                 if(this.wasLeaf){
13445                     this.removeClass("x-tree-node-leaf");
13446                     this.wasLeaf = false;
13447                 }
13448                 if(this.c1 != c1 || this.c2 != c2){
13449                     Roo.fly(this.elNode).replaceClass(c1, c2);
13450                     this.c1 = c1; this.c2 = c2;
13451                 }
13452             }else{
13453                 // this changes non-leafs into leafs if they have no children.
13454                 // it's not very rational behaviour..
13455                 
13456                 if(!this.wasLeaf && this.node.leaf){
13457                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13458                     delete this.c1;
13459                     delete this.c2;
13460                     this.wasLeaf = true;
13461                 }
13462             }
13463             var ecc = "x-tree-ec-icon "+cls;
13464             if(this.ecc != ecc){
13465                 this.ecNode.className = ecc;
13466                 this.ecc = ecc;
13467             }
13468         }
13469     },
13470
13471     getChildIndent : function(){
13472         if(!this.childIndent){
13473             var buf = [];
13474             var p = this.node;
13475             while(p){
13476                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13477                     if(!p.isLast()) {
13478                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13479                     } else {
13480                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13481                     }
13482                 }
13483                 p = p.parentNode;
13484             }
13485             this.childIndent = buf.join("");
13486         }
13487         return this.childIndent;
13488     },
13489
13490     renderIndent : function(){
13491         if(this.rendered){
13492             var indent = "";
13493             var p = this.node.parentNode;
13494             if(p){
13495                 indent = p.ui.getChildIndent();
13496             }
13497             if(this.indentMarkup != indent){ // don't rerender if not required
13498                 this.indentNode.innerHTML = indent;
13499                 this.indentMarkup = indent;
13500             }
13501             this.updateExpandIcon();
13502         }
13503     }
13504 };
13505
13506 Roo.tree.RootTreeNodeUI = function(){
13507     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13508 };
13509 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13510     render : function(){
13511         if(!this.rendered){
13512             var targetNode = this.node.ownerTree.innerCt.dom;
13513             this.node.expanded = true;
13514             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13515             this.wrap = this.ctNode = targetNode.firstChild;
13516         }
13517     },
13518     collapse : function(){
13519     },
13520     expand : function(){
13521     }
13522 });/*
13523  * Based on:
13524  * Ext JS Library 1.1.1
13525  * Copyright(c) 2006-2007, Ext JS, LLC.
13526  *
13527  * Originally Released Under LGPL - original licence link has changed is not relivant.
13528  *
13529  * Fork - LGPL
13530  * <script type="text/javascript">
13531  */
13532 /**
13533  * @class Roo.tree.TreeLoader
13534  * @extends Roo.util.Observable
13535  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13536  * nodes from a specified URL. The response must be a javascript Array definition
13537  * who's elements are node definition objects. eg:
13538  * <pre><code>
13539 {  success : true,
13540    data :      [
13541    
13542     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13543     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13544     ]
13545 }
13546
13547
13548 </code></pre>
13549  * <br><br>
13550  * The old style respose with just an array is still supported, but not recommended.
13551  * <br><br>
13552  *
13553  * A server request is sent, and child nodes are loaded only when a node is expanded.
13554  * The loading node's id is passed to the server under the parameter name "node" to
13555  * enable the server to produce the correct child nodes.
13556  * <br><br>
13557  * To pass extra parameters, an event handler may be attached to the "beforeload"
13558  * event, and the parameters specified in the TreeLoader's baseParams property:
13559  * <pre><code>
13560     myTreeLoader.on("beforeload", function(treeLoader, node) {
13561         this.baseParams.category = node.attributes.category;
13562     }, this);
13563 </code></pre><
13564  * This would pass an HTTP parameter called "category" to the server containing
13565  * the value of the Node's "category" attribute.
13566  * @constructor
13567  * Creates a new Treeloader.
13568  * @param {Object} config A config object containing config properties.
13569  */
13570 Roo.tree.TreeLoader = function(config){
13571     this.baseParams = {};
13572     this.requestMethod = "POST";
13573     Roo.apply(this, config);
13574
13575     this.addEvents({
13576     
13577         /**
13578          * @event beforeload
13579          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13580          * @param {Object} This TreeLoader object.
13581          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13582          * @param {Object} callback The callback function specified in the {@link #load} call.
13583          */
13584         beforeload : true,
13585         /**
13586          * @event load
13587          * Fires when the node has been successfuly loaded.
13588          * @param {Object} This TreeLoader object.
13589          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13590          * @param {Object} response The response object containing the data from the server.
13591          */
13592         load : true,
13593         /**
13594          * @event loadexception
13595          * Fires if the network request failed.
13596          * @param {Object} This TreeLoader object.
13597          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13598          * @param {Object} response The response object containing the data from the server.
13599          */
13600         loadexception : true,
13601         /**
13602          * @event create
13603          * Fires before a node is created, enabling you to return custom Node types 
13604          * @param {Object} This TreeLoader object.
13605          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13606          */
13607         create : true
13608     });
13609
13610     Roo.tree.TreeLoader.superclass.constructor.call(this);
13611 };
13612
13613 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13614     /**
13615     * @cfg {String} dataUrl The URL from which to request a Json string which
13616     * specifies an array of node definition object representing the child nodes
13617     * to be loaded.
13618     */
13619     /**
13620     * @cfg {String} requestMethod either GET or POST
13621     * defaults to POST (due to BC)
13622     * to be loaded.
13623     */
13624     /**
13625     * @cfg {Object} baseParams (optional) An object containing properties which
13626     * specify HTTP parameters to be passed to each request for child nodes.
13627     */
13628     /**
13629     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13630     * created by this loader. If the attributes sent by the server have an attribute in this object,
13631     * they take priority.
13632     */
13633     /**
13634     * @cfg {Object} uiProviders (optional) An object containing properties which
13635     * 
13636     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13637     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13638     * <i>uiProvider</i> attribute of a returned child node is a string rather
13639     * than a reference to a TreeNodeUI implementation, this that string value
13640     * is used as a property name in the uiProviders object. You can define the provider named
13641     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13642     */
13643     uiProviders : {},
13644
13645     /**
13646     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13647     * child nodes before loading.
13648     */
13649     clearOnLoad : true,
13650
13651     /**
13652     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13653     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13654     * Grid query { data : [ .....] }
13655     */
13656     
13657     root : false,
13658      /**
13659     * @cfg {String} queryParam (optional) 
13660     * Name of the query as it will be passed on the querystring (defaults to 'node')
13661     * eg. the request will be ?node=[id]
13662     */
13663     
13664     
13665     queryParam: false,
13666     
13667     /**
13668      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13669      * This is called automatically when a node is expanded, but may be used to reload
13670      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13671      * @param {Roo.tree.TreeNode} node
13672      * @param {Function} callback
13673      */
13674     load : function(node, callback){
13675         if(this.clearOnLoad){
13676             while(node.firstChild){
13677                 node.removeChild(node.firstChild);
13678             }
13679         }
13680         if(node.attributes.children){ // preloaded json children
13681             var cs = node.attributes.children;
13682             for(var i = 0, len = cs.length; i < len; i++){
13683                 node.appendChild(this.createNode(cs[i]));
13684             }
13685             if(typeof callback == "function"){
13686                 callback();
13687             }
13688         }else if(this.dataUrl){
13689             this.requestData(node, callback);
13690         }
13691     },
13692
13693     getParams: function(node){
13694         var buf = [], bp = this.baseParams;
13695         for(var key in bp){
13696             if(typeof bp[key] != "function"){
13697                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13698             }
13699         }
13700         var n = this.queryParam === false ? 'node' : this.queryParam;
13701         buf.push(n + "=", encodeURIComponent(node.id));
13702         return buf.join("");
13703     },
13704
13705     requestData : function(node, callback){
13706         if(this.fireEvent("beforeload", this, node, callback) !== false){
13707             this.transId = Roo.Ajax.request({
13708                 method:this.requestMethod,
13709                 url: this.dataUrl||this.url,
13710                 success: this.handleResponse,
13711                 failure: this.handleFailure,
13712                 scope: this,
13713                 argument: {callback: callback, node: node},
13714                 params: this.getParams(node)
13715             });
13716         }else{
13717             // if the load is cancelled, make sure we notify
13718             // the node that we are done
13719             if(typeof callback == "function"){
13720                 callback();
13721             }
13722         }
13723     },
13724
13725     isLoading : function(){
13726         return this.transId ? true : false;
13727     },
13728
13729     abort : function(){
13730         if(this.isLoading()){
13731             Roo.Ajax.abort(this.transId);
13732         }
13733     },
13734
13735     // private
13736     createNode : function(attr)
13737     {
13738         // apply baseAttrs, nice idea Corey!
13739         if(this.baseAttrs){
13740             Roo.applyIf(attr, this.baseAttrs);
13741         }
13742         if(this.applyLoader !== false){
13743             attr.loader = this;
13744         }
13745         // uiProvider = depreciated..
13746         
13747         if(typeof(attr.uiProvider) == 'string'){
13748            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13749                 /**  eval:var:attr */ eval(attr.uiProvider);
13750         }
13751         if(typeof(this.uiProviders['default']) != 'undefined') {
13752             attr.uiProvider = this.uiProviders['default'];
13753         }
13754         
13755         this.fireEvent('create', this, attr);
13756         
13757         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13758         return(attr.leaf ?
13759                         new Roo.tree.TreeNode(attr) :
13760                         new Roo.tree.AsyncTreeNode(attr));
13761     },
13762
13763     processResponse : function(response, node, callback)
13764     {
13765         var json = response.responseText;
13766         try {
13767             
13768             var o = Roo.decode(json);
13769             
13770             if (this.root === false && typeof(o.success) != undefined) {
13771                 this.root = 'data'; // the default behaviour for list like data..
13772                 }
13773                 
13774             if (this.root !== false &&  !o.success) {
13775                 // it's a failure condition.
13776                 var a = response.argument;
13777                 this.fireEvent("loadexception", this, a.node, response);
13778                 Roo.log("Load failed - should have a handler really");
13779                 return;
13780             }
13781             
13782             
13783             
13784             if (this.root !== false) {
13785                  o = o[this.root];
13786             }
13787             
13788             for(var i = 0, len = o.length; i < len; i++){
13789                 var n = this.createNode(o[i]);
13790                 if(n){
13791                     node.appendChild(n);
13792                 }
13793             }
13794             if(typeof callback == "function"){
13795                 callback(this, node);
13796             }
13797         }catch(e){
13798             this.handleFailure(response);
13799         }
13800     },
13801
13802     handleResponse : function(response){
13803         this.transId = false;
13804         var a = response.argument;
13805         this.processResponse(response, a.node, a.callback);
13806         this.fireEvent("load", this, a.node, response);
13807     },
13808
13809     handleFailure : function(response)
13810     {
13811         // should handle failure better..
13812         this.transId = false;
13813         var a = response.argument;
13814         this.fireEvent("loadexception", this, a.node, response);
13815         if(typeof a.callback == "function"){
13816             a.callback(this, a.node);
13817         }
13818     }
13819 });/*
13820  * Based on:
13821  * Ext JS Library 1.1.1
13822  * Copyright(c) 2006-2007, Ext JS, LLC.
13823  *
13824  * Originally Released Under LGPL - original licence link has changed is not relivant.
13825  *
13826  * Fork - LGPL
13827  * <script type="text/javascript">
13828  */
13829
13830 /**
13831 * @class Roo.tree.TreeFilter
13832 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13833 * @param {TreePanel} tree
13834 * @param {Object} config (optional)
13835  */
13836 Roo.tree.TreeFilter = function(tree, config){
13837     this.tree = tree;
13838     this.filtered = {};
13839     Roo.apply(this, config);
13840 };
13841
13842 Roo.tree.TreeFilter.prototype = {
13843     clearBlank:false,
13844     reverse:false,
13845     autoClear:false,
13846     remove:false,
13847
13848      /**
13849      * Filter the data by a specific attribute.
13850      * @param {String/RegExp} value Either string that the attribute value
13851      * should start with or a RegExp to test against the attribute
13852      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13853      * @param {TreeNode} startNode (optional) The node to start the filter at.
13854      */
13855     filter : function(value, attr, startNode){
13856         attr = attr || "text";
13857         var f;
13858         if(typeof value == "string"){
13859             var vlen = value.length;
13860             // auto clear empty filter
13861             if(vlen == 0 && this.clearBlank){
13862                 this.clear();
13863                 return;
13864             }
13865             value = value.toLowerCase();
13866             f = function(n){
13867                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13868             };
13869         }else if(value.exec){ // regex?
13870             f = function(n){
13871                 return value.test(n.attributes[attr]);
13872             };
13873         }else{
13874             throw 'Illegal filter type, must be string or regex';
13875         }
13876         this.filterBy(f, null, startNode);
13877         },
13878
13879     /**
13880      * Filter by a function. The passed function will be called with each
13881      * node in the tree (or from the startNode). If the function returns true, the node is kept
13882      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13883      * @param {Function} fn The filter function
13884      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13885      */
13886     filterBy : function(fn, scope, startNode){
13887         startNode = startNode || this.tree.root;
13888         if(this.autoClear){
13889             this.clear();
13890         }
13891         var af = this.filtered, rv = this.reverse;
13892         var f = function(n){
13893             if(n == startNode){
13894                 return true;
13895             }
13896             if(af[n.id]){
13897                 return false;
13898             }
13899             var m = fn.call(scope || n, n);
13900             if(!m || rv){
13901                 af[n.id] = n;
13902                 n.ui.hide();
13903                 return false;
13904             }
13905             return true;
13906         };
13907         startNode.cascade(f);
13908         if(this.remove){
13909            for(var id in af){
13910                if(typeof id != "function"){
13911                    var n = af[id];
13912                    if(n && n.parentNode){
13913                        n.parentNode.removeChild(n);
13914                    }
13915                }
13916            }
13917         }
13918     },
13919
13920     /**
13921      * Clears the current filter. Note: with the "remove" option
13922      * set a filter cannot be cleared.
13923      */
13924     clear : function(){
13925         var t = this.tree;
13926         var af = this.filtered;
13927         for(var id in af){
13928             if(typeof id != "function"){
13929                 var n = af[id];
13930                 if(n){
13931                     n.ui.show();
13932                 }
13933             }
13934         }
13935         this.filtered = {};
13936     }
13937 };
13938 /*
13939  * Based on:
13940  * Ext JS Library 1.1.1
13941  * Copyright(c) 2006-2007, Ext JS, LLC.
13942  *
13943  * Originally Released Under LGPL - original licence link has changed is not relivant.
13944  *
13945  * Fork - LGPL
13946  * <script type="text/javascript">
13947  */
13948  
13949
13950 /**
13951  * @class Roo.tree.TreeSorter
13952  * Provides sorting of nodes in a TreePanel
13953  * 
13954  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13955  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13956  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13957  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13958  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13959  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13960  * @constructor
13961  * @param {TreePanel} tree
13962  * @param {Object} config
13963  */
13964 Roo.tree.TreeSorter = function(tree, config){
13965     Roo.apply(this, config);
13966     tree.on("beforechildrenrendered", this.doSort, this);
13967     tree.on("append", this.updateSort, this);
13968     tree.on("insert", this.updateSort, this);
13969     
13970     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13971     var p = this.property || "text";
13972     var sortType = this.sortType;
13973     var fs = this.folderSort;
13974     var cs = this.caseSensitive === true;
13975     var leafAttr = this.leafAttr || 'leaf';
13976
13977     this.sortFn = function(n1, n2){
13978         if(fs){
13979             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13980                 return 1;
13981             }
13982             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13983                 return -1;
13984             }
13985         }
13986         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13987         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13988         if(v1 < v2){
13989                         return dsc ? +1 : -1;
13990                 }else if(v1 > v2){
13991                         return dsc ? -1 : +1;
13992         }else{
13993                 return 0;
13994         }
13995     };
13996 };
13997
13998 Roo.tree.TreeSorter.prototype = {
13999     doSort : function(node){
14000         node.sort(this.sortFn);
14001     },
14002     
14003     compareNodes : function(n1, n2){
14004         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14005     },
14006     
14007     updateSort : function(tree, node){
14008         if(node.childrenRendered){
14009             this.doSort.defer(1, this, [node]);
14010         }
14011     }
14012 };/*
14013  * Based on:
14014  * Ext JS Library 1.1.1
14015  * Copyright(c) 2006-2007, Ext JS, LLC.
14016  *
14017  * Originally Released Under LGPL - original licence link has changed is not relivant.
14018  *
14019  * Fork - LGPL
14020  * <script type="text/javascript">
14021  */
14022
14023 if(Roo.dd.DropZone){
14024     
14025 Roo.tree.TreeDropZone = function(tree, config){
14026     this.allowParentInsert = false;
14027     this.allowContainerDrop = false;
14028     this.appendOnly = false;
14029     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14030     this.tree = tree;
14031     this.lastInsertClass = "x-tree-no-status";
14032     this.dragOverData = {};
14033 };
14034
14035 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14036     ddGroup : "TreeDD",
14037     scroll:  true,
14038     
14039     expandDelay : 1000,
14040     
14041     expandNode : function(node){
14042         if(node.hasChildNodes() && !node.isExpanded()){
14043             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14044         }
14045     },
14046     
14047     queueExpand : function(node){
14048         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14049     },
14050     
14051     cancelExpand : function(){
14052         if(this.expandProcId){
14053             clearTimeout(this.expandProcId);
14054             this.expandProcId = false;
14055         }
14056     },
14057     
14058     isValidDropPoint : function(n, pt, dd, e, data){
14059         if(!n || !data){ return false; }
14060         var targetNode = n.node;
14061         var dropNode = data.node;
14062         // default drop rules
14063         if(!(targetNode && targetNode.isTarget && pt)){
14064             return false;
14065         }
14066         if(pt == "append" && targetNode.allowChildren === false){
14067             return false;
14068         }
14069         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14070             return false;
14071         }
14072         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14073             return false;
14074         }
14075         // reuse the object
14076         var overEvent = this.dragOverData;
14077         overEvent.tree = this.tree;
14078         overEvent.target = targetNode;
14079         overEvent.data = data;
14080         overEvent.point = pt;
14081         overEvent.source = dd;
14082         overEvent.rawEvent = e;
14083         overEvent.dropNode = dropNode;
14084         overEvent.cancel = false;  
14085         var result = this.tree.fireEvent("nodedragover", overEvent);
14086         return overEvent.cancel === false && result !== false;
14087     },
14088     
14089     getDropPoint : function(e, n, dd)
14090     {
14091         var tn = n.node;
14092         if(tn.isRoot){
14093             return tn.allowChildren !== false ? "append" : false; // always append for root
14094         }
14095         var dragEl = n.ddel;
14096         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14097         var y = Roo.lib.Event.getPageY(e);
14098         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14099         
14100         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14101         var noAppend = tn.allowChildren === false;
14102         if(this.appendOnly || tn.parentNode.allowChildren === false){
14103             return noAppend ? false : "append";
14104         }
14105         var noBelow = false;
14106         if(!this.allowParentInsert){
14107             noBelow = tn.hasChildNodes() && tn.isExpanded();
14108         }
14109         var q = (b - t) / (noAppend ? 2 : 3);
14110         if(y >= t && y < (t + q)){
14111             return "above";
14112         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14113             return "below";
14114         }else{
14115             return "append";
14116         }
14117     },
14118     
14119     onNodeEnter : function(n, dd, e, data)
14120     {
14121         this.cancelExpand();
14122     },
14123     
14124     onNodeOver : function(n, dd, e, data)
14125     {
14126        
14127         var pt = this.getDropPoint(e, n, dd);
14128         var node = n.node;
14129         
14130         // auto node expand check
14131         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14132             this.queueExpand(node);
14133         }else if(pt != "append"){
14134             this.cancelExpand();
14135         }
14136         
14137         // set the insert point style on the target node
14138         var returnCls = this.dropNotAllowed;
14139         if(this.isValidDropPoint(n, pt, dd, e, data)){
14140            if(pt){
14141                var el = n.ddel;
14142                var cls;
14143                if(pt == "above"){
14144                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14145                    cls = "x-tree-drag-insert-above";
14146                }else if(pt == "below"){
14147                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14148                    cls = "x-tree-drag-insert-below";
14149                }else{
14150                    returnCls = "x-tree-drop-ok-append";
14151                    cls = "x-tree-drag-append";
14152                }
14153                if(this.lastInsertClass != cls){
14154                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14155                    this.lastInsertClass = cls;
14156                }
14157            }
14158        }
14159        return returnCls;
14160     },
14161     
14162     onNodeOut : function(n, dd, e, data){
14163         
14164         this.cancelExpand();
14165         this.removeDropIndicators(n);
14166     },
14167     
14168     onNodeDrop : function(n, dd, e, data){
14169         var point = this.getDropPoint(e, n, dd);
14170         var targetNode = n.node;
14171         targetNode.ui.startDrop();
14172         if(!this.isValidDropPoint(n, point, dd, e, data)){
14173             targetNode.ui.endDrop();
14174             return false;
14175         }
14176         // first try to find the drop node
14177         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14178         var dropEvent = {
14179             tree : this.tree,
14180             target: targetNode,
14181             data: data,
14182             point: point,
14183             source: dd,
14184             rawEvent: e,
14185             dropNode: dropNode,
14186             cancel: !dropNode   
14187         };
14188         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14189         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14190             targetNode.ui.endDrop();
14191             return false;
14192         }
14193         // allow target changing
14194         targetNode = dropEvent.target;
14195         if(point == "append" && !targetNode.isExpanded()){
14196             targetNode.expand(false, null, function(){
14197                 this.completeDrop(dropEvent);
14198             }.createDelegate(this));
14199         }else{
14200             this.completeDrop(dropEvent);
14201         }
14202         return true;
14203     },
14204     
14205     completeDrop : function(de){
14206         var ns = de.dropNode, p = de.point, t = de.target;
14207         if(!(ns instanceof Array)){
14208             ns = [ns];
14209         }
14210         var n;
14211         for(var i = 0, len = ns.length; i < len; i++){
14212             n = ns[i];
14213             if(p == "above"){
14214                 t.parentNode.insertBefore(n, t);
14215             }else if(p == "below"){
14216                 t.parentNode.insertBefore(n, t.nextSibling);
14217             }else{
14218                 t.appendChild(n);
14219             }
14220         }
14221         n.ui.focus();
14222         if(this.tree.hlDrop){
14223             n.ui.highlight();
14224         }
14225         t.ui.endDrop();
14226         this.tree.fireEvent("nodedrop", de);
14227     },
14228     
14229     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14230         if(this.tree.hlDrop){
14231             dropNode.ui.focus();
14232             dropNode.ui.highlight();
14233         }
14234         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14235     },
14236     
14237     getTree : function(){
14238         return this.tree;
14239     },
14240     
14241     removeDropIndicators : function(n){
14242         if(n && n.ddel){
14243             var el = n.ddel;
14244             Roo.fly(el).removeClass([
14245                     "x-tree-drag-insert-above",
14246                     "x-tree-drag-insert-below",
14247                     "x-tree-drag-append"]);
14248             this.lastInsertClass = "_noclass";
14249         }
14250     },
14251     
14252     beforeDragDrop : function(target, e, id){
14253         this.cancelExpand();
14254         return true;
14255     },
14256     
14257     afterRepair : function(data){
14258         if(data && Roo.enableFx){
14259             data.node.ui.highlight();
14260         }
14261         this.hideProxy();
14262     } 
14263     
14264 });
14265
14266 }
14267 /*
14268  * Based on:
14269  * Ext JS Library 1.1.1
14270  * Copyright(c) 2006-2007, Ext JS, LLC.
14271  *
14272  * Originally Released Under LGPL - original licence link has changed is not relivant.
14273  *
14274  * Fork - LGPL
14275  * <script type="text/javascript">
14276  */
14277  
14278
14279 if(Roo.dd.DragZone){
14280 Roo.tree.TreeDragZone = function(tree, config){
14281     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14282     this.tree = tree;
14283 };
14284
14285 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14286     ddGroup : "TreeDD",
14287    
14288     onBeforeDrag : function(data, e){
14289         var n = data.node;
14290         return n && n.draggable && !n.disabled;
14291     },
14292      
14293     
14294     onInitDrag : function(e){
14295         var data = this.dragData;
14296         this.tree.getSelectionModel().select(data.node);
14297         this.proxy.update("");
14298         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14299         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14300     },
14301     
14302     getRepairXY : function(e, data){
14303         return data.node.ui.getDDRepairXY();
14304     },
14305     
14306     onEndDrag : function(data, e){
14307         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14308         
14309         
14310     },
14311     
14312     onValidDrop : function(dd, e, id){
14313         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14314         this.hideProxy();
14315     },
14316     
14317     beforeInvalidDrop : function(e, id){
14318         // this scrolls the original position back into view
14319         var sm = this.tree.getSelectionModel();
14320         sm.clearSelections();
14321         sm.select(this.dragData.node);
14322     }
14323 });
14324 }/*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.tree.TreeEditor
14336  * @extends Roo.Editor
14337  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14338  * as the editor field.
14339  * @constructor
14340  * @param {Object} config (used to be the tree panel.)
14341  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14342  * 
14343  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14344  * @cfg {Roo.form.TextField|Object} field The field configuration
14345  *
14346  * 
14347  */
14348 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14349     var tree = config;
14350     var field;
14351     if (oldconfig) { // old style..
14352         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14353     } else {
14354         // new style..
14355         tree = config.tree;
14356         config.field = config.field  || {};
14357         config.field.xtype = 'TextField';
14358         field = Roo.factory(config.field, Roo.form);
14359     }
14360     config = config || {};
14361     
14362     
14363     this.addEvents({
14364         /**
14365          * @event beforenodeedit
14366          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14367          * false from the handler of this event.
14368          * @param {Editor} this
14369          * @param {Roo.tree.Node} node 
14370          */
14371         "beforenodeedit" : true
14372     });
14373     
14374     //Roo.log(config);
14375     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14376
14377     this.tree = tree;
14378
14379     tree.on('beforeclick', this.beforeNodeClick, this);
14380     tree.getTreeEl().on('mousedown', this.hide, this);
14381     this.on('complete', this.updateNode, this);
14382     this.on('beforestartedit', this.fitToTree, this);
14383     this.on('startedit', this.bindScroll, this, {delay:10});
14384     this.on('specialkey', this.onSpecialKey, this);
14385 };
14386
14387 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14388     /**
14389      * @cfg {String} alignment
14390      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14391      */
14392     alignment: "l-l",
14393     // inherit
14394     autoSize: false,
14395     /**
14396      * @cfg {Boolean} hideEl
14397      * True to hide the bound element while the editor is displayed (defaults to false)
14398      */
14399     hideEl : false,
14400     /**
14401      * @cfg {String} cls
14402      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14403      */
14404     cls: "x-small-editor x-tree-editor",
14405     /**
14406      * @cfg {Boolean} shim
14407      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14408      */
14409     shim:false,
14410     // inherit
14411     shadow:"frame",
14412     /**
14413      * @cfg {Number} maxWidth
14414      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14415      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14416      * scroll and client offsets into account prior to each edit.
14417      */
14418     maxWidth: 250,
14419
14420     editDelay : 350,
14421
14422     // private
14423     fitToTree : function(ed, el){
14424         var td = this.tree.getTreeEl().dom, nd = el.dom;
14425         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14426             td.scrollLeft = nd.offsetLeft;
14427         }
14428         var w = Math.min(
14429                 this.maxWidth,
14430                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14431         this.setSize(w, '');
14432         
14433         return this.fireEvent('beforenodeedit', this, this.editNode);
14434         
14435     },
14436
14437     // private
14438     triggerEdit : function(node){
14439         this.completeEdit();
14440         this.editNode = node;
14441         this.startEdit(node.ui.textNode, node.text);
14442     },
14443
14444     // private
14445     bindScroll : function(){
14446         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14447     },
14448
14449     // private
14450     beforeNodeClick : function(node, e){
14451         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14452         this.lastClick = new Date();
14453         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14454             e.stopEvent();
14455             this.triggerEdit(node);
14456             return false;
14457         }
14458         return true;
14459     },
14460
14461     // private
14462     updateNode : function(ed, value){
14463         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14464         this.editNode.setText(value);
14465     },
14466
14467     // private
14468     onHide : function(){
14469         Roo.tree.TreeEditor.superclass.onHide.call(this);
14470         if(this.editNode){
14471             this.editNode.ui.focus();
14472         }
14473     },
14474
14475     // private
14476     onSpecialKey : function(field, e){
14477         var k = e.getKey();
14478         if(k == e.ESC){
14479             e.stopEvent();
14480             this.cancelEdit();
14481         }else if(k == e.ENTER && !e.hasModifier()){
14482             e.stopEvent();
14483             this.completeEdit();
14484         }
14485     }
14486 });//<Script type="text/javascript">
14487 /*
14488  * Based on:
14489  * Ext JS Library 1.1.1
14490  * Copyright(c) 2006-2007, Ext JS, LLC.
14491  *
14492  * Originally Released Under LGPL - original licence link has changed is not relivant.
14493  *
14494  * Fork - LGPL
14495  * <script type="text/javascript">
14496  */
14497  
14498 /**
14499  * Not documented??? - probably should be...
14500  */
14501
14502 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14503     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14504     
14505     renderElements : function(n, a, targetNode, bulkRender){
14506         //consel.log("renderElements?");
14507         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14508
14509         var t = n.getOwnerTree();
14510         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14511         
14512         var cols = t.columns;
14513         var bw = t.borderWidth;
14514         var c = cols[0];
14515         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14516          var cb = typeof a.checked == "boolean";
14517         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14518         var colcls = 'x-t-' + tid + '-c0';
14519         var buf = [
14520             '<li class="x-tree-node">',
14521             
14522                 
14523                 '<div class="x-tree-node-el ', a.cls,'">',
14524                     // extran...
14525                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14526                 
14527                 
14528                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14529                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14530                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14531                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14532                            (a.iconCls ? ' '+a.iconCls : ''),
14533                            '" unselectable="on" />',
14534                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14535                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14536                              
14537                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14538                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14539                             '<span unselectable="on" qtip="' + tx + '">',
14540                              tx,
14541                              '</span></a>' ,
14542                     '</div>',
14543                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14544                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14545                  ];
14546         for(var i = 1, len = cols.length; i < len; i++){
14547             c = cols[i];
14548             colcls = 'x-t-' + tid + '-c' +i;
14549             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14550             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14551                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14552                       "</div>");
14553          }
14554          
14555          buf.push(
14556             '</a>',
14557             '<div class="x-clear"></div></div>',
14558             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14559             "</li>");
14560         
14561         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14562             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14563                                 n.nextSibling.ui.getEl(), buf.join(""));
14564         }else{
14565             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14566         }
14567         var el = this.wrap.firstChild;
14568         this.elRow = el;
14569         this.elNode = el.firstChild;
14570         this.ranchor = el.childNodes[1];
14571         this.ctNode = this.wrap.childNodes[1];
14572         var cs = el.firstChild.childNodes;
14573         this.indentNode = cs[0];
14574         this.ecNode = cs[1];
14575         this.iconNode = cs[2];
14576         var index = 3;
14577         if(cb){
14578             this.checkbox = cs[3];
14579             index++;
14580         }
14581         this.anchor = cs[index];
14582         
14583         this.textNode = cs[index].firstChild;
14584         
14585         //el.on("click", this.onClick, this);
14586         //el.on("dblclick", this.onDblClick, this);
14587         
14588         
14589        // console.log(this);
14590     },
14591     initEvents : function(){
14592         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14593         
14594             
14595         var a = this.ranchor;
14596
14597         var el = Roo.get(a);
14598
14599         if(Roo.isOpera){ // opera render bug ignores the CSS
14600             el.setStyle("text-decoration", "none");
14601         }
14602
14603         el.on("click", this.onClick, this);
14604         el.on("dblclick", this.onDblClick, this);
14605         el.on("contextmenu", this.onContextMenu, this);
14606         
14607     },
14608     
14609     /*onSelectedChange : function(state){
14610         if(state){
14611             this.focus();
14612             this.addClass("x-tree-selected");
14613         }else{
14614             //this.blur();
14615             this.removeClass("x-tree-selected");
14616         }
14617     },*/
14618     addClass : function(cls){
14619         if(this.elRow){
14620             Roo.fly(this.elRow).addClass(cls);
14621         }
14622         
14623     },
14624     
14625     
14626     removeClass : function(cls){
14627         if(this.elRow){
14628             Roo.fly(this.elRow).removeClass(cls);
14629         }
14630     }
14631
14632     
14633     
14634 });//<Script type="text/javascript">
14635
14636 /*
14637  * Based on:
14638  * Ext JS Library 1.1.1
14639  * Copyright(c) 2006-2007, Ext JS, LLC.
14640  *
14641  * Originally Released Under LGPL - original licence link has changed is not relivant.
14642  *
14643  * Fork - LGPL
14644  * <script type="text/javascript">
14645  */
14646  
14647
14648 /**
14649  * @class Roo.tree.ColumnTree
14650  * @extends Roo.data.TreePanel
14651  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14652  * @cfg {int} borderWidth  compined right/left border allowance
14653  * @constructor
14654  * @param {String/HTMLElement/Element} el The container element
14655  * @param {Object} config
14656  */
14657 Roo.tree.ColumnTree =  function(el, config)
14658 {
14659    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14660    this.addEvents({
14661         /**
14662         * @event resize
14663         * Fire this event on a container when it resizes
14664         * @param {int} w Width
14665         * @param {int} h Height
14666         */
14667        "resize" : true
14668     });
14669     this.on('resize', this.onResize, this);
14670 };
14671
14672 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14673     //lines:false,
14674     
14675     
14676     borderWidth: Roo.isBorderBox ? 0 : 2, 
14677     headEls : false,
14678     
14679     render : function(){
14680         // add the header.....
14681        
14682         Roo.tree.ColumnTree.superclass.render.apply(this);
14683         
14684         this.el.addClass('x-column-tree');
14685         
14686         this.headers = this.el.createChild(
14687             {cls:'x-tree-headers'},this.innerCt.dom);
14688    
14689         var cols = this.columns, c;
14690         var totalWidth = 0;
14691         this.headEls = [];
14692         var  len = cols.length;
14693         for(var i = 0; i < len; i++){
14694              c = cols[i];
14695              totalWidth += c.width;
14696             this.headEls.push(this.headers.createChild({
14697                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14698                  cn: {
14699                      cls:'x-tree-hd-text',
14700                      html: c.header
14701                  },
14702                  style:'width:'+(c.width-this.borderWidth)+'px;'
14703              }));
14704         }
14705         this.headers.createChild({cls:'x-clear'});
14706         // prevent floats from wrapping when clipped
14707         this.headers.setWidth(totalWidth);
14708         //this.innerCt.setWidth(totalWidth);
14709         this.innerCt.setStyle({ overflow: 'auto' });
14710         this.onResize(this.width, this.height);
14711              
14712         
14713     },
14714     onResize : function(w,h)
14715     {
14716         this.height = h;
14717         this.width = w;
14718         // resize cols..
14719         this.innerCt.setWidth(this.width);
14720         this.innerCt.setHeight(this.height-20);
14721         
14722         // headers...
14723         var cols = this.columns, c;
14724         var totalWidth = 0;
14725         var expEl = false;
14726         var len = cols.length;
14727         for(var i = 0; i < len; i++){
14728             c = cols[i];
14729             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14730                 // it's the expander..
14731                 expEl  = this.headEls[i];
14732                 continue;
14733             }
14734             totalWidth += c.width;
14735             
14736         }
14737         if (expEl) {
14738             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14739         }
14740         this.headers.setWidth(w-20);
14741
14742         
14743         
14744         
14745     }
14746 });
14747 /*
14748  * Based on:
14749  * Ext JS Library 1.1.1
14750  * Copyright(c) 2006-2007, Ext JS, LLC.
14751  *
14752  * Originally Released Under LGPL - original licence link has changed is not relivant.
14753  *
14754  * Fork - LGPL
14755  * <script type="text/javascript">
14756  */
14757  
14758 /**
14759  * @class Roo.menu.Menu
14760  * @extends Roo.util.Observable
14761  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14762  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14763  * @constructor
14764  * Creates a new Menu
14765  * @param {Object} config Configuration options
14766  */
14767 Roo.menu.Menu = function(config){
14768     Roo.apply(this, config);
14769     this.id = this.id || Roo.id();
14770     this.addEvents({
14771         /**
14772          * @event beforeshow
14773          * Fires before this menu is displayed
14774          * @param {Roo.menu.Menu} this
14775          */
14776         beforeshow : true,
14777         /**
14778          * @event beforehide
14779          * Fires before this menu is hidden
14780          * @param {Roo.menu.Menu} this
14781          */
14782         beforehide : true,
14783         /**
14784          * @event show
14785          * Fires after this menu is displayed
14786          * @param {Roo.menu.Menu} this
14787          */
14788         show : true,
14789         /**
14790          * @event hide
14791          * Fires after this menu is hidden
14792          * @param {Roo.menu.Menu} this
14793          */
14794         hide : true,
14795         /**
14796          * @event click
14797          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14798          * @param {Roo.menu.Menu} this
14799          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14800          * @param {Roo.EventObject} e
14801          */
14802         click : true,
14803         /**
14804          * @event mouseover
14805          * Fires when the mouse is hovering over this menu
14806          * @param {Roo.menu.Menu} this
14807          * @param {Roo.EventObject} e
14808          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14809          */
14810         mouseover : true,
14811         /**
14812          * @event mouseout
14813          * Fires when the mouse exits this menu
14814          * @param {Roo.menu.Menu} this
14815          * @param {Roo.EventObject} e
14816          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14817          */
14818         mouseout : true,
14819         /**
14820          * @event itemclick
14821          * Fires when a menu item contained in this menu is clicked
14822          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14823          * @param {Roo.EventObject} e
14824          */
14825         itemclick: true
14826     });
14827     if (this.registerMenu) {
14828         Roo.menu.MenuMgr.register(this);
14829     }
14830     
14831     var mis = this.items;
14832     this.items = new Roo.util.MixedCollection();
14833     if(mis){
14834         this.add.apply(this, mis);
14835     }
14836 };
14837
14838 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14839     /**
14840      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14841      */
14842     minWidth : 120,
14843     /**
14844      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14845      * for bottom-right shadow (defaults to "sides")
14846      */
14847     shadow : "sides",
14848     /**
14849      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14850      * this menu (defaults to "tl-tr?")
14851      */
14852     subMenuAlign : "tl-tr?",
14853     /**
14854      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14855      * relative to its element of origin (defaults to "tl-bl?")
14856      */
14857     defaultAlign : "tl-bl?",
14858     /**
14859      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14860      */
14861     allowOtherMenus : false,
14862     /**
14863      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14864      */
14865     registerMenu : true,
14866
14867     hidden:true,
14868
14869     // private
14870     render : function(){
14871         if(this.el){
14872             return;
14873         }
14874         var el = this.el = new Roo.Layer({
14875             cls: "x-menu",
14876             shadow:this.shadow,
14877             constrain: false,
14878             parentEl: this.parentEl || document.body,
14879             zindex:15000
14880         });
14881
14882         this.keyNav = new Roo.menu.MenuNav(this);
14883
14884         if(this.plain){
14885             el.addClass("x-menu-plain");
14886         }
14887         if(this.cls){
14888             el.addClass(this.cls);
14889         }
14890         // generic focus element
14891         this.focusEl = el.createChild({
14892             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14893         });
14894         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14895         //disabling touch- as it's causing issues ..
14896         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14897         ul.on('click'   , this.onClick, this);
14898         
14899         
14900         ul.on("mouseover", this.onMouseOver, this);
14901         ul.on("mouseout", this.onMouseOut, this);
14902         this.items.each(function(item){
14903             if (item.hidden) {
14904                 return;
14905             }
14906             
14907             var li = document.createElement("li");
14908             li.className = "x-menu-list-item";
14909             ul.dom.appendChild(li);
14910             item.render(li, this);
14911         }, this);
14912         this.ul = ul;
14913         this.autoWidth();
14914     },
14915
14916     // private
14917     autoWidth : function(){
14918         var el = this.el, ul = this.ul;
14919         if(!el){
14920             return;
14921         }
14922         var w = this.width;
14923         if(w){
14924             el.setWidth(w);
14925         }else if(Roo.isIE){
14926             el.setWidth(this.minWidth);
14927             var t = el.dom.offsetWidth; // force recalc
14928             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14929         }
14930     },
14931
14932     // private
14933     delayAutoWidth : function(){
14934         if(this.rendered){
14935             if(!this.awTask){
14936                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14937             }
14938             this.awTask.delay(20);
14939         }
14940     },
14941
14942     // private
14943     findTargetItem : function(e){
14944         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14945         if(t && t.menuItemId){
14946             return this.items.get(t.menuItemId);
14947         }
14948     },
14949
14950     // private
14951     onClick : function(e){
14952         Roo.log("menu.onClick");
14953         var t = this.findTargetItem(e);
14954         if(!t){
14955             return;
14956         }
14957         Roo.log(e);
14958         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14959             if(t == this.activeItem && t.shouldDeactivate(e)){
14960                 this.activeItem.deactivate();
14961                 delete this.activeItem;
14962                 return;
14963             }
14964             if(t.canActivate){
14965                 this.setActiveItem(t, true);
14966             }
14967             return;
14968             
14969             
14970         }
14971         
14972         t.onClick(e);
14973         this.fireEvent("click", this, t, e);
14974     },
14975
14976     // private
14977     setActiveItem : function(item, autoExpand){
14978         if(item != this.activeItem){
14979             if(this.activeItem){
14980                 this.activeItem.deactivate();
14981             }
14982             this.activeItem = item;
14983             item.activate(autoExpand);
14984         }else if(autoExpand){
14985             item.expandMenu();
14986         }
14987     },
14988
14989     // private
14990     tryActivate : function(start, step){
14991         var items = this.items;
14992         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14993             var item = items.get(i);
14994             if(!item.disabled && item.canActivate){
14995                 this.setActiveItem(item, false);
14996                 return item;
14997             }
14998         }
14999         return false;
15000     },
15001
15002     // private
15003     onMouseOver : function(e){
15004         var t;
15005         if(t = this.findTargetItem(e)){
15006             if(t.canActivate && !t.disabled){
15007                 this.setActiveItem(t, true);
15008             }
15009         }
15010         this.fireEvent("mouseover", this, e, t);
15011     },
15012
15013     // private
15014     onMouseOut : function(e){
15015         var t;
15016         if(t = this.findTargetItem(e)){
15017             if(t == this.activeItem && t.shouldDeactivate(e)){
15018                 this.activeItem.deactivate();
15019                 delete this.activeItem;
15020             }
15021         }
15022         this.fireEvent("mouseout", this, e, t);
15023     },
15024
15025     /**
15026      * Read-only.  Returns true if the menu is currently displayed, else false.
15027      * @type Boolean
15028      */
15029     isVisible : function(){
15030         return this.el && !this.hidden;
15031     },
15032
15033     /**
15034      * Displays this menu relative to another element
15035      * @param {String/HTMLElement/Roo.Element} element The element to align to
15036      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15037      * the element (defaults to this.defaultAlign)
15038      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15039      */
15040     show : function(el, pos, parentMenu){
15041         this.parentMenu = parentMenu;
15042         if(!this.el){
15043             this.render();
15044         }
15045         this.fireEvent("beforeshow", this);
15046         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15047     },
15048
15049     /**
15050      * Displays this menu at a specific xy position
15051      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15052      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15053      */
15054     showAt : function(xy, parentMenu, /* private: */_e){
15055         this.parentMenu = parentMenu;
15056         if(!this.el){
15057             this.render();
15058         }
15059         if(_e !== false){
15060             this.fireEvent("beforeshow", this);
15061             xy = this.el.adjustForConstraints(xy);
15062         }
15063         this.el.setXY(xy);
15064         this.el.show();
15065         this.hidden = false;
15066         this.focus();
15067         this.fireEvent("show", this);
15068     },
15069
15070     focus : function(){
15071         if(!this.hidden){
15072             this.doFocus.defer(50, this);
15073         }
15074     },
15075
15076     doFocus : function(){
15077         if(!this.hidden){
15078             this.focusEl.focus();
15079         }
15080     },
15081
15082     /**
15083      * Hides this menu and optionally all parent menus
15084      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15085      */
15086     hide : function(deep){
15087         if(this.el && this.isVisible()){
15088             this.fireEvent("beforehide", this);
15089             if(this.activeItem){
15090                 this.activeItem.deactivate();
15091                 this.activeItem = null;
15092             }
15093             this.el.hide();
15094             this.hidden = true;
15095             this.fireEvent("hide", this);
15096         }
15097         if(deep === true && this.parentMenu){
15098             this.parentMenu.hide(true);
15099         }
15100     },
15101
15102     /**
15103      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15104      * Any of the following are valid:
15105      * <ul>
15106      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15107      * <li>An HTMLElement object which will be converted to a menu item</li>
15108      * <li>A menu item config object that will be created as a new menu item</li>
15109      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15110      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15111      * </ul>
15112      * Usage:
15113      * <pre><code>
15114 // Create the menu
15115 var menu = new Roo.menu.Menu();
15116
15117 // Create a menu item to add by reference
15118 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15119
15120 // Add a bunch of items at once using different methods.
15121 // Only the last item added will be returned.
15122 var item = menu.add(
15123     menuItem,                // add existing item by ref
15124     'Dynamic Item',          // new TextItem
15125     '-',                     // new separator
15126     { text: 'Config Item' }  // new item by config
15127 );
15128 </code></pre>
15129      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15130      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15131      */
15132     add : function(){
15133         var a = arguments, l = a.length, item;
15134         for(var i = 0; i < l; i++){
15135             var el = a[i];
15136             if ((typeof(el) == "object") && el.xtype && el.xns) {
15137                 el = Roo.factory(el, Roo.menu);
15138             }
15139             
15140             if(el.render){ // some kind of Item
15141                 item = this.addItem(el);
15142             }else if(typeof el == "string"){ // string
15143                 if(el == "separator" || el == "-"){
15144                     item = this.addSeparator();
15145                 }else{
15146                     item = this.addText(el);
15147                 }
15148             }else if(el.tagName || el.el){ // element
15149                 item = this.addElement(el);
15150             }else if(typeof el == "object"){ // must be menu item config?
15151                 item = this.addMenuItem(el);
15152             }
15153         }
15154         return item;
15155     },
15156
15157     /**
15158      * Returns this menu's underlying {@link Roo.Element} object
15159      * @return {Roo.Element} The element
15160      */
15161     getEl : function(){
15162         if(!this.el){
15163             this.render();
15164         }
15165         return this.el;
15166     },
15167
15168     /**
15169      * Adds a separator bar to the menu
15170      * @return {Roo.menu.Item} The menu item that was added
15171      */
15172     addSeparator : function(){
15173         return this.addItem(new Roo.menu.Separator());
15174     },
15175
15176     /**
15177      * Adds an {@link Roo.Element} object to the menu
15178      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15179      * @return {Roo.menu.Item} The menu item that was added
15180      */
15181     addElement : function(el){
15182         return this.addItem(new Roo.menu.BaseItem(el));
15183     },
15184
15185     /**
15186      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15187      * @param {Roo.menu.Item} item The menu item to add
15188      * @return {Roo.menu.Item} The menu item that was added
15189      */
15190     addItem : function(item){
15191         this.items.add(item);
15192         if(this.ul){
15193             var li = document.createElement("li");
15194             li.className = "x-menu-list-item";
15195             this.ul.dom.appendChild(li);
15196             item.render(li, this);
15197             this.delayAutoWidth();
15198         }
15199         return item;
15200     },
15201
15202     /**
15203      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15204      * @param {Object} config A MenuItem config object
15205      * @return {Roo.menu.Item} The menu item that was added
15206      */
15207     addMenuItem : function(config){
15208         if(!(config instanceof Roo.menu.Item)){
15209             if(typeof config.checked == "boolean"){ // must be check menu item config?
15210                 config = new Roo.menu.CheckItem(config);
15211             }else{
15212                 config = new Roo.menu.Item(config);
15213             }
15214         }
15215         return this.addItem(config);
15216     },
15217
15218     /**
15219      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15220      * @param {String} text The text to display in the menu item
15221      * @return {Roo.menu.Item} The menu item that was added
15222      */
15223     addText : function(text){
15224         return this.addItem(new Roo.menu.TextItem({ text : text }));
15225     },
15226
15227     /**
15228      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15229      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15230      * @param {Roo.menu.Item} item The menu item to add
15231      * @return {Roo.menu.Item} The menu item that was added
15232      */
15233     insert : function(index, item){
15234         this.items.insert(index, item);
15235         if(this.ul){
15236             var li = document.createElement("li");
15237             li.className = "x-menu-list-item";
15238             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15239             item.render(li, this);
15240             this.delayAutoWidth();
15241         }
15242         return item;
15243     },
15244
15245     /**
15246      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15247      * @param {Roo.menu.Item} item The menu item to remove
15248      */
15249     remove : function(item){
15250         this.items.removeKey(item.id);
15251         item.destroy();
15252     },
15253
15254     /**
15255      * Removes and destroys all items in the menu
15256      */
15257     removeAll : function(){
15258         var f;
15259         while(f = this.items.first()){
15260             this.remove(f);
15261         }
15262     }
15263 });
15264
15265 // MenuNav is a private utility class used internally by the Menu
15266 Roo.menu.MenuNav = function(menu){
15267     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15268     this.scope = this.menu = menu;
15269 };
15270
15271 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15272     doRelay : function(e, h){
15273         var k = e.getKey();
15274         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15275             this.menu.tryActivate(0, 1);
15276             return false;
15277         }
15278         return h.call(this.scope || this, e, this.menu);
15279     },
15280
15281     up : function(e, m){
15282         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15283             m.tryActivate(m.items.length-1, -1);
15284         }
15285     },
15286
15287     down : function(e, m){
15288         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15289             m.tryActivate(0, 1);
15290         }
15291     },
15292
15293     right : function(e, m){
15294         if(m.activeItem){
15295             m.activeItem.expandMenu(true);
15296         }
15297     },
15298
15299     left : function(e, m){
15300         m.hide();
15301         if(m.parentMenu && m.parentMenu.activeItem){
15302             m.parentMenu.activeItem.activate();
15303         }
15304     },
15305
15306     enter : function(e, m){
15307         if(m.activeItem){
15308             e.stopPropagation();
15309             m.activeItem.onClick(e);
15310             m.fireEvent("click", this, m.activeItem);
15311             return true;
15312         }
15313     }
15314 });/*
15315  * Based on:
15316  * Ext JS Library 1.1.1
15317  * Copyright(c) 2006-2007, Ext JS, LLC.
15318  *
15319  * Originally Released Under LGPL - original licence link has changed is not relivant.
15320  *
15321  * Fork - LGPL
15322  * <script type="text/javascript">
15323  */
15324  
15325 /**
15326  * @class Roo.menu.MenuMgr
15327  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15328  * @singleton
15329  */
15330 Roo.menu.MenuMgr = function(){
15331    var menus, active, groups = {}, attached = false, lastShow = new Date();
15332
15333    // private - called when first menu is created
15334    function init(){
15335        menus = {};
15336        active = new Roo.util.MixedCollection();
15337        Roo.get(document).addKeyListener(27, function(){
15338            if(active.length > 0){
15339                hideAll();
15340            }
15341        });
15342    }
15343
15344    // private
15345    function hideAll(){
15346        if(active && active.length > 0){
15347            var c = active.clone();
15348            c.each(function(m){
15349                m.hide();
15350            });
15351        }
15352    }
15353
15354    // private
15355    function onHide(m){
15356        active.remove(m);
15357        if(active.length < 1){
15358            Roo.get(document).un("mousedown", onMouseDown);
15359            attached = false;
15360        }
15361    }
15362
15363    // private
15364    function onShow(m){
15365        var last = active.last();
15366        lastShow = new Date();
15367        active.add(m);
15368        if(!attached){
15369            Roo.get(document).on("mousedown", onMouseDown);
15370            attached = true;
15371        }
15372        if(m.parentMenu){
15373           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15374           m.parentMenu.activeChild = m;
15375        }else if(last && last.isVisible()){
15376           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15377        }
15378    }
15379
15380    // private
15381    function onBeforeHide(m){
15382        if(m.activeChild){
15383            m.activeChild.hide();
15384        }
15385        if(m.autoHideTimer){
15386            clearTimeout(m.autoHideTimer);
15387            delete m.autoHideTimer;
15388        }
15389    }
15390
15391    // private
15392    function onBeforeShow(m){
15393        var pm = m.parentMenu;
15394        if(!pm && !m.allowOtherMenus){
15395            hideAll();
15396        }else if(pm && pm.activeChild && active != m){
15397            pm.activeChild.hide();
15398        }
15399    }
15400
15401    // private
15402    function onMouseDown(e){
15403        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15404            hideAll();
15405        }
15406    }
15407
15408    // private
15409    function onBeforeCheck(mi, state){
15410        if(state){
15411            var g = groups[mi.group];
15412            for(var i = 0, l = g.length; i < l; i++){
15413                if(g[i] != mi){
15414                    g[i].setChecked(false);
15415                }
15416            }
15417        }
15418    }
15419
15420    return {
15421
15422        /**
15423         * Hides all menus that are currently visible
15424         */
15425        hideAll : function(){
15426             hideAll();  
15427        },
15428
15429        // private
15430        register : function(menu){
15431            if(!menus){
15432                init();
15433            }
15434            menus[menu.id] = menu;
15435            menu.on("beforehide", onBeforeHide);
15436            menu.on("hide", onHide);
15437            menu.on("beforeshow", onBeforeShow);
15438            menu.on("show", onShow);
15439            var g = menu.group;
15440            if(g && menu.events["checkchange"]){
15441                if(!groups[g]){
15442                    groups[g] = [];
15443                }
15444                groups[g].push(menu);
15445                menu.on("checkchange", onCheck);
15446            }
15447        },
15448
15449         /**
15450          * Returns a {@link Roo.menu.Menu} object
15451          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15452          * be used to generate and return a new Menu instance.
15453          */
15454        get : function(menu){
15455            if(typeof menu == "string"){ // menu id
15456                return menus[menu];
15457            }else if(menu.events){  // menu instance
15458                return menu;
15459            }else if(typeof menu.length == 'number'){ // array of menu items?
15460                return new Roo.menu.Menu({items:menu});
15461            }else{ // otherwise, must be a config
15462                return new Roo.menu.Menu(menu);
15463            }
15464        },
15465
15466        // private
15467        unregister : function(menu){
15468            delete menus[menu.id];
15469            menu.un("beforehide", onBeforeHide);
15470            menu.un("hide", onHide);
15471            menu.un("beforeshow", onBeforeShow);
15472            menu.un("show", onShow);
15473            var g = menu.group;
15474            if(g && menu.events["checkchange"]){
15475                groups[g].remove(menu);
15476                menu.un("checkchange", onCheck);
15477            }
15478        },
15479
15480        // private
15481        registerCheckable : function(menuItem){
15482            var g = menuItem.group;
15483            if(g){
15484                if(!groups[g]){
15485                    groups[g] = [];
15486                }
15487                groups[g].push(menuItem);
15488                menuItem.on("beforecheckchange", onBeforeCheck);
15489            }
15490        },
15491
15492        // private
15493        unregisterCheckable : function(menuItem){
15494            var g = menuItem.group;
15495            if(g){
15496                groups[g].remove(menuItem);
15497                menuItem.un("beforecheckchange", onBeforeCheck);
15498            }
15499        }
15500    };
15501 }();/*
15502  * Based on:
15503  * Ext JS Library 1.1.1
15504  * Copyright(c) 2006-2007, Ext JS, LLC.
15505  *
15506  * Originally Released Under LGPL - original licence link has changed is not relivant.
15507  *
15508  * Fork - LGPL
15509  * <script type="text/javascript">
15510  */
15511  
15512
15513 /**
15514  * @class Roo.menu.BaseItem
15515  * @extends Roo.Component
15516  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15517  * management and base configuration options shared by all menu components.
15518  * @constructor
15519  * Creates a new BaseItem
15520  * @param {Object} config Configuration options
15521  */
15522 Roo.menu.BaseItem = function(config){
15523     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15524
15525     this.addEvents({
15526         /**
15527          * @event click
15528          * Fires when this item is clicked
15529          * @param {Roo.menu.BaseItem} this
15530          * @param {Roo.EventObject} e
15531          */
15532         click: true,
15533         /**
15534          * @event activate
15535          * Fires when this item is activated
15536          * @param {Roo.menu.BaseItem} this
15537          */
15538         activate : true,
15539         /**
15540          * @event deactivate
15541          * Fires when this item is deactivated
15542          * @param {Roo.menu.BaseItem} this
15543          */
15544         deactivate : true
15545     });
15546
15547     if(this.handler){
15548         this.on("click", this.handler, this.scope, true);
15549     }
15550 };
15551
15552 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15553     /**
15554      * @cfg {Function} handler
15555      * A function that will handle the click event of this menu item (defaults to undefined)
15556      */
15557     /**
15558      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15559      */
15560     canActivate : false,
15561     
15562      /**
15563      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15564      */
15565     hidden: false,
15566     
15567     /**
15568      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15569      */
15570     activeClass : "x-menu-item-active",
15571     /**
15572      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15573      */
15574     hideOnClick : true,
15575     /**
15576      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15577      */
15578     hideDelay : 100,
15579
15580     // private
15581     ctype: "Roo.menu.BaseItem",
15582
15583     // private
15584     actionMode : "container",
15585
15586     // private
15587     render : function(container, parentMenu){
15588         this.parentMenu = parentMenu;
15589         Roo.menu.BaseItem.superclass.render.call(this, container);
15590         this.container.menuItemId = this.id;
15591     },
15592
15593     // private
15594     onRender : function(container, position){
15595         this.el = Roo.get(this.el);
15596         container.dom.appendChild(this.el.dom);
15597     },
15598
15599     // private
15600     onClick : function(e){
15601         if(!this.disabled && this.fireEvent("click", this, e) !== false
15602                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15603             this.handleClick(e);
15604         }else{
15605             e.stopEvent();
15606         }
15607     },
15608
15609     // private
15610     activate : function(){
15611         if(this.disabled){
15612             return false;
15613         }
15614         var li = this.container;
15615         li.addClass(this.activeClass);
15616         this.region = li.getRegion().adjust(2, 2, -2, -2);
15617         this.fireEvent("activate", this);
15618         return true;
15619     },
15620
15621     // private
15622     deactivate : function(){
15623         this.container.removeClass(this.activeClass);
15624         this.fireEvent("deactivate", this);
15625     },
15626
15627     // private
15628     shouldDeactivate : function(e){
15629         return !this.region || !this.region.contains(e.getPoint());
15630     },
15631
15632     // private
15633     handleClick : function(e){
15634         if(this.hideOnClick){
15635             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15636         }
15637     },
15638
15639     // private
15640     expandMenu : function(autoActivate){
15641         // do nothing
15642     },
15643
15644     // private
15645     hideMenu : function(){
15646         // do nothing
15647     }
15648 });/*
15649  * Based on:
15650  * Ext JS Library 1.1.1
15651  * Copyright(c) 2006-2007, Ext JS, LLC.
15652  *
15653  * Originally Released Under LGPL - original licence link has changed is not relivant.
15654  *
15655  * Fork - LGPL
15656  * <script type="text/javascript">
15657  */
15658  
15659 /**
15660  * @class Roo.menu.Adapter
15661  * @extends Roo.menu.BaseItem
15662  * 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.
15663  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15664  * @constructor
15665  * Creates a new Adapter
15666  * @param {Object} config Configuration options
15667  */
15668 Roo.menu.Adapter = function(component, config){
15669     Roo.menu.Adapter.superclass.constructor.call(this, config);
15670     this.component = component;
15671 };
15672 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15673     // private
15674     canActivate : true,
15675
15676     // private
15677     onRender : function(container, position){
15678         this.component.render(container);
15679         this.el = this.component.getEl();
15680     },
15681
15682     // private
15683     activate : function(){
15684         if(this.disabled){
15685             return false;
15686         }
15687         this.component.focus();
15688         this.fireEvent("activate", this);
15689         return true;
15690     },
15691
15692     // private
15693     deactivate : function(){
15694         this.fireEvent("deactivate", this);
15695     },
15696
15697     // private
15698     disable : function(){
15699         this.component.disable();
15700         Roo.menu.Adapter.superclass.disable.call(this);
15701     },
15702
15703     // private
15704     enable : function(){
15705         this.component.enable();
15706         Roo.menu.Adapter.superclass.enable.call(this);
15707     }
15708 });/*
15709  * Based on:
15710  * Ext JS Library 1.1.1
15711  * Copyright(c) 2006-2007, Ext JS, LLC.
15712  *
15713  * Originally Released Under LGPL - original licence link has changed is not relivant.
15714  *
15715  * Fork - LGPL
15716  * <script type="text/javascript">
15717  */
15718
15719 /**
15720  * @class Roo.menu.TextItem
15721  * @extends Roo.menu.BaseItem
15722  * Adds a static text string to a menu, usually used as either a heading or group separator.
15723  * Note: old style constructor with text is still supported.
15724  * 
15725  * @constructor
15726  * Creates a new TextItem
15727  * @param {Object} cfg Configuration
15728  */
15729 Roo.menu.TextItem = function(cfg){
15730     if (typeof(cfg) == 'string') {
15731         this.text = cfg;
15732     } else {
15733         Roo.apply(this,cfg);
15734     }
15735     
15736     Roo.menu.TextItem.superclass.constructor.call(this);
15737 };
15738
15739 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15740     /**
15741      * @cfg {Boolean} text Text to show on item.
15742      */
15743     text : '',
15744     
15745     /**
15746      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15747      */
15748     hideOnClick : false,
15749     /**
15750      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15751      */
15752     itemCls : "x-menu-text",
15753
15754     // private
15755     onRender : function(){
15756         var s = document.createElement("span");
15757         s.className = this.itemCls;
15758         s.innerHTML = this.text;
15759         this.el = s;
15760         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15761     }
15762 });/*
15763  * Based on:
15764  * Ext JS Library 1.1.1
15765  * Copyright(c) 2006-2007, Ext JS, LLC.
15766  *
15767  * Originally Released Under LGPL - original licence link has changed is not relivant.
15768  *
15769  * Fork - LGPL
15770  * <script type="text/javascript">
15771  */
15772
15773 /**
15774  * @class Roo.menu.Separator
15775  * @extends Roo.menu.BaseItem
15776  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15777  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15778  * @constructor
15779  * @param {Object} config Configuration options
15780  */
15781 Roo.menu.Separator = function(config){
15782     Roo.menu.Separator.superclass.constructor.call(this, config);
15783 };
15784
15785 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15786     /**
15787      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15788      */
15789     itemCls : "x-menu-sep",
15790     /**
15791      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15792      */
15793     hideOnClick : false,
15794
15795     // private
15796     onRender : function(li){
15797         var s = document.createElement("span");
15798         s.className = this.itemCls;
15799         s.innerHTML = "&#160;";
15800         this.el = s;
15801         li.addClass("x-menu-sep-li");
15802         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15803     }
15804 });/*
15805  * Based on:
15806  * Ext JS Library 1.1.1
15807  * Copyright(c) 2006-2007, Ext JS, LLC.
15808  *
15809  * Originally Released Under LGPL - original licence link has changed is not relivant.
15810  *
15811  * Fork - LGPL
15812  * <script type="text/javascript">
15813  */
15814 /**
15815  * @class Roo.menu.Item
15816  * @extends Roo.menu.BaseItem
15817  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15818  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15819  * activation and click handling.
15820  * @constructor
15821  * Creates a new Item
15822  * @param {Object} config Configuration options
15823  */
15824 Roo.menu.Item = function(config){
15825     Roo.menu.Item.superclass.constructor.call(this, config);
15826     if(this.menu){
15827         this.menu = Roo.menu.MenuMgr.get(this.menu);
15828     }
15829 };
15830 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15831     
15832     /**
15833      * @cfg {String} text
15834      * The text to show on the menu item.
15835      */
15836     text: '',
15837      /**
15838      * @cfg {String} HTML to render in menu
15839      * The text to show on the menu item (HTML version).
15840      */
15841     html: '',
15842     /**
15843      * @cfg {String} icon
15844      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15845      */
15846     icon: undefined,
15847     /**
15848      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15849      */
15850     itemCls : "x-menu-item",
15851     /**
15852      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15853      */
15854     canActivate : true,
15855     /**
15856      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15857      */
15858     showDelay: 200,
15859     // doc'd in BaseItem
15860     hideDelay: 200,
15861
15862     // private
15863     ctype: "Roo.menu.Item",
15864     
15865     // private
15866     onRender : function(container, position){
15867         var el = document.createElement("a");
15868         el.hideFocus = true;
15869         el.unselectable = "on";
15870         el.href = this.href || "#";
15871         if(this.hrefTarget){
15872             el.target = this.hrefTarget;
15873         }
15874         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15875         
15876         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15877         
15878         el.innerHTML = String.format(
15879                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15880                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15881         this.el = el;
15882         Roo.menu.Item.superclass.onRender.call(this, container, position);
15883     },
15884
15885     /**
15886      * Sets the text to display in this menu item
15887      * @param {String} text The text to display
15888      * @param {Boolean} isHTML true to indicate text is pure html.
15889      */
15890     setText : function(text, isHTML){
15891         if (isHTML) {
15892             this.html = text;
15893         } else {
15894             this.text = text;
15895             this.html = '';
15896         }
15897         if(this.rendered){
15898             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15899      
15900             this.el.update(String.format(
15901                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15902                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15903             this.parentMenu.autoWidth();
15904         }
15905     },
15906
15907     // private
15908     handleClick : function(e){
15909         if(!this.href){ // if no link defined, stop the event automatically
15910             e.stopEvent();
15911         }
15912         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15913     },
15914
15915     // private
15916     activate : function(autoExpand){
15917         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15918             this.focus();
15919             if(autoExpand){
15920                 this.expandMenu();
15921             }
15922         }
15923         return true;
15924     },
15925
15926     // private
15927     shouldDeactivate : function(e){
15928         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15929             if(this.menu && this.menu.isVisible()){
15930                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15931             }
15932             return true;
15933         }
15934         return false;
15935     },
15936
15937     // private
15938     deactivate : function(){
15939         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15940         this.hideMenu();
15941     },
15942
15943     // private
15944     expandMenu : function(autoActivate){
15945         if(!this.disabled && this.menu){
15946             clearTimeout(this.hideTimer);
15947             delete this.hideTimer;
15948             if(!this.menu.isVisible() && !this.showTimer){
15949                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15950             }else if (this.menu.isVisible() && autoActivate){
15951                 this.menu.tryActivate(0, 1);
15952             }
15953         }
15954     },
15955
15956     // private
15957     deferExpand : function(autoActivate){
15958         delete this.showTimer;
15959         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15960         if(autoActivate){
15961             this.menu.tryActivate(0, 1);
15962         }
15963     },
15964
15965     // private
15966     hideMenu : function(){
15967         clearTimeout(this.showTimer);
15968         delete this.showTimer;
15969         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15970             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15971         }
15972     },
15973
15974     // private
15975     deferHide : function(){
15976         delete this.hideTimer;
15977         this.menu.hide();
15978     }
15979 });/*
15980  * Based on:
15981  * Ext JS Library 1.1.1
15982  * Copyright(c) 2006-2007, Ext JS, LLC.
15983  *
15984  * Originally Released Under LGPL - original licence link has changed is not relivant.
15985  *
15986  * Fork - LGPL
15987  * <script type="text/javascript">
15988  */
15989  
15990 /**
15991  * @class Roo.menu.CheckItem
15992  * @extends Roo.menu.Item
15993  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15994  * @constructor
15995  * Creates a new CheckItem
15996  * @param {Object} config Configuration options
15997  */
15998 Roo.menu.CheckItem = function(config){
15999     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16000     this.addEvents({
16001         /**
16002          * @event beforecheckchange
16003          * Fires before the checked value is set, providing an opportunity to cancel if needed
16004          * @param {Roo.menu.CheckItem} this
16005          * @param {Boolean} checked The new checked value that will be set
16006          */
16007         "beforecheckchange" : true,
16008         /**
16009          * @event checkchange
16010          * Fires after the checked value has been set
16011          * @param {Roo.menu.CheckItem} this
16012          * @param {Boolean} checked The checked value that was set
16013          */
16014         "checkchange" : true
16015     });
16016     if(this.checkHandler){
16017         this.on('checkchange', this.checkHandler, this.scope);
16018     }
16019 };
16020 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16021     /**
16022      * @cfg {String} group
16023      * All check items with the same group name will automatically be grouped into a single-select
16024      * radio button group (defaults to '')
16025      */
16026     /**
16027      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16028      */
16029     itemCls : "x-menu-item x-menu-check-item",
16030     /**
16031      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16032      */
16033     groupClass : "x-menu-group-item",
16034
16035     /**
16036      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16037      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16038      * initialized with checked = true will be rendered as checked.
16039      */
16040     checked: false,
16041
16042     // private
16043     ctype: "Roo.menu.CheckItem",
16044
16045     // private
16046     onRender : function(c){
16047         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16048         if(this.group){
16049             this.el.addClass(this.groupClass);
16050         }
16051         Roo.menu.MenuMgr.registerCheckable(this);
16052         if(this.checked){
16053             this.checked = false;
16054             this.setChecked(true, true);
16055         }
16056     },
16057
16058     // private
16059     destroy : function(){
16060         if(this.rendered){
16061             Roo.menu.MenuMgr.unregisterCheckable(this);
16062         }
16063         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16064     },
16065
16066     /**
16067      * Set the checked state of this item
16068      * @param {Boolean} checked The new checked value
16069      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16070      */
16071     setChecked : function(state, suppressEvent){
16072         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16073             if(this.container){
16074                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16075             }
16076             this.checked = state;
16077             if(suppressEvent !== true){
16078                 this.fireEvent("checkchange", this, state);
16079             }
16080         }
16081     },
16082
16083     // private
16084     handleClick : function(e){
16085        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16086            this.setChecked(!this.checked);
16087        }
16088        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16089     }
16090 });/*
16091  * Based on:
16092  * Ext JS Library 1.1.1
16093  * Copyright(c) 2006-2007, Ext JS, LLC.
16094  *
16095  * Originally Released Under LGPL - original licence link has changed is not relivant.
16096  *
16097  * Fork - LGPL
16098  * <script type="text/javascript">
16099  */
16100  
16101 /**
16102  * @class Roo.menu.DateItem
16103  * @extends Roo.menu.Adapter
16104  * A menu item that wraps the {@link Roo.DatPicker} component.
16105  * @constructor
16106  * Creates a new DateItem
16107  * @param {Object} config Configuration options
16108  */
16109 Roo.menu.DateItem = function(config){
16110     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16111     /** The Roo.DatePicker object @type Roo.DatePicker */
16112     this.picker = this.component;
16113     this.addEvents({select: true});
16114     
16115     this.picker.on("render", function(picker){
16116         picker.getEl().swallowEvent("click");
16117         picker.container.addClass("x-menu-date-item");
16118     });
16119
16120     this.picker.on("select", this.onSelect, this);
16121 };
16122
16123 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16124     // private
16125     onSelect : function(picker, date){
16126         this.fireEvent("select", this, date, picker);
16127         Roo.menu.DateItem.superclass.handleClick.call(this);
16128     }
16129 });/*
16130  * Based on:
16131  * Ext JS Library 1.1.1
16132  * Copyright(c) 2006-2007, Ext JS, LLC.
16133  *
16134  * Originally Released Under LGPL - original licence link has changed is not relivant.
16135  *
16136  * Fork - LGPL
16137  * <script type="text/javascript">
16138  */
16139  
16140 /**
16141  * @class Roo.menu.ColorItem
16142  * @extends Roo.menu.Adapter
16143  * A menu item that wraps the {@link Roo.ColorPalette} component.
16144  * @constructor
16145  * Creates a new ColorItem
16146  * @param {Object} config Configuration options
16147  */
16148 Roo.menu.ColorItem = function(config){
16149     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16150     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16151     this.palette = this.component;
16152     this.relayEvents(this.palette, ["select"]);
16153     if(this.selectHandler){
16154         this.on('select', this.selectHandler, this.scope);
16155     }
16156 };
16157 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16158  * Based on:
16159  * Ext JS Library 1.1.1
16160  * Copyright(c) 2006-2007, Ext JS, LLC.
16161  *
16162  * Originally Released Under LGPL - original licence link has changed is not relivant.
16163  *
16164  * Fork - LGPL
16165  * <script type="text/javascript">
16166  */
16167  
16168
16169 /**
16170  * @class Roo.menu.DateMenu
16171  * @extends Roo.menu.Menu
16172  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16173  * @constructor
16174  * Creates a new DateMenu
16175  * @param {Object} config Configuration options
16176  */
16177 Roo.menu.DateMenu = function(config){
16178     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16179     this.plain = true;
16180     var di = new Roo.menu.DateItem(config);
16181     this.add(di);
16182     /**
16183      * The {@link Roo.DatePicker} instance for this DateMenu
16184      * @type DatePicker
16185      */
16186     this.picker = di.picker;
16187     /**
16188      * @event select
16189      * @param {DatePicker} picker
16190      * @param {Date} date
16191      */
16192     this.relayEvents(di, ["select"]);
16193     this.on('beforeshow', function(){
16194         if(this.picker){
16195             this.picker.hideMonthPicker(false);
16196         }
16197     }, this);
16198 };
16199 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16200     cls:'x-date-menu'
16201 });/*
16202  * Based on:
16203  * Ext JS Library 1.1.1
16204  * Copyright(c) 2006-2007, Ext JS, LLC.
16205  *
16206  * Originally Released Under LGPL - original licence link has changed is not relivant.
16207  *
16208  * Fork - LGPL
16209  * <script type="text/javascript">
16210  */
16211  
16212
16213 /**
16214  * @class Roo.menu.ColorMenu
16215  * @extends Roo.menu.Menu
16216  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16217  * @constructor
16218  * Creates a new ColorMenu
16219  * @param {Object} config Configuration options
16220  */
16221 Roo.menu.ColorMenu = function(config){
16222     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16223     this.plain = true;
16224     var ci = new Roo.menu.ColorItem(config);
16225     this.add(ci);
16226     /**
16227      * The {@link Roo.ColorPalette} instance for this ColorMenu
16228      * @type ColorPalette
16229      */
16230     this.palette = ci.palette;
16231     /**
16232      * @event select
16233      * @param {ColorPalette} palette
16234      * @param {String} color
16235      */
16236     this.relayEvents(ci, ["select"]);
16237 };
16238 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16239  * Based on:
16240  * Ext JS Library 1.1.1
16241  * Copyright(c) 2006-2007, Ext JS, LLC.
16242  *
16243  * Originally Released Under LGPL - original licence link has changed is not relivant.
16244  *
16245  * Fork - LGPL
16246  * <script type="text/javascript">
16247  */
16248  
16249 /**
16250  * @class Roo.form.Field
16251  * @extends Roo.BoxComponent
16252  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16253  * @constructor
16254  * Creates a new Field
16255  * @param {Object} config Configuration options
16256  */
16257 Roo.form.Field = function(config){
16258     Roo.form.Field.superclass.constructor.call(this, config);
16259 };
16260
16261 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16262     /**
16263      * @cfg {String} fieldLabel Label to use when rendering a form.
16264      */
16265        /**
16266      * @cfg {String} qtip Mouse over tip
16267      */
16268      
16269     /**
16270      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16271      */
16272     invalidClass : "x-form-invalid",
16273     /**
16274      * @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")
16275      */
16276     invalidText : "The value in this field is invalid",
16277     /**
16278      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16279      */
16280     focusClass : "x-form-focus",
16281     /**
16282      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16283       automatic validation (defaults to "keyup").
16284      */
16285     validationEvent : "keyup",
16286     /**
16287      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16288      */
16289     validateOnBlur : true,
16290     /**
16291      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16292      */
16293     validationDelay : 250,
16294     /**
16295      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16296      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16297      */
16298     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16299     /**
16300      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16301      */
16302     fieldClass : "x-form-field",
16303     /**
16304      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16305      *<pre>
16306 Value         Description
16307 -----------   ----------------------------------------------------------------------
16308 qtip          Display a quick tip when the user hovers over the field
16309 title         Display a default browser title attribute popup
16310 under         Add a block div beneath the field containing the error text
16311 side          Add an error icon to the right of the field with a popup on hover
16312 [element id]  Add the error text directly to the innerHTML of the specified element
16313 </pre>
16314      */
16315     msgTarget : 'qtip',
16316     /**
16317      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16318      */
16319     msgFx : 'normal',
16320
16321     /**
16322      * @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.
16323      */
16324     readOnly : false,
16325
16326     /**
16327      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16328      */
16329     disabled : false,
16330
16331     /**
16332      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16333      */
16334     inputType : undefined,
16335     
16336     /**
16337      * @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).
16338          */
16339         tabIndex : undefined,
16340         
16341     // private
16342     isFormField : true,
16343
16344     // private
16345     hasFocus : false,
16346     /**
16347      * @property {Roo.Element} fieldEl
16348      * Element Containing the rendered Field (with label etc.)
16349      */
16350     /**
16351      * @cfg {Mixed} value A value to initialize this field with.
16352      */
16353     value : undefined,
16354
16355     /**
16356      * @cfg {String} name The field's HTML name attribute.
16357      */
16358     /**
16359      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16360      */
16361     // private
16362     loadedValue : false,
16363      
16364      
16365         // private ??
16366         initComponent : function(){
16367         Roo.form.Field.superclass.initComponent.call(this);
16368         this.addEvents({
16369             /**
16370              * @event focus
16371              * Fires when this field receives input focus.
16372              * @param {Roo.form.Field} this
16373              */
16374             focus : true,
16375             /**
16376              * @event blur
16377              * Fires when this field loses input focus.
16378              * @param {Roo.form.Field} this
16379              */
16380             blur : true,
16381             /**
16382              * @event specialkey
16383              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16384              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16385              * @param {Roo.form.Field} this
16386              * @param {Roo.EventObject} e The event object
16387              */
16388             specialkey : true,
16389             /**
16390              * @event change
16391              * Fires just before the field blurs if the field value has changed.
16392              * @param {Roo.form.Field} this
16393              * @param {Mixed} newValue The new value
16394              * @param {Mixed} oldValue The original value
16395              */
16396             change : true,
16397             /**
16398              * @event invalid
16399              * Fires after the field has been marked as invalid.
16400              * @param {Roo.form.Field} this
16401              * @param {String} msg The validation message
16402              */
16403             invalid : true,
16404             /**
16405              * @event valid
16406              * Fires after the field has been validated with no errors.
16407              * @param {Roo.form.Field} this
16408              */
16409             valid : true,
16410              /**
16411              * @event keyup
16412              * Fires after the key up
16413              * @param {Roo.form.Field} this
16414              * @param {Roo.EventObject}  e The event Object
16415              */
16416             keyup : true
16417         });
16418     },
16419
16420     /**
16421      * Returns the name attribute of the field if available
16422      * @return {String} name The field name
16423      */
16424     getName: function(){
16425          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16426     },
16427
16428     // private
16429     onRender : function(ct, position){
16430         Roo.form.Field.superclass.onRender.call(this, ct, position);
16431         if(!this.el){
16432             var cfg = this.getAutoCreate();
16433             if(!cfg.name){
16434                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16435             }
16436             if (!cfg.name.length) {
16437                 delete cfg.name;
16438             }
16439             if(this.inputType){
16440                 cfg.type = this.inputType;
16441             }
16442             this.el = ct.createChild(cfg, position);
16443         }
16444         var type = this.el.dom.type;
16445         if(type){
16446             if(type == 'password'){
16447                 type = 'text';
16448             }
16449             this.el.addClass('x-form-'+type);
16450         }
16451         if(this.readOnly){
16452             this.el.dom.readOnly = true;
16453         }
16454         if(this.tabIndex !== undefined){
16455             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16456         }
16457
16458         this.el.addClass([this.fieldClass, this.cls]);
16459         this.initValue();
16460     },
16461
16462     /**
16463      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16464      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16465      * @return {Roo.form.Field} this
16466      */
16467     applyTo : function(target){
16468         this.allowDomMove = false;
16469         this.el = Roo.get(target);
16470         this.render(this.el.dom.parentNode);
16471         return this;
16472     },
16473
16474     // private
16475     initValue : function(){
16476         if(this.value !== undefined){
16477             this.setValue(this.value);
16478         }else if(this.el.dom.value.length > 0){
16479             this.setValue(this.el.dom.value);
16480         }
16481     },
16482
16483     /**
16484      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16485      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16486      */
16487     isDirty : function() {
16488         if(this.disabled) {
16489             return false;
16490         }
16491         return String(this.getValue()) !== String(this.originalValue);
16492     },
16493
16494     /**
16495      * stores the current value in loadedValue
16496      */
16497     resetHasChanged : function()
16498     {
16499         this.loadedValue = String(this.getValue());
16500     },
16501     /**
16502      * checks the current value against the 'loaded' value.
16503      * Note - will return false if 'resetHasChanged' has not been called first.
16504      */
16505     hasChanged : function()
16506     {
16507         if(this.disabled || this.readOnly) {
16508             return false;
16509         }
16510         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16511     },
16512     
16513     
16514     
16515     // private
16516     afterRender : function(){
16517         Roo.form.Field.superclass.afterRender.call(this);
16518         this.initEvents();
16519     },
16520
16521     // private
16522     fireKey : function(e){
16523         //Roo.log('field ' + e.getKey());
16524         if(e.isNavKeyPress()){
16525             this.fireEvent("specialkey", this, e);
16526         }
16527     },
16528
16529     /**
16530      * Resets the current field value to the originally loaded value and clears any validation messages
16531      */
16532     reset : function(){
16533         this.setValue(this.resetValue);
16534         this.clearInvalid();
16535     },
16536
16537     // private
16538     initEvents : function(){
16539         // safari killled keypress - so keydown is now used..
16540         this.el.on("keydown" , this.fireKey,  this);
16541         this.el.on("focus", this.onFocus,  this);
16542         this.el.on("blur", this.onBlur,  this);
16543         this.el.relayEvent('keyup', this);
16544
16545         // reference to original value for reset
16546         this.originalValue = this.getValue();
16547         this.resetValue =  this.getValue();
16548     },
16549
16550     // private
16551     onFocus : function(){
16552         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16553             this.el.addClass(this.focusClass);
16554         }
16555         if(!this.hasFocus){
16556             this.hasFocus = true;
16557             this.startValue = this.getValue();
16558             this.fireEvent("focus", this);
16559         }
16560     },
16561
16562     beforeBlur : Roo.emptyFn,
16563
16564     // private
16565     onBlur : function(){
16566         this.beforeBlur();
16567         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16568             this.el.removeClass(this.focusClass);
16569         }
16570         this.hasFocus = false;
16571         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16572             this.validate();
16573         }
16574         var v = this.getValue();
16575         if(String(v) !== String(this.startValue)){
16576             this.fireEvent('change', this, v, this.startValue);
16577         }
16578         this.fireEvent("blur", this);
16579     },
16580
16581     /**
16582      * Returns whether or not the field value is currently valid
16583      * @param {Boolean} preventMark True to disable marking the field invalid
16584      * @return {Boolean} True if the value is valid, else false
16585      */
16586     isValid : function(preventMark){
16587         if(this.disabled){
16588             return true;
16589         }
16590         var restore = this.preventMark;
16591         this.preventMark = preventMark === true;
16592         var v = this.validateValue(this.processValue(this.getRawValue()));
16593         this.preventMark = restore;
16594         return v;
16595     },
16596
16597     /**
16598      * Validates the field value
16599      * @return {Boolean} True if the value is valid, else false
16600      */
16601     validate : function(){
16602         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16603             this.clearInvalid();
16604             return true;
16605         }
16606         return false;
16607     },
16608
16609     processValue : function(value){
16610         return value;
16611     },
16612
16613     // private
16614     // Subclasses should provide the validation implementation by overriding this
16615     validateValue : function(value){
16616         return true;
16617     },
16618
16619     /**
16620      * Mark this field as invalid
16621      * @param {String} msg The validation message
16622      */
16623     markInvalid : function(msg){
16624         if(!this.rendered || this.preventMark){ // not rendered
16625             return;
16626         }
16627         
16628         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16629         
16630         obj.el.addClass(this.invalidClass);
16631         msg = msg || this.invalidText;
16632         switch(this.msgTarget){
16633             case 'qtip':
16634                 obj.el.dom.qtip = msg;
16635                 obj.el.dom.qclass = 'x-form-invalid-tip';
16636                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16637                     Roo.QuickTips.enable();
16638                 }
16639                 break;
16640             case 'title':
16641                 this.el.dom.title = msg;
16642                 break;
16643             case 'under':
16644                 if(!this.errorEl){
16645                     var elp = this.el.findParent('.x-form-element', 5, true);
16646                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16647                     this.errorEl.setWidth(elp.getWidth(true)-20);
16648                 }
16649                 this.errorEl.update(msg);
16650                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16651                 break;
16652             case 'side':
16653                 if(!this.errorIcon){
16654                     var elp = this.el.findParent('.x-form-element', 5, true);
16655                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16656                 }
16657                 this.alignErrorIcon();
16658                 this.errorIcon.dom.qtip = msg;
16659                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16660                 this.errorIcon.show();
16661                 this.on('resize', this.alignErrorIcon, this);
16662                 break;
16663             default:
16664                 var t = Roo.getDom(this.msgTarget);
16665                 t.innerHTML = msg;
16666                 t.style.display = this.msgDisplay;
16667                 break;
16668         }
16669         this.fireEvent('invalid', this, msg);
16670     },
16671
16672     // private
16673     alignErrorIcon : function(){
16674         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16675     },
16676
16677     /**
16678      * Clear any invalid styles/messages for this field
16679      */
16680     clearInvalid : function(){
16681         if(!this.rendered || this.preventMark){ // not rendered
16682             return;
16683         }
16684         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16685         
16686         obj.el.removeClass(this.invalidClass);
16687         switch(this.msgTarget){
16688             case 'qtip':
16689                 obj.el.dom.qtip = '';
16690                 break;
16691             case 'title':
16692                 this.el.dom.title = '';
16693                 break;
16694             case 'under':
16695                 if(this.errorEl){
16696                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16697                 }
16698                 break;
16699             case 'side':
16700                 if(this.errorIcon){
16701                     this.errorIcon.dom.qtip = '';
16702                     this.errorIcon.hide();
16703                     this.un('resize', this.alignErrorIcon, this);
16704                 }
16705                 break;
16706             default:
16707                 var t = Roo.getDom(this.msgTarget);
16708                 t.innerHTML = '';
16709                 t.style.display = 'none';
16710                 break;
16711         }
16712         this.fireEvent('valid', this);
16713     },
16714
16715     /**
16716      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16717      * @return {Mixed} value The field value
16718      */
16719     getRawValue : function(){
16720         var v = this.el.getValue();
16721         
16722         return v;
16723     },
16724
16725     /**
16726      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16727      * @return {Mixed} value The field value
16728      */
16729     getValue : function(){
16730         var v = this.el.getValue();
16731          
16732         return v;
16733     },
16734
16735     /**
16736      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16737      * @param {Mixed} value The value to set
16738      */
16739     setRawValue : function(v){
16740         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16741     },
16742
16743     /**
16744      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16745      * @param {Mixed} value The value to set
16746      */
16747     setValue : function(v){
16748         this.value = v;
16749         if(this.rendered){
16750             this.el.dom.value = (v === null || v === undefined ? '' : v);
16751              this.validate();
16752         }
16753     },
16754
16755     adjustSize : function(w, h){
16756         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16757         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16758         return s;
16759     },
16760
16761     adjustWidth : function(tag, w){
16762         tag = tag.toLowerCase();
16763         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16764             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16765                 if(tag == 'input'){
16766                     return w + 2;
16767                 }
16768                 if(tag == 'textarea'){
16769                     return w-2;
16770                 }
16771             }else if(Roo.isOpera){
16772                 if(tag == 'input'){
16773                     return w + 2;
16774                 }
16775                 if(tag == 'textarea'){
16776                     return w-2;
16777                 }
16778             }
16779         }
16780         return w;
16781     }
16782 });
16783
16784
16785 // anything other than normal should be considered experimental
16786 Roo.form.Field.msgFx = {
16787     normal : {
16788         show: function(msgEl, f){
16789             msgEl.setDisplayed('block');
16790         },
16791
16792         hide : function(msgEl, f){
16793             msgEl.setDisplayed(false).update('');
16794         }
16795     },
16796
16797     slide : {
16798         show: function(msgEl, f){
16799             msgEl.slideIn('t', {stopFx:true});
16800         },
16801
16802         hide : function(msgEl, f){
16803             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16804         }
16805     },
16806
16807     slideRight : {
16808         show: function(msgEl, f){
16809             msgEl.fixDisplay();
16810             msgEl.alignTo(f.el, 'tl-tr');
16811             msgEl.slideIn('l', {stopFx:true});
16812         },
16813
16814         hide : function(msgEl, f){
16815             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16816         }
16817     }
16818 };/*
16819  * Based on:
16820  * Ext JS Library 1.1.1
16821  * Copyright(c) 2006-2007, Ext JS, LLC.
16822  *
16823  * Originally Released Under LGPL - original licence link has changed is not relivant.
16824  *
16825  * Fork - LGPL
16826  * <script type="text/javascript">
16827  */
16828  
16829
16830 /**
16831  * @class Roo.form.TextField
16832  * @extends Roo.form.Field
16833  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16834  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16835  * @constructor
16836  * Creates a new TextField
16837  * @param {Object} config Configuration options
16838  */
16839 Roo.form.TextField = function(config){
16840     Roo.form.TextField.superclass.constructor.call(this, config);
16841     this.addEvents({
16842         /**
16843          * @event autosize
16844          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16845          * according to the default logic, but this event provides a hook for the developer to apply additional
16846          * logic at runtime to resize the field if needed.
16847              * @param {Roo.form.Field} this This text field
16848              * @param {Number} width The new field width
16849              */
16850         autosize : true
16851     });
16852 };
16853
16854 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16855     /**
16856      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16857      */
16858     grow : false,
16859     /**
16860      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16861      */
16862     growMin : 30,
16863     /**
16864      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16865      */
16866     growMax : 800,
16867     /**
16868      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16869      */
16870     vtype : null,
16871     /**
16872      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16873      */
16874     maskRe : null,
16875     /**
16876      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16877      */
16878     disableKeyFilter : false,
16879     /**
16880      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16881      */
16882     allowBlank : true,
16883     /**
16884      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16885      */
16886     minLength : 0,
16887     /**
16888      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16889      */
16890     maxLength : Number.MAX_VALUE,
16891     /**
16892      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16893      */
16894     minLengthText : "The minimum length for this field is {0}",
16895     /**
16896      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16897      */
16898     maxLengthText : "The maximum length for this field is {0}",
16899     /**
16900      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16901      */
16902     selectOnFocus : false,
16903     /**
16904      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16905      */
16906     blankText : "This field is required",
16907     /**
16908      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16909      * If available, this function will be called only after the basic validators all return true, and will be passed the
16910      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16911      */
16912     validator : null,
16913     /**
16914      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16915      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16916      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16917      */
16918     regex : null,
16919     /**
16920      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16921      */
16922     regexText : "",
16923     /**
16924      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16925      */
16926     emptyText : null,
16927    
16928
16929     // private
16930     initEvents : function()
16931     {
16932         if (this.emptyText) {
16933             this.el.attr('placeholder', this.emptyText);
16934         }
16935         
16936         Roo.form.TextField.superclass.initEvents.call(this);
16937         if(this.validationEvent == 'keyup'){
16938             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16939             this.el.on('keyup', this.filterValidation, this);
16940         }
16941         else if(this.validationEvent !== false){
16942             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16943         }
16944         
16945         if(this.selectOnFocus){
16946             this.on("focus", this.preFocus, this);
16947             
16948         }
16949         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16950             this.el.on("keypress", this.filterKeys, this);
16951         }
16952         if(this.grow){
16953             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16954             this.el.on("click", this.autoSize,  this);
16955         }
16956         if(this.el.is('input[type=password]') && Roo.isSafari){
16957             this.el.on('keydown', this.SafariOnKeyDown, this);
16958         }
16959     },
16960
16961     processValue : function(value){
16962         if(this.stripCharsRe){
16963             var newValue = value.replace(this.stripCharsRe, '');
16964             if(newValue !== value){
16965                 this.setRawValue(newValue);
16966                 return newValue;
16967             }
16968         }
16969         return value;
16970     },
16971
16972     filterValidation : function(e){
16973         if(!e.isNavKeyPress()){
16974             this.validationTask.delay(this.validationDelay);
16975         }
16976     },
16977
16978     // private
16979     onKeyUp : function(e){
16980         if(!e.isNavKeyPress()){
16981             this.autoSize();
16982         }
16983     },
16984
16985     /**
16986      * Resets the current field value to the originally-loaded value and clears any validation messages.
16987      *  
16988      */
16989     reset : function(){
16990         Roo.form.TextField.superclass.reset.call(this);
16991        
16992     },
16993
16994     
16995     // private
16996     preFocus : function(){
16997         
16998         if(this.selectOnFocus){
16999             this.el.dom.select();
17000         }
17001     },
17002
17003     
17004     // private
17005     filterKeys : function(e){
17006         var k = e.getKey();
17007         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17008             return;
17009         }
17010         var c = e.getCharCode(), cc = String.fromCharCode(c);
17011         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17012             return;
17013         }
17014         if(!this.maskRe.test(cc)){
17015             e.stopEvent();
17016         }
17017     },
17018
17019     setValue : function(v){
17020         
17021         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17022         
17023         this.autoSize();
17024     },
17025
17026     /**
17027      * Validates a value according to the field's validation rules and marks the field as invalid
17028      * if the validation fails
17029      * @param {Mixed} value The value to validate
17030      * @return {Boolean} True if the value is valid, else false
17031      */
17032     validateValue : function(value){
17033         if(value.length < 1)  { // if it's blank
17034              if(this.allowBlank){
17035                 this.clearInvalid();
17036                 return true;
17037              }else{
17038                 this.markInvalid(this.blankText);
17039                 return false;
17040              }
17041         }
17042         if(value.length < this.minLength){
17043             this.markInvalid(String.format(this.minLengthText, this.minLength));
17044             return false;
17045         }
17046         if(value.length > this.maxLength){
17047             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17048             return false;
17049         }
17050         if(this.vtype){
17051             var vt = Roo.form.VTypes;
17052             if(!vt[this.vtype](value, this)){
17053                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17054                 return false;
17055             }
17056         }
17057         if(typeof this.validator == "function"){
17058             var msg = this.validator(value);
17059             if(msg !== true){
17060                 this.markInvalid(msg);
17061                 return false;
17062             }
17063         }
17064         if(this.regex && !this.regex.test(value)){
17065             this.markInvalid(this.regexText);
17066             return false;
17067         }
17068         return true;
17069     },
17070
17071     /**
17072      * Selects text in this field
17073      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17074      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17075      */
17076     selectText : function(start, end){
17077         var v = this.getRawValue();
17078         if(v.length > 0){
17079             start = start === undefined ? 0 : start;
17080             end = end === undefined ? v.length : end;
17081             var d = this.el.dom;
17082             if(d.setSelectionRange){
17083                 d.setSelectionRange(start, end);
17084             }else if(d.createTextRange){
17085                 var range = d.createTextRange();
17086                 range.moveStart("character", start);
17087                 range.moveEnd("character", v.length-end);
17088                 range.select();
17089             }
17090         }
17091     },
17092
17093     /**
17094      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17095      * This only takes effect if grow = true, and fires the autosize event.
17096      */
17097     autoSize : function(){
17098         if(!this.grow || !this.rendered){
17099             return;
17100         }
17101         if(!this.metrics){
17102             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17103         }
17104         var el = this.el;
17105         var v = el.dom.value;
17106         var d = document.createElement('div');
17107         d.appendChild(document.createTextNode(v));
17108         v = d.innerHTML;
17109         d = null;
17110         v += "&#160;";
17111         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17112         this.el.setWidth(w);
17113         this.fireEvent("autosize", this, w);
17114     },
17115     
17116     // private
17117     SafariOnKeyDown : function(event)
17118     {
17119         // this is a workaround for a password hang bug on chrome/ webkit.
17120         
17121         var isSelectAll = false;
17122         
17123         if(this.el.dom.selectionEnd > 0){
17124             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17125         }
17126         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17127             event.preventDefault();
17128             this.setValue('');
17129             return;
17130         }
17131         
17132         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17133             
17134             event.preventDefault();
17135             // this is very hacky as keydown always get's upper case.
17136             
17137             var cc = String.fromCharCode(event.getCharCode());
17138             
17139             
17140             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17141             
17142         }
17143         
17144         
17145     }
17146 });/*
17147  * Based on:
17148  * Ext JS Library 1.1.1
17149  * Copyright(c) 2006-2007, Ext JS, LLC.
17150  *
17151  * Originally Released Under LGPL - original licence link has changed is not relivant.
17152  *
17153  * Fork - LGPL
17154  * <script type="text/javascript">
17155  */
17156  
17157 /**
17158  * @class Roo.form.Hidden
17159  * @extends Roo.form.TextField
17160  * Simple Hidden element used on forms 
17161  * 
17162  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17163  * 
17164  * @constructor
17165  * Creates a new Hidden form element.
17166  * @param {Object} config Configuration options
17167  */
17168
17169
17170
17171 // easy hidden field...
17172 Roo.form.Hidden = function(config){
17173     Roo.form.Hidden.superclass.constructor.call(this, config);
17174 };
17175   
17176 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17177     fieldLabel:      '',
17178     inputType:      'hidden',
17179     width:          50,
17180     allowBlank:     true,
17181     labelSeparator: '',
17182     hidden:         true,
17183     itemCls :       'x-form-item-display-none'
17184
17185
17186 });
17187
17188
17189 /*
17190  * Based on:
17191  * Ext JS Library 1.1.1
17192  * Copyright(c) 2006-2007, Ext JS, LLC.
17193  *
17194  * Originally Released Under LGPL - original licence link has changed is not relivant.
17195  *
17196  * Fork - LGPL
17197  * <script type="text/javascript">
17198  */
17199  
17200 /**
17201  * @class Roo.form.TriggerField
17202  * @extends Roo.form.TextField
17203  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17204  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17205  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17206  * for which you can provide a custom implementation.  For example:
17207  * <pre><code>
17208 var trigger = new Roo.form.TriggerField();
17209 trigger.onTriggerClick = myTriggerFn;
17210 trigger.applyTo('my-field');
17211 </code></pre>
17212  *
17213  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17214  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17215  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17216  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17217  * @constructor
17218  * Create a new TriggerField.
17219  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17220  * to the base TextField)
17221  */
17222 Roo.form.TriggerField = function(config){
17223     this.mimicing = false;
17224     Roo.form.TriggerField.superclass.constructor.call(this, config);
17225 };
17226
17227 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17228     /**
17229      * @cfg {String} triggerClass A CSS class to apply to the trigger
17230      */
17231     /**
17232      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17233      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17234      */
17235     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17236     /**
17237      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17238      */
17239     hideTrigger:false,
17240
17241     /** @cfg {Boolean} grow @hide */
17242     /** @cfg {Number} growMin @hide */
17243     /** @cfg {Number} growMax @hide */
17244
17245     /**
17246      * @hide 
17247      * @method
17248      */
17249     autoSize: Roo.emptyFn,
17250     // private
17251     monitorTab : true,
17252     // private
17253     deferHeight : true,
17254
17255     
17256     actionMode : 'wrap',
17257     // private
17258     onResize : function(w, h){
17259         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17260         if(typeof w == 'number'){
17261             var x = w - this.trigger.getWidth();
17262             this.el.setWidth(this.adjustWidth('input', x));
17263             this.trigger.setStyle('left', x+'px');
17264         }
17265     },
17266
17267     // private
17268     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17269
17270     // private
17271     getResizeEl : function(){
17272         return this.wrap;
17273     },
17274
17275     // private
17276     getPositionEl : function(){
17277         return this.wrap;
17278     },
17279
17280     // private
17281     alignErrorIcon : function(){
17282         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17283     },
17284
17285     // private
17286     onRender : function(ct, position){
17287         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17288         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17289         this.trigger = this.wrap.createChild(this.triggerConfig ||
17290                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17291         if(this.hideTrigger){
17292             this.trigger.setDisplayed(false);
17293         }
17294         this.initTrigger();
17295         if(!this.width){
17296             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17297         }
17298     },
17299
17300     // private
17301     initTrigger : function(){
17302         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17303         this.trigger.addClassOnOver('x-form-trigger-over');
17304         this.trigger.addClassOnClick('x-form-trigger-click');
17305     },
17306
17307     // private
17308     onDestroy : function(){
17309         if(this.trigger){
17310             this.trigger.removeAllListeners();
17311             this.trigger.remove();
17312         }
17313         if(this.wrap){
17314             this.wrap.remove();
17315         }
17316         Roo.form.TriggerField.superclass.onDestroy.call(this);
17317     },
17318
17319     // private
17320     onFocus : function(){
17321         Roo.form.TriggerField.superclass.onFocus.call(this);
17322         if(!this.mimicing){
17323             this.wrap.addClass('x-trigger-wrap-focus');
17324             this.mimicing = true;
17325             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17326             if(this.monitorTab){
17327                 this.el.on("keydown", this.checkTab, this);
17328             }
17329         }
17330     },
17331
17332     // private
17333     checkTab : function(e){
17334         if(e.getKey() == e.TAB){
17335             this.triggerBlur();
17336         }
17337     },
17338
17339     // private
17340     onBlur : function(){
17341         // do nothing
17342     },
17343
17344     // private
17345     mimicBlur : function(e, t){
17346         if(!this.wrap.contains(t) && this.validateBlur()){
17347             this.triggerBlur();
17348         }
17349     },
17350
17351     // private
17352     triggerBlur : function(){
17353         this.mimicing = false;
17354         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17355         if(this.monitorTab){
17356             this.el.un("keydown", this.checkTab, this);
17357         }
17358         this.wrap.removeClass('x-trigger-wrap-focus');
17359         Roo.form.TriggerField.superclass.onBlur.call(this);
17360     },
17361
17362     // private
17363     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17364     validateBlur : function(e, t){
17365         return true;
17366     },
17367
17368     // private
17369     onDisable : function(){
17370         Roo.form.TriggerField.superclass.onDisable.call(this);
17371         if(this.wrap){
17372             this.wrap.addClass('x-item-disabled');
17373         }
17374     },
17375
17376     // private
17377     onEnable : function(){
17378         Roo.form.TriggerField.superclass.onEnable.call(this);
17379         if(this.wrap){
17380             this.wrap.removeClass('x-item-disabled');
17381         }
17382     },
17383
17384     // private
17385     onShow : function(){
17386         var ae = this.getActionEl();
17387         
17388         if(ae){
17389             ae.dom.style.display = '';
17390             ae.dom.style.visibility = 'visible';
17391         }
17392     },
17393
17394     // private
17395     
17396     onHide : function(){
17397         var ae = this.getActionEl();
17398         ae.dom.style.display = 'none';
17399     },
17400
17401     /**
17402      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17403      * by an implementing function.
17404      * @method
17405      * @param {EventObject} e
17406      */
17407     onTriggerClick : Roo.emptyFn
17408 });
17409
17410 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17411 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17412 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17413 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17414     initComponent : function(){
17415         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17416
17417         this.triggerConfig = {
17418             tag:'span', cls:'x-form-twin-triggers', cn:[
17419             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17420             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17421         ]};
17422     },
17423
17424     getTrigger : function(index){
17425         return this.triggers[index];
17426     },
17427
17428     initTrigger : function(){
17429         var ts = this.trigger.select('.x-form-trigger', true);
17430         this.wrap.setStyle('overflow', 'hidden');
17431         var triggerField = this;
17432         ts.each(function(t, all, index){
17433             t.hide = function(){
17434                 var w = triggerField.wrap.getWidth();
17435                 this.dom.style.display = 'none';
17436                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17437             };
17438             t.show = function(){
17439                 var w = triggerField.wrap.getWidth();
17440                 this.dom.style.display = '';
17441                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17442             };
17443             var triggerIndex = 'Trigger'+(index+1);
17444
17445             if(this['hide'+triggerIndex]){
17446                 t.dom.style.display = 'none';
17447             }
17448             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17449             t.addClassOnOver('x-form-trigger-over');
17450             t.addClassOnClick('x-form-trigger-click');
17451         }, this);
17452         this.triggers = ts.elements;
17453     },
17454
17455     onTrigger1Click : Roo.emptyFn,
17456     onTrigger2Click : Roo.emptyFn
17457 });/*
17458  * Based on:
17459  * Ext JS Library 1.1.1
17460  * Copyright(c) 2006-2007, Ext JS, LLC.
17461  *
17462  * Originally Released Under LGPL - original licence link has changed is not relivant.
17463  *
17464  * Fork - LGPL
17465  * <script type="text/javascript">
17466  */
17467  
17468 /**
17469  * @class Roo.form.TextArea
17470  * @extends Roo.form.TextField
17471  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17472  * support for auto-sizing.
17473  * @constructor
17474  * Creates a new TextArea
17475  * @param {Object} config Configuration options
17476  */
17477 Roo.form.TextArea = function(config){
17478     Roo.form.TextArea.superclass.constructor.call(this, config);
17479     // these are provided exchanges for backwards compat
17480     // minHeight/maxHeight were replaced by growMin/growMax to be
17481     // compatible with TextField growing config values
17482     if(this.minHeight !== undefined){
17483         this.growMin = this.minHeight;
17484     }
17485     if(this.maxHeight !== undefined){
17486         this.growMax = this.maxHeight;
17487     }
17488 };
17489
17490 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17491     /**
17492      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17493      */
17494     growMin : 60,
17495     /**
17496      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17497      */
17498     growMax: 1000,
17499     /**
17500      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17501      * in the field (equivalent to setting overflow: hidden, defaults to false)
17502      */
17503     preventScrollbars: false,
17504     /**
17505      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17506      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17507      */
17508
17509     // private
17510     onRender : function(ct, position){
17511         if(!this.el){
17512             this.defaultAutoCreate = {
17513                 tag: "textarea",
17514                 style:"width:300px;height:60px;",
17515                 autocomplete: "new-password"
17516             };
17517         }
17518         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17519         if(this.grow){
17520             this.textSizeEl = Roo.DomHelper.append(document.body, {
17521                 tag: "pre", cls: "x-form-grow-sizer"
17522             });
17523             if(this.preventScrollbars){
17524                 this.el.setStyle("overflow", "hidden");
17525             }
17526             this.el.setHeight(this.growMin);
17527         }
17528     },
17529
17530     onDestroy : function(){
17531         if(this.textSizeEl){
17532             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17533         }
17534         Roo.form.TextArea.superclass.onDestroy.call(this);
17535     },
17536
17537     // private
17538     onKeyUp : function(e){
17539         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17540             this.autoSize();
17541         }
17542     },
17543
17544     /**
17545      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17546      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17547      */
17548     autoSize : function(){
17549         if(!this.grow || !this.textSizeEl){
17550             return;
17551         }
17552         var el = this.el;
17553         var v = el.dom.value;
17554         var ts = this.textSizeEl;
17555
17556         ts.innerHTML = '';
17557         ts.appendChild(document.createTextNode(v));
17558         v = ts.innerHTML;
17559
17560         Roo.fly(ts).setWidth(this.el.getWidth());
17561         if(v.length < 1){
17562             v = "&#160;&#160;";
17563         }else{
17564             if(Roo.isIE){
17565                 v = v.replace(/\n/g, '<p>&#160;</p>');
17566             }
17567             v += "&#160;\n&#160;";
17568         }
17569         ts.innerHTML = v;
17570         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17571         if(h != this.lastHeight){
17572             this.lastHeight = h;
17573             this.el.setHeight(h);
17574             this.fireEvent("autosize", this, h);
17575         }
17576     }
17577 });/*
17578  * Based on:
17579  * Ext JS Library 1.1.1
17580  * Copyright(c) 2006-2007, Ext JS, LLC.
17581  *
17582  * Originally Released Under LGPL - original licence link has changed is not relivant.
17583  *
17584  * Fork - LGPL
17585  * <script type="text/javascript">
17586  */
17587  
17588
17589 /**
17590  * @class Roo.form.NumberField
17591  * @extends Roo.form.TextField
17592  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17593  * @constructor
17594  * Creates a new NumberField
17595  * @param {Object} config Configuration options
17596  */
17597 Roo.form.NumberField = function(config){
17598     Roo.form.NumberField.superclass.constructor.call(this, config);
17599 };
17600
17601 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17602     /**
17603      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17604      */
17605     fieldClass: "x-form-field x-form-num-field",
17606     /**
17607      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17608      */
17609     allowDecimals : true,
17610     /**
17611      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17612      */
17613     decimalSeparator : ".",
17614     /**
17615      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17616      */
17617     decimalPrecision : 2,
17618     /**
17619      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17620      */
17621     allowNegative : true,
17622     /**
17623      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17624      */
17625     minValue : Number.NEGATIVE_INFINITY,
17626     /**
17627      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17628      */
17629     maxValue : Number.MAX_VALUE,
17630     /**
17631      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17632      */
17633     minText : "The minimum value for this field is {0}",
17634     /**
17635      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17636      */
17637     maxText : "The maximum value for this field is {0}",
17638     /**
17639      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17640      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17641      */
17642     nanText : "{0} is not a valid number",
17643
17644     // private
17645     initEvents : function(){
17646         Roo.form.NumberField.superclass.initEvents.call(this);
17647         var allowed = "0123456789";
17648         if(this.allowDecimals){
17649             allowed += this.decimalSeparator;
17650         }
17651         if(this.allowNegative){
17652             allowed += "-";
17653         }
17654         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17655         var keyPress = function(e){
17656             var k = e.getKey();
17657             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17658                 return;
17659             }
17660             var c = e.getCharCode();
17661             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17662                 e.stopEvent();
17663             }
17664         };
17665         this.el.on("keypress", keyPress, this);
17666     },
17667
17668     // private
17669     validateValue : function(value){
17670         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17671             return false;
17672         }
17673         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17674              return true;
17675         }
17676         var num = this.parseValue(value);
17677         if(isNaN(num)){
17678             this.markInvalid(String.format(this.nanText, value));
17679             return false;
17680         }
17681         if(num < this.minValue){
17682             this.markInvalid(String.format(this.minText, this.minValue));
17683             return false;
17684         }
17685         if(num > this.maxValue){
17686             this.markInvalid(String.format(this.maxText, this.maxValue));
17687             return false;
17688         }
17689         return true;
17690     },
17691
17692     getValue : function(){
17693         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17694     },
17695
17696     // private
17697     parseValue : function(value){
17698         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17699         return isNaN(value) ? '' : value;
17700     },
17701
17702     // private
17703     fixPrecision : function(value){
17704         var nan = isNaN(value);
17705         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17706             return nan ? '' : value;
17707         }
17708         return parseFloat(value).toFixed(this.decimalPrecision);
17709     },
17710
17711     setValue : function(v){
17712         v = this.fixPrecision(v);
17713         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17714     },
17715
17716     // private
17717     decimalPrecisionFcn : function(v){
17718         return Math.floor(v);
17719     },
17720
17721     beforeBlur : function(){
17722         var v = this.parseValue(this.getRawValue());
17723         if(v){
17724             this.setValue(v);
17725         }
17726     }
17727 });/*
17728  * Based on:
17729  * Ext JS Library 1.1.1
17730  * Copyright(c) 2006-2007, Ext JS, LLC.
17731  *
17732  * Originally Released Under LGPL - original licence link has changed is not relivant.
17733  *
17734  * Fork - LGPL
17735  * <script type="text/javascript">
17736  */
17737  
17738 /**
17739  * @class Roo.form.DateField
17740  * @extends Roo.form.TriggerField
17741  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17742 * @constructor
17743 * Create a new DateField
17744 * @param {Object} config
17745  */
17746 Roo.form.DateField = function(config){
17747     Roo.form.DateField.superclass.constructor.call(this, config);
17748     
17749       this.addEvents({
17750          
17751         /**
17752          * @event select
17753          * Fires when a date is selected
17754              * @param {Roo.form.DateField} combo This combo box
17755              * @param {Date} date The date selected
17756              */
17757         'select' : true
17758          
17759     });
17760     
17761     
17762     if(typeof this.minValue == "string") {
17763         this.minValue = this.parseDate(this.minValue);
17764     }
17765     if(typeof this.maxValue == "string") {
17766         this.maxValue = this.parseDate(this.maxValue);
17767     }
17768     this.ddMatch = null;
17769     if(this.disabledDates){
17770         var dd = this.disabledDates;
17771         var re = "(?:";
17772         for(var i = 0; i < dd.length; i++){
17773             re += dd[i];
17774             if(i != dd.length-1) {
17775                 re += "|";
17776             }
17777         }
17778         this.ddMatch = new RegExp(re + ")");
17779     }
17780 };
17781
17782 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17783     /**
17784      * @cfg {String} format
17785      * The default date format string which can be overriden for localization support.  The format must be
17786      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17787      */
17788     format : "m/d/y",
17789     /**
17790      * @cfg {String} altFormats
17791      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17792      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17793      */
17794     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17795     /**
17796      * @cfg {Array} disabledDays
17797      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17798      */
17799     disabledDays : null,
17800     /**
17801      * @cfg {String} disabledDaysText
17802      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17803      */
17804     disabledDaysText : "Disabled",
17805     /**
17806      * @cfg {Array} disabledDates
17807      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17808      * expression so they are very powerful. Some examples:
17809      * <ul>
17810      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17811      * <li>["03/08", "09/16"] would disable those days for every year</li>
17812      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17813      * <li>["03/../2006"] would disable every day in March 2006</li>
17814      * <li>["^03"] would disable every day in every March</li>
17815      * </ul>
17816      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17817      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17818      */
17819     disabledDates : null,
17820     /**
17821      * @cfg {String} disabledDatesText
17822      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17823      */
17824     disabledDatesText : "Disabled",
17825     /**
17826      * @cfg {Date/String} minValue
17827      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17828      * valid format (defaults to null).
17829      */
17830     minValue : null,
17831     /**
17832      * @cfg {Date/String} maxValue
17833      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17834      * valid format (defaults to null).
17835      */
17836     maxValue : null,
17837     /**
17838      * @cfg {String} minText
17839      * The error text to display when the date in the cell is before minValue (defaults to
17840      * 'The date in this field must be after {minValue}').
17841      */
17842     minText : "The date in this field must be equal to or after {0}",
17843     /**
17844      * @cfg {String} maxText
17845      * The error text to display when the date in the cell is after maxValue (defaults to
17846      * 'The date in this field must be before {maxValue}').
17847      */
17848     maxText : "The date in this field must be equal to or before {0}",
17849     /**
17850      * @cfg {String} invalidText
17851      * The error text to display when the date in the field is invalid (defaults to
17852      * '{value} is not a valid date - it must be in the format {format}').
17853      */
17854     invalidText : "{0} is not a valid date - it must be in the format {1}",
17855     /**
17856      * @cfg {String} triggerClass
17857      * An additional CSS class used to style the trigger button.  The trigger will always get the
17858      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17859      * which displays a calendar icon).
17860      */
17861     triggerClass : 'x-form-date-trigger',
17862     
17863
17864     /**
17865      * @cfg {Boolean} useIso
17866      * if enabled, then the date field will use a hidden field to store the 
17867      * real value as iso formated date. default (false)
17868      */ 
17869     useIso : false,
17870     /**
17871      * @cfg {String/Object} autoCreate
17872      * A DomHelper element spec, or true for a default element spec (defaults to
17873      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17874      */ 
17875     // private
17876     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17877     
17878     // private
17879     hiddenField: false,
17880     
17881     onRender : function(ct, position)
17882     {
17883         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17884         if (this.useIso) {
17885             //this.el.dom.removeAttribute('name'); 
17886             Roo.log("Changing name?");
17887             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17888             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17889                     'before', true);
17890             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17891             // prevent input submission
17892             this.hiddenName = this.name;
17893         }
17894             
17895             
17896     },
17897     
17898     // private
17899     validateValue : function(value)
17900     {
17901         value = this.formatDate(value);
17902         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17903             Roo.log('super failed');
17904             return false;
17905         }
17906         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17907              return true;
17908         }
17909         var svalue = value;
17910         value = this.parseDate(value);
17911         if(!value){
17912             Roo.log('parse date failed' + svalue);
17913             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17914             return false;
17915         }
17916         var time = value.getTime();
17917         if(this.minValue && time < this.minValue.getTime()){
17918             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17919             return false;
17920         }
17921         if(this.maxValue && time > this.maxValue.getTime()){
17922             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17923             return false;
17924         }
17925         if(this.disabledDays){
17926             var day = value.getDay();
17927             for(var i = 0; i < this.disabledDays.length; i++) {
17928                 if(day === this.disabledDays[i]){
17929                     this.markInvalid(this.disabledDaysText);
17930                     return false;
17931                 }
17932             }
17933         }
17934         var fvalue = this.formatDate(value);
17935         if(this.ddMatch && this.ddMatch.test(fvalue)){
17936             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17937             return false;
17938         }
17939         return true;
17940     },
17941
17942     // private
17943     // Provides logic to override the default TriggerField.validateBlur which just returns true
17944     validateBlur : function(){
17945         return !this.menu || !this.menu.isVisible();
17946     },
17947     
17948     getName: function()
17949     {
17950         // returns hidden if it's set..
17951         if (!this.rendered) {return ''};
17952         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17953         
17954     },
17955
17956     /**
17957      * Returns the current date value of the date field.
17958      * @return {Date} The date value
17959      */
17960     getValue : function(){
17961         
17962         return  this.hiddenField ?
17963                 this.hiddenField.value :
17964                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17965     },
17966
17967     /**
17968      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17969      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17970      * (the default format used is "m/d/y").
17971      * <br />Usage:
17972      * <pre><code>
17973 //All of these calls set the same date value (May 4, 2006)
17974
17975 //Pass a date object:
17976 var dt = new Date('5/4/06');
17977 dateField.setValue(dt);
17978
17979 //Pass a date string (default format):
17980 dateField.setValue('5/4/06');
17981
17982 //Pass a date string (custom format):
17983 dateField.format = 'Y-m-d';
17984 dateField.setValue('2006-5-4');
17985 </code></pre>
17986      * @param {String/Date} date The date or valid date string
17987      */
17988     setValue : function(date){
17989         if (this.hiddenField) {
17990             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17991         }
17992         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17993         // make sure the value field is always stored as a date..
17994         this.value = this.parseDate(date);
17995         
17996         
17997     },
17998
17999     // private
18000     parseDate : function(value){
18001         if(!value || value instanceof Date){
18002             return value;
18003         }
18004         var v = Date.parseDate(value, this.format);
18005          if (!v && this.useIso) {
18006             v = Date.parseDate(value, 'Y-m-d');
18007         }
18008         if(!v && this.altFormats){
18009             if(!this.altFormatsArray){
18010                 this.altFormatsArray = this.altFormats.split("|");
18011             }
18012             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18013                 v = Date.parseDate(value, this.altFormatsArray[i]);
18014             }
18015         }
18016         return v;
18017     },
18018
18019     // private
18020     formatDate : function(date, fmt){
18021         return (!date || !(date instanceof Date)) ?
18022                date : date.dateFormat(fmt || this.format);
18023     },
18024
18025     // private
18026     menuListeners : {
18027         select: function(m, d){
18028             
18029             this.setValue(d);
18030             this.fireEvent('select', this, d);
18031         },
18032         show : function(){ // retain focus styling
18033             this.onFocus();
18034         },
18035         hide : function(){
18036             this.focus.defer(10, this);
18037             var ml = this.menuListeners;
18038             this.menu.un("select", ml.select,  this);
18039             this.menu.un("show", ml.show,  this);
18040             this.menu.un("hide", ml.hide,  this);
18041         }
18042     },
18043
18044     // private
18045     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18046     onTriggerClick : function(){
18047         if(this.disabled){
18048             return;
18049         }
18050         if(this.menu == null){
18051             this.menu = new Roo.menu.DateMenu();
18052         }
18053         Roo.apply(this.menu.picker,  {
18054             showClear: this.allowBlank,
18055             minDate : this.minValue,
18056             maxDate : this.maxValue,
18057             disabledDatesRE : this.ddMatch,
18058             disabledDatesText : this.disabledDatesText,
18059             disabledDays : this.disabledDays,
18060             disabledDaysText : this.disabledDaysText,
18061             format : this.useIso ? 'Y-m-d' : this.format,
18062             minText : String.format(this.minText, this.formatDate(this.minValue)),
18063             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18064         });
18065         this.menu.on(Roo.apply({}, this.menuListeners, {
18066             scope:this
18067         }));
18068         this.menu.picker.setValue(this.getValue() || new Date());
18069         this.menu.show(this.el, "tl-bl?");
18070     },
18071
18072     beforeBlur : function(){
18073         var v = this.parseDate(this.getRawValue());
18074         if(v){
18075             this.setValue(v);
18076         }
18077     },
18078
18079     /*@
18080      * overide
18081      * 
18082      */
18083     isDirty : function() {
18084         if(this.disabled) {
18085             return false;
18086         }
18087         
18088         if(typeof(this.startValue) === 'undefined'){
18089             return false;
18090         }
18091         
18092         return String(this.getValue()) !== String(this.startValue);
18093         
18094     }
18095 });/*
18096  * Based on:
18097  * Ext JS Library 1.1.1
18098  * Copyright(c) 2006-2007, Ext JS, LLC.
18099  *
18100  * Originally Released Under LGPL - original licence link has changed is not relivant.
18101  *
18102  * Fork - LGPL
18103  * <script type="text/javascript">
18104  */
18105  
18106 /**
18107  * @class Roo.form.MonthField
18108  * @extends Roo.form.TriggerField
18109  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18110 * @constructor
18111 * Create a new MonthField
18112 * @param {Object} config
18113  */
18114 Roo.form.MonthField = function(config){
18115     
18116     Roo.form.MonthField.superclass.constructor.call(this, config);
18117     
18118       this.addEvents({
18119          
18120         /**
18121          * @event select
18122          * Fires when a date is selected
18123              * @param {Roo.form.MonthFieeld} combo This combo box
18124              * @param {Date} date The date selected
18125              */
18126         'select' : true
18127          
18128     });
18129     
18130     
18131     if(typeof this.minValue == "string") {
18132         this.minValue = this.parseDate(this.minValue);
18133     }
18134     if(typeof this.maxValue == "string") {
18135         this.maxValue = this.parseDate(this.maxValue);
18136     }
18137     this.ddMatch = null;
18138     if(this.disabledDates){
18139         var dd = this.disabledDates;
18140         var re = "(?:";
18141         for(var i = 0; i < dd.length; i++){
18142             re += dd[i];
18143             if(i != dd.length-1) {
18144                 re += "|";
18145             }
18146         }
18147         this.ddMatch = new RegExp(re + ")");
18148     }
18149 };
18150
18151 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18152     /**
18153      * @cfg {String} format
18154      * The default date format string which can be overriden for localization support.  The format must be
18155      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18156      */
18157     format : "M Y",
18158     /**
18159      * @cfg {String} altFormats
18160      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18161      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18162      */
18163     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18164     /**
18165      * @cfg {Array} disabledDays
18166      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18167      */
18168     disabledDays : [0,1,2,3,4,5,6],
18169     /**
18170      * @cfg {String} disabledDaysText
18171      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18172      */
18173     disabledDaysText : "Disabled",
18174     /**
18175      * @cfg {Array} disabledDates
18176      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18177      * expression so they are very powerful. Some examples:
18178      * <ul>
18179      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18180      * <li>["03/08", "09/16"] would disable those days for every year</li>
18181      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18182      * <li>["03/../2006"] would disable every day in March 2006</li>
18183      * <li>["^03"] would disable every day in every March</li>
18184      * </ul>
18185      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18186      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18187      */
18188     disabledDates : null,
18189     /**
18190      * @cfg {String} disabledDatesText
18191      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18192      */
18193     disabledDatesText : "Disabled",
18194     /**
18195      * @cfg {Date/String} minValue
18196      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18197      * valid format (defaults to null).
18198      */
18199     minValue : null,
18200     /**
18201      * @cfg {Date/String} maxValue
18202      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18203      * valid format (defaults to null).
18204      */
18205     maxValue : null,
18206     /**
18207      * @cfg {String} minText
18208      * The error text to display when the date in the cell is before minValue (defaults to
18209      * 'The date in this field must be after {minValue}').
18210      */
18211     minText : "The date in this field must be equal to or after {0}",
18212     /**
18213      * @cfg {String} maxTextf
18214      * The error text to display when the date in the cell is after maxValue (defaults to
18215      * 'The date in this field must be before {maxValue}').
18216      */
18217     maxText : "The date in this field must be equal to or before {0}",
18218     /**
18219      * @cfg {String} invalidText
18220      * The error text to display when the date in the field is invalid (defaults to
18221      * '{value} is not a valid date - it must be in the format {format}').
18222      */
18223     invalidText : "{0} is not a valid date - it must be in the format {1}",
18224     /**
18225      * @cfg {String} triggerClass
18226      * An additional CSS class used to style the trigger button.  The trigger will always get the
18227      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18228      * which displays a calendar icon).
18229      */
18230     triggerClass : 'x-form-date-trigger',
18231     
18232
18233     /**
18234      * @cfg {Boolean} useIso
18235      * if enabled, then the date field will use a hidden field to store the 
18236      * real value as iso formated date. default (true)
18237      */ 
18238     useIso : true,
18239     /**
18240      * @cfg {String/Object} autoCreate
18241      * A DomHelper element spec, or true for a default element spec (defaults to
18242      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18243      */ 
18244     // private
18245     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18246     
18247     // private
18248     hiddenField: false,
18249     
18250     hideMonthPicker : false,
18251     
18252     onRender : function(ct, position)
18253     {
18254         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18255         if (this.useIso) {
18256             this.el.dom.removeAttribute('name'); 
18257             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18258                     'before', true);
18259             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18260             // prevent input submission
18261             this.hiddenName = this.name;
18262         }
18263             
18264             
18265     },
18266     
18267     // private
18268     validateValue : function(value)
18269     {
18270         value = this.formatDate(value);
18271         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18272             return false;
18273         }
18274         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18275              return true;
18276         }
18277         var svalue = value;
18278         value = this.parseDate(value);
18279         if(!value){
18280             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18281             return false;
18282         }
18283         var time = value.getTime();
18284         if(this.minValue && time < this.minValue.getTime()){
18285             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18286             return false;
18287         }
18288         if(this.maxValue && time > this.maxValue.getTime()){
18289             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18290             return false;
18291         }
18292         /*if(this.disabledDays){
18293             var day = value.getDay();
18294             for(var i = 0; i < this.disabledDays.length; i++) {
18295                 if(day === this.disabledDays[i]){
18296                     this.markInvalid(this.disabledDaysText);
18297                     return false;
18298                 }
18299             }
18300         }
18301         */
18302         var fvalue = this.formatDate(value);
18303         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18304             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18305             return false;
18306         }
18307         */
18308         return true;
18309     },
18310
18311     // private
18312     // Provides logic to override the default TriggerField.validateBlur which just returns true
18313     validateBlur : function(){
18314         return !this.menu || !this.menu.isVisible();
18315     },
18316
18317     /**
18318      * Returns the current date value of the date field.
18319      * @return {Date} The date value
18320      */
18321     getValue : function(){
18322         
18323         
18324         
18325         return  this.hiddenField ?
18326                 this.hiddenField.value :
18327                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18328     },
18329
18330     /**
18331      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18332      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18333      * (the default format used is "m/d/y").
18334      * <br />Usage:
18335      * <pre><code>
18336 //All of these calls set the same date value (May 4, 2006)
18337
18338 //Pass a date object:
18339 var dt = new Date('5/4/06');
18340 monthField.setValue(dt);
18341
18342 //Pass a date string (default format):
18343 monthField.setValue('5/4/06');
18344
18345 //Pass a date string (custom format):
18346 monthField.format = 'Y-m-d';
18347 monthField.setValue('2006-5-4');
18348 </code></pre>
18349      * @param {String/Date} date The date or valid date string
18350      */
18351     setValue : function(date){
18352         Roo.log('month setValue' + date);
18353         // can only be first of month..
18354         
18355         var val = this.parseDate(date);
18356         
18357         if (this.hiddenField) {
18358             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18359         }
18360         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18361         this.value = this.parseDate(date);
18362     },
18363
18364     // private
18365     parseDate : function(value){
18366         if(!value || value instanceof Date){
18367             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18368             return value;
18369         }
18370         var v = Date.parseDate(value, this.format);
18371         if (!v && this.useIso) {
18372             v = Date.parseDate(value, 'Y-m-d');
18373         }
18374         if (v) {
18375             // 
18376             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18377         }
18378         
18379         
18380         if(!v && this.altFormats){
18381             if(!this.altFormatsArray){
18382                 this.altFormatsArray = this.altFormats.split("|");
18383             }
18384             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18385                 v = Date.parseDate(value, this.altFormatsArray[i]);
18386             }
18387         }
18388         return v;
18389     },
18390
18391     // private
18392     formatDate : function(date, fmt){
18393         return (!date || !(date instanceof Date)) ?
18394                date : date.dateFormat(fmt || this.format);
18395     },
18396
18397     // private
18398     menuListeners : {
18399         select: function(m, d){
18400             this.setValue(d);
18401             this.fireEvent('select', this, d);
18402         },
18403         show : function(){ // retain focus styling
18404             this.onFocus();
18405         },
18406         hide : function(){
18407             this.focus.defer(10, this);
18408             var ml = this.menuListeners;
18409             this.menu.un("select", ml.select,  this);
18410             this.menu.un("show", ml.show,  this);
18411             this.menu.un("hide", ml.hide,  this);
18412         }
18413     },
18414     // private
18415     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18416     onTriggerClick : function(){
18417         if(this.disabled){
18418             return;
18419         }
18420         if(this.menu == null){
18421             this.menu = new Roo.menu.DateMenu();
18422            
18423         }
18424         
18425         Roo.apply(this.menu.picker,  {
18426             
18427             showClear: this.allowBlank,
18428             minDate : this.minValue,
18429             maxDate : this.maxValue,
18430             disabledDatesRE : this.ddMatch,
18431             disabledDatesText : this.disabledDatesText,
18432             
18433             format : this.useIso ? 'Y-m-d' : this.format,
18434             minText : String.format(this.minText, this.formatDate(this.minValue)),
18435             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18436             
18437         });
18438          this.menu.on(Roo.apply({}, this.menuListeners, {
18439             scope:this
18440         }));
18441        
18442         
18443         var m = this.menu;
18444         var p = m.picker;
18445         
18446         // hide month picker get's called when we called by 'before hide';
18447         
18448         var ignorehide = true;
18449         p.hideMonthPicker  = function(disableAnim){
18450             if (ignorehide) {
18451                 return;
18452             }
18453              if(this.monthPicker){
18454                 Roo.log("hideMonthPicker called");
18455                 if(disableAnim === true){
18456                     this.monthPicker.hide();
18457                 }else{
18458                     this.monthPicker.slideOut('t', {duration:.2});
18459                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18460                     p.fireEvent("select", this, this.value);
18461                     m.hide();
18462                 }
18463             }
18464         }
18465         
18466         Roo.log('picker set value');
18467         Roo.log(this.getValue());
18468         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18469         m.show(this.el, 'tl-bl?');
18470         ignorehide  = false;
18471         // this will trigger hideMonthPicker..
18472         
18473         
18474         // hidden the day picker
18475         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18476         
18477         
18478         
18479       
18480         
18481         p.showMonthPicker.defer(100, p);
18482     
18483         
18484        
18485     },
18486
18487     beforeBlur : function(){
18488         var v = this.parseDate(this.getRawValue());
18489         if(v){
18490             this.setValue(v);
18491         }
18492     }
18493
18494     /** @cfg {Boolean} grow @hide */
18495     /** @cfg {Number} growMin @hide */
18496     /** @cfg {Number} growMax @hide */
18497     /**
18498      * @hide
18499      * @method autoSize
18500      */
18501 });/*
18502  * Based on:
18503  * Ext JS Library 1.1.1
18504  * Copyright(c) 2006-2007, Ext JS, LLC.
18505  *
18506  * Originally Released Under LGPL - original licence link has changed is not relivant.
18507  *
18508  * Fork - LGPL
18509  * <script type="text/javascript">
18510  */
18511  
18512
18513 /**
18514  * @class Roo.form.ComboBox
18515  * @extends Roo.form.TriggerField
18516  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18517  * @constructor
18518  * Create a new ComboBox.
18519  * @param {Object} config Configuration options
18520  */
18521 Roo.form.ComboBox = function(config){
18522     Roo.form.ComboBox.superclass.constructor.call(this, config);
18523     this.addEvents({
18524         /**
18525          * @event expand
18526          * Fires when the dropdown list is expanded
18527              * @param {Roo.form.ComboBox} combo This combo box
18528              */
18529         'expand' : true,
18530         /**
18531          * @event collapse
18532          * Fires when the dropdown list is collapsed
18533              * @param {Roo.form.ComboBox} combo This combo box
18534              */
18535         'collapse' : true,
18536         /**
18537          * @event beforeselect
18538          * Fires before a list item is selected. Return false to cancel the selection.
18539              * @param {Roo.form.ComboBox} combo This combo box
18540              * @param {Roo.data.Record} record The data record returned from the underlying store
18541              * @param {Number} index The index of the selected item in the dropdown list
18542              */
18543         'beforeselect' : true,
18544         /**
18545          * @event select
18546          * Fires when a list item is selected
18547              * @param {Roo.form.ComboBox} combo This combo box
18548              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18549              * @param {Number} index The index of the selected item in the dropdown list
18550              */
18551         'select' : true,
18552         /**
18553          * @event beforequery
18554          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18555          * The event object passed has these properties:
18556              * @param {Roo.form.ComboBox} combo This combo box
18557              * @param {String} query The query
18558              * @param {Boolean} forceAll true to force "all" query
18559              * @param {Boolean} cancel true to cancel the query
18560              * @param {Object} e The query event object
18561              */
18562         'beforequery': true,
18563          /**
18564          * @event add
18565          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18566              * @param {Roo.form.ComboBox} combo This combo box
18567              */
18568         'add' : true,
18569         /**
18570          * @event edit
18571          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18572              * @param {Roo.form.ComboBox} combo This combo box
18573              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18574              */
18575         'edit' : true
18576         
18577         
18578     });
18579     if(this.transform){
18580         this.allowDomMove = false;
18581         var s = Roo.getDom(this.transform);
18582         if(!this.hiddenName){
18583             this.hiddenName = s.name;
18584         }
18585         if(!this.store){
18586             this.mode = 'local';
18587             var d = [], opts = s.options;
18588             for(var i = 0, len = opts.length;i < len; i++){
18589                 var o = opts[i];
18590                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18591                 if(o.selected) {
18592                     this.value = value;
18593                 }
18594                 d.push([value, o.text]);
18595             }
18596             this.store = new Roo.data.SimpleStore({
18597                 'id': 0,
18598                 fields: ['value', 'text'],
18599                 data : d
18600             });
18601             this.valueField = 'value';
18602             this.displayField = 'text';
18603         }
18604         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18605         if(!this.lazyRender){
18606             this.target = true;
18607             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18608             s.parentNode.removeChild(s); // remove it
18609             this.render(this.el.parentNode);
18610         }else{
18611             s.parentNode.removeChild(s); // remove it
18612         }
18613
18614     }
18615     if (this.store) {
18616         this.store = Roo.factory(this.store, Roo.data);
18617     }
18618     
18619     this.selectedIndex = -1;
18620     if(this.mode == 'local'){
18621         if(config.queryDelay === undefined){
18622             this.queryDelay = 10;
18623         }
18624         if(config.minChars === undefined){
18625             this.minChars = 0;
18626         }
18627     }
18628 };
18629
18630 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18631     /**
18632      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18633      */
18634     /**
18635      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18636      * rendering into an Roo.Editor, defaults to false)
18637      */
18638     /**
18639      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18640      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18641      */
18642     /**
18643      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18644      */
18645     /**
18646      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18647      * the dropdown list (defaults to undefined, with no header element)
18648      */
18649
18650      /**
18651      * @cfg {String/Roo.Template} tpl The template to use to render the output
18652      */
18653      
18654     // private
18655     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18656     /**
18657      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18658      */
18659     listWidth: undefined,
18660     /**
18661      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18662      * mode = 'remote' or 'text' if mode = 'local')
18663      */
18664     displayField: undefined,
18665     /**
18666      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18667      * mode = 'remote' or 'value' if mode = 'local'). 
18668      * Note: use of a valueField requires the user make a selection
18669      * in order for a value to be mapped.
18670      */
18671     valueField: undefined,
18672     
18673     
18674     /**
18675      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18676      * field's data value (defaults to the underlying DOM element's name)
18677      */
18678     hiddenName: undefined,
18679     /**
18680      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18681      */
18682     listClass: '',
18683     /**
18684      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18685      */
18686     selectedClass: 'x-combo-selected',
18687     /**
18688      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18689      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18690      * which displays a downward arrow icon).
18691      */
18692     triggerClass : 'x-form-arrow-trigger',
18693     /**
18694      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18695      */
18696     shadow:'sides',
18697     /**
18698      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18699      * anchor positions (defaults to 'tl-bl')
18700      */
18701     listAlign: 'tl-bl?',
18702     /**
18703      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18704      */
18705     maxHeight: 300,
18706     /**
18707      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18708      * query specified by the allQuery config option (defaults to 'query')
18709      */
18710     triggerAction: 'query',
18711     /**
18712      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18713      * (defaults to 4, does not apply if editable = false)
18714      */
18715     minChars : 4,
18716     /**
18717      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18718      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18719      */
18720     typeAhead: false,
18721     /**
18722      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18723      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18724      */
18725     queryDelay: 500,
18726     /**
18727      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18728      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18729      */
18730     pageSize: 0,
18731     /**
18732      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18733      * when editable = true (defaults to false)
18734      */
18735     selectOnFocus:false,
18736     /**
18737      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18738      */
18739     queryParam: 'query',
18740     /**
18741      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18742      * when mode = 'remote' (defaults to 'Loading...')
18743      */
18744     loadingText: 'Loading...',
18745     /**
18746      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18747      */
18748     resizable: false,
18749     /**
18750      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18751      */
18752     handleHeight : 8,
18753     /**
18754      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18755      * traditional select (defaults to true)
18756      */
18757     editable: true,
18758     /**
18759      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18760      */
18761     allQuery: '',
18762     /**
18763      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18764      */
18765     mode: 'remote',
18766     /**
18767      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18768      * listWidth has a higher value)
18769      */
18770     minListWidth : 70,
18771     /**
18772      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18773      * allow the user to set arbitrary text into the field (defaults to false)
18774      */
18775     forceSelection:false,
18776     /**
18777      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18778      * if typeAhead = true (defaults to 250)
18779      */
18780     typeAheadDelay : 250,
18781     /**
18782      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18783      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18784      */
18785     valueNotFoundText : undefined,
18786     /**
18787      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18788      */
18789     blockFocus : false,
18790     
18791     /**
18792      * @cfg {Boolean} disableClear Disable showing of clear button.
18793      */
18794     disableClear : false,
18795     /**
18796      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18797      */
18798     alwaysQuery : false,
18799     
18800     //private
18801     addicon : false,
18802     editicon: false,
18803     
18804     // element that contains real text value.. (when hidden is used..)
18805      
18806     // private
18807     onRender : function(ct, position){
18808         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18809         if(this.hiddenName){
18810             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18811                     'before', true);
18812             this.hiddenField.value =
18813                 this.hiddenValue !== undefined ? this.hiddenValue :
18814                 this.value !== undefined ? this.value : '';
18815
18816             // prevent input submission
18817             this.el.dom.removeAttribute('name');
18818              
18819              
18820         }
18821         if(Roo.isGecko){
18822             this.el.dom.setAttribute('autocomplete', 'off');
18823         }
18824
18825         var cls = 'x-combo-list';
18826
18827         this.list = new Roo.Layer({
18828             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18829         });
18830
18831         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18832         this.list.setWidth(lw);
18833         this.list.swallowEvent('mousewheel');
18834         this.assetHeight = 0;
18835
18836         if(this.title){
18837             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18838             this.assetHeight += this.header.getHeight();
18839         }
18840
18841         this.innerList = this.list.createChild({cls:cls+'-inner'});
18842         this.innerList.on('mouseover', this.onViewOver, this);
18843         this.innerList.on('mousemove', this.onViewMove, this);
18844         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18845         
18846         if(this.allowBlank && !this.pageSize && !this.disableClear){
18847             this.footer = this.list.createChild({cls:cls+'-ft'});
18848             this.pageTb = new Roo.Toolbar(this.footer);
18849            
18850         }
18851         if(this.pageSize){
18852             this.footer = this.list.createChild({cls:cls+'-ft'});
18853             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18854                     {pageSize: this.pageSize});
18855             
18856         }
18857         
18858         if (this.pageTb && this.allowBlank && !this.disableClear) {
18859             var _this = this;
18860             this.pageTb.add(new Roo.Toolbar.Fill(), {
18861                 cls: 'x-btn-icon x-btn-clear',
18862                 text: '&#160;',
18863                 handler: function()
18864                 {
18865                     _this.collapse();
18866                     _this.clearValue();
18867                     _this.onSelect(false, -1);
18868                 }
18869             });
18870         }
18871         if (this.footer) {
18872             this.assetHeight += this.footer.getHeight();
18873         }
18874         
18875
18876         if(!this.tpl){
18877             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18878         }
18879
18880         this.view = new Roo.View(this.innerList, this.tpl, {
18881             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18882         });
18883
18884         this.view.on('click', this.onViewClick, this);
18885
18886         this.store.on('beforeload', this.onBeforeLoad, this);
18887         this.store.on('load', this.onLoad, this);
18888         this.store.on('loadexception', this.onLoadException, this);
18889
18890         if(this.resizable){
18891             this.resizer = new Roo.Resizable(this.list,  {
18892                pinned:true, handles:'se'
18893             });
18894             this.resizer.on('resize', function(r, w, h){
18895                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18896                 this.listWidth = w;
18897                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18898                 this.restrictHeight();
18899             }, this);
18900             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18901         }
18902         if(!this.editable){
18903             this.editable = true;
18904             this.setEditable(false);
18905         }  
18906         
18907         
18908         if (typeof(this.events.add.listeners) != 'undefined') {
18909             
18910             this.addicon = this.wrap.createChild(
18911                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18912        
18913             this.addicon.on('click', function(e) {
18914                 this.fireEvent('add', this);
18915             }, this);
18916         }
18917         if (typeof(this.events.edit.listeners) != 'undefined') {
18918             
18919             this.editicon = this.wrap.createChild(
18920                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18921             if (this.addicon) {
18922                 this.editicon.setStyle('margin-left', '40px');
18923             }
18924             this.editicon.on('click', function(e) {
18925                 
18926                 // we fire even  if inothing is selected..
18927                 this.fireEvent('edit', this, this.lastData );
18928                 
18929             }, this);
18930         }
18931         
18932         
18933         
18934     },
18935
18936     // private
18937     initEvents : function(){
18938         Roo.form.ComboBox.superclass.initEvents.call(this);
18939
18940         this.keyNav = new Roo.KeyNav(this.el, {
18941             "up" : function(e){
18942                 this.inKeyMode = true;
18943                 this.selectPrev();
18944             },
18945
18946             "down" : function(e){
18947                 if(!this.isExpanded()){
18948                     this.onTriggerClick();
18949                 }else{
18950                     this.inKeyMode = true;
18951                     this.selectNext();
18952                 }
18953             },
18954
18955             "enter" : function(e){
18956                 this.onViewClick();
18957                 //return true;
18958             },
18959
18960             "esc" : function(e){
18961                 this.collapse();
18962             },
18963
18964             "tab" : function(e){
18965                 this.onViewClick(false);
18966                 this.fireEvent("specialkey", this, e);
18967                 return true;
18968             },
18969
18970             scope : this,
18971
18972             doRelay : function(foo, bar, hname){
18973                 if(hname == 'down' || this.scope.isExpanded()){
18974                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18975                 }
18976                 return true;
18977             },
18978
18979             forceKeyDown: true
18980         });
18981         this.queryDelay = Math.max(this.queryDelay || 10,
18982                 this.mode == 'local' ? 10 : 250);
18983         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18984         if(this.typeAhead){
18985             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18986         }
18987         if(this.editable !== false){
18988             this.el.on("keyup", this.onKeyUp, this);
18989         }
18990         if(this.forceSelection){
18991             this.on('blur', this.doForce, this);
18992         }
18993     },
18994
18995     onDestroy : function(){
18996         if(this.view){
18997             this.view.setStore(null);
18998             this.view.el.removeAllListeners();
18999             this.view.el.remove();
19000             this.view.purgeListeners();
19001         }
19002         if(this.list){
19003             this.list.destroy();
19004         }
19005         if(this.store){
19006             this.store.un('beforeload', this.onBeforeLoad, this);
19007             this.store.un('load', this.onLoad, this);
19008             this.store.un('loadexception', this.onLoadException, this);
19009         }
19010         Roo.form.ComboBox.superclass.onDestroy.call(this);
19011     },
19012
19013     // private
19014     fireKey : function(e){
19015         if(e.isNavKeyPress() && !this.list.isVisible()){
19016             this.fireEvent("specialkey", this, e);
19017         }
19018     },
19019
19020     // private
19021     onResize: function(w, h){
19022         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19023         
19024         if(typeof w != 'number'){
19025             // we do not handle it!?!?
19026             return;
19027         }
19028         var tw = this.trigger.getWidth();
19029         tw += this.addicon ? this.addicon.getWidth() : 0;
19030         tw += this.editicon ? this.editicon.getWidth() : 0;
19031         var x = w - tw;
19032         this.el.setWidth( this.adjustWidth('input', x));
19033             
19034         this.trigger.setStyle('left', x+'px');
19035         
19036         if(this.list && this.listWidth === undefined){
19037             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19038             this.list.setWidth(lw);
19039             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19040         }
19041         
19042     
19043         
19044     },
19045
19046     /**
19047      * Allow or prevent the user from directly editing the field text.  If false is passed,
19048      * the user will only be able to select from the items defined in the dropdown list.  This method
19049      * is the runtime equivalent of setting the 'editable' config option at config time.
19050      * @param {Boolean} value True to allow the user to directly edit the field text
19051      */
19052     setEditable : function(value){
19053         if(value == this.editable){
19054             return;
19055         }
19056         this.editable = value;
19057         if(!value){
19058             this.el.dom.setAttribute('readOnly', true);
19059             this.el.on('mousedown', this.onTriggerClick,  this);
19060             this.el.addClass('x-combo-noedit');
19061         }else{
19062             this.el.dom.setAttribute('readOnly', false);
19063             this.el.un('mousedown', this.onTriggerClick,  this);
19064             this.el.removeClass('x-combo-noedit');
19065         }
19066     },
19067
19068     // private
19069     onBeforeLoad : function(){
19070         if(!this.hasFocus){
19071             return;
19072         }
19073         this.innerList.update(this.loadingText ?
19074                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19075         this.restrictHeight();
19076         this.selectedIndex = -1;
19077     },
19078
19079     // private
19080     onLoad : function(){
19081         if(!this.hasFocus){
19082             return;
19083         }
19084         if(this.store.getCount() > 0){
19085             this.expand();
19086             this.restrictHeight();
19087             if(this.lastQuery == this.allQuery){
19088                 if(this.editable){
19089                     this.el.dom.select();
19090                 }
19091                 if(!this.selectByValue(this.value, true)){
19092                     this.select(0, true);
19093                 }
19094             }else{
19095                 this.selectNext();
19096                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19097                     this.taTask.delay(this.typeAheadDelay);
19098                 }
19099             }
19100         }else{
19101             this.onEmptyResults();
19102         }
19103         //this.el.focus();
19104     },
19105     // private
19106     onLoadException : function()
19107     {
19108         this.collapse();
19109         Roo.log(this.store.reader.jsonData);
19110         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19111             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19112         }
19113         
19114         
19115     },
19116     // private
19117     onTypeAhead : function(){
19118         if(this.store.getCount() > 0){
19119             var r = this.store.getAt(0);
19120             var newValue = r.data[this.displayField];
19121             var len = newValue.length;
19122             var selStart = this.getRawValue().length;
19123             if(selStart != len){
19124                 this.setRawValue(newValue);
19125                 this.selectText(selStart, newValue.length);
19126             }
19127         }
19128     },
19129
19130     // private
19131     onSelect : function(record, index){
19132         if(this.fireEvent('beforeselect', this, record, index) !== false){
19133             this.setFromData(index > -1 ? record.data : false);
19134             this.collapse();
19135             this.fireEvent('select', this, record, index);
19136         }
19137     },
19138
19139     /**
19140      * Returns the currently selected field value or empty string if no value is set.
19141      * @return {String} value The selected value
19142      */
19143     getValue : function(){
19144         if(this.valueField){
19145             return typeof this.value != 'undefined' ? this.value : '';
19146         }
19147         return Roo.form.ComboBox.superclass.getValue.call(this);
19148     },
19149
19150     /**
19151      * Clears any text/value currently set in the field
19152      */
19153     clearValue : function(){
19154         if(this.hiddenField){
19155             this.hiddenField.value = '';
19156         }
19157         this.value = '';
19158         this.setRawValue('');
19159         this.lastSelectionText = '';
19160         
19161     },
19162
19163     /**
19164      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19165      * will be displayed in the field.  If the value does not match the data value of an existing item,
19166      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19167      * Otherwise the field will be blank (although the value will still be set).
19168      * @param {String} value The value to match
19169      */
19170     setValue : function(v){
19171         var text = v;
19172         if(this.valueField){
19173             var r = this.findRecord(this.valueField, v);
19174             if(r){
19175                 text = r.data[this.displayField];
19176             }else if(this.valueNotFoundText !== undefined){
19177                 text = this.valueNotFoundText;
19178             }
19179         }
19180         this.lastSelectionText = text;
19181         if(this.hiddenField){
19182             this.hiddenField.value = v;
19183         }
19184         Roo.form.ComboBox.superclass.setValue.call(this, text);
19185         this.value = v;
19186     },
19187     /**
19188      * @property {Object} the last set data for the element
19189      */
19190     
19191     lastData : false,
19192     /**
19193      * Sets the value of the field based on a object which is related to the record format for the store.
19194      * @param {Object} value the value to set as. or false on reset?
19195      */
19196     setFromData : function(o){
19197         var dv = ''; // display value
19198         var vv = ''; // value value..
19199         this.lastData = o;
19200         if (this.displayField) {
19201             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19202         } else {
19203             // this is an error condition!!!
19204             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19205         }
19206         
19207         if(this.valueField){
19208             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19209         }
19210         if(this.hiddenField){
19211             this.hiddenField.value = vv;
19212             
19213             this.lastSelectionText = dv;
19214             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19215             this.value = vv;
19216             return;
19217         }
19218         // no hidden field.. - we store the value in 'value', but still display
19219         // display field!!!!
19220         this.lastSelectionText = dv;
19221         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19222         this.value = vv;
19223         
19224         
19225     },
19226     // private
19227     reset : function(){
19228         // overridden so that last data is reset..
19229         this.setValue(this.resetValue);
19230         this.clearInvalid();
19231         this.lastData = false;
19232         if (this.view) {
19233             this.view.clearSelections();
19234         }
19235     },
19236     // private
19237     findRecord : function(prop, value){
19238         var record;
19239         if(this.store.getCount() > 0){
19240             this.store.each(function(r){
19241                 if(r.data[prop] == value){
19242                     record = r;
19243                     return false;
19244                 }
19245                 return true;
19246             });
19247         }
19248         return record;
19249     },
19250     
19251     getName: function()
19252     {
19253         // returns hidden if it's set..
19254         if (!this.rendered) {return ''};
19255         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19256         
19257     },
19258     // private
19259     onViewMove : function(e, t){
19260         this.inKeyMode = false;
19261     },
19262
19263     // private
19264     onViewOver : function(e, t){
19265         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19266             return;
19267         }
19268         var item = this.view.findItemFromChild(t);
19269         if(item){
19270             var index = this.view.indexOf(item);
19271             this.select(index, false);
19272         }
19273     },
19274
19275     // private
19276     onViewClick : function(doFocus)
19277     {
19278         var index = this.view.getSelectedIndexes()[0];
19279         var r = this.store.getAt(index);
19280         if(r){
19281             this.onSelect(r, index);
19282         }
19283         if(doFocus !== false && !this.blockFocus){
19284             this.el.focus();
19285         }
19286     },
19287
19288     // private
19289     restrictHeight : function(){
19290         this.innerList.dom.style.height = '';
19291         var inner = this.innerList.dom;
19292         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19293         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19294         this.list.beginUpdate();
19295         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19296         this.list.alignTo(this.el, this.listAlign);
19297         this.list.endUpdate();
19298     },
19299
19300     // private
19301     onEmptyResults : function(){
19302         this.collapse();
19303     },
19304
19305     /**
19306      * Returns true if the dropdown list is expanded, else false.
19307      */
19308     isExpanded : function(){
19309         return this.list.isVisible();
19310     },
19311
19312     /**
19313      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19314      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19315      * @param {String} value The data value of the item to select
19316      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19317      * selected item if it is not currently in view (defaults to true)
19318      * @return {Boolean} True if the value matched an item in the list, else false
19319      */
19320     selectByValue : function(v, scrollIntoView){
19321         if(v !== undefined && v !== null){
19322             var r = this.findRecord(this.valueField || this.displayField, v);
19323             if(r){
19324                 this.select(this.store.indexOf(r), scrollIntoView);
19325                 return true;
19326             }
19327         }
19328         return false;
19329     },
19330
19331     /**
19332      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19333      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19334      * @param {Number} index The zero-based index of the list item to select
19335      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19336      * selected item if it is not currently in view (defaults to true)
19337      */
19338     select : function(index, scrollIntoView){
19339         this.selectedIndex = index;
19340         this.view.select(index);
19341         if(scrollIntoView !== false){
19342             var el = this.view.getNode(index);
19343             if(el){
19344                 this.innerList.scrollChildIntoView(el, false);
19345             }
19346         }
19347     },
19348
19349     // private
19350     selectNext : function(){
19351         var ct = this.store.getCount();
19352         if(ct > 0){
19353             if(this.selectedIndex == -1){
19354                 this.select(0);
19355             }else if(this.selectedIndex < ct-1){
19356                 this.select(this.selectedIndex+1);
19357             }
19358         }
19359     },
19360
19361     // private
19362     selectPrev : function(){
19363         var ct = this.store.getCount();
19364         if(ct > 0){
19365             if(this.selectedIndex == -1){
19366                 this.select(0);
19367             }else if(this.selectedIndex != 0){
19368                 this.select(this.selectedIndex-1);
19369             }
19370         }
19371     },
19372
19373     // private
19374     onKeyUp : function(e){
19375         if(this.editable !== false && !e.isSpecialKey()){
19376             this.lastKey = e.getKey();
19377             this.dqTask.delay(this.queryDelay);
19378         }
19379     },
19380
19381     // private
19382     validateBlur : function(){
19383         return !this.list || !this.list.isVisible();   
19384     },
19385
19386     // private
19387     initQuery : function(){
19388         this.doQuery(this.getRawValue());
19389     },
19390
19391     // private
19392     doForce : function(){
19393         if(this.el.dom.value.length > 0){
19394             this.el.dom.value =
19395                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19396              
19397         }
19398     },
19399
19400     /**
19401      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19402      * query allowing the query action to be canceled if needed.
19403      * @param {String} query The SQL query to execute
19404      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19405      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19406      * saved in the current store (defaults to false)
19407      */
19408     doQuery : function(q, forceAll){
19409         if(q === undefined || q === null){
19410             q = '';
19411         }
19412         var qe = {
19413             query: q,
19414             forceAll: forceAll,
19415             combo: this,
19416             cancel:false
19417         };
19418         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19419             return false;
19420         }
19421         q = qe.query;
19422         forceAll = qe.forceAll;
19423         if(forceAll === true || (q.length >= this.minChars)){
19424             if(this.lastQuery != q || this.alwaysQuery){
19425                 this.lastQuery = q;
19426                 if(this.mode == 'local'){
19427                     this.selectedIndex = -1;
19428                     if(forceAll){
19429                         this.store.clearFilter();
19430                     }else{
19431                         this.store.filter(this.displayField, q);
19432                     }
19433                     this.onLoad();
19434                 }else{
19435                     this.store.baseParams[this.queryParam] = q;
19436                     this.store.load({
19437                         params: this.getParams(q)
19438                     });
19439                     this.expand();
19440                 }
19441             }else{
19442                 this.selectedIndex = -1;
19443                 this.onLoad();   
19444             }
19445         }
19446     },
19447
19448     // private
19449     getParams : function(q){
19450         var p = {};
19451         //p[this.queryParam] = q;
19452         if(this.pageSize){
19453             p.start = 0;
19454             p.limit = this.pageSize;
19455         }
19456         return p;
19457     },
19458
19459     /**
19460      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19461      */
19462     collapse : function(){
19463         if(!this.isExpanded()){
19464             return;
19465         }
19466         this.list.hide();
19467         Roo.get(document).un('mousedown', this.collapseIf, this);
19468         Roo.get(document).un('mousewheel', this.collapseIf, this);
19469         if (!this.editable) {
19470             Roo.get(document).un('keydown', this.listKeyPress, this);
19471         }
19472         this.fireEvent('collapse', this);
19473     },
19474
19475     // private
19476     collapseIf : function(e){
19477         if(!e.within(this.wrap) && !e.within(this.list)){
19478             this.collapse();
19479         }
19480     },
19481
19482     /**
19483      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19484      */
19485     expand : function(){
19486         if(this.isExpanded() || !this.hasFocus){
19487             return;
19488         }
19489         this.list.alignTo(this.el, this.listAlign);
19490         this.list.show();
19491         Roo.get(document).on('mousedown', this.collapseIf, this);
19492         Roo.get(document).on('mousewheel', this.collapseIf, this);
19493         if (!this.editable) {
19494             Roo.get(document).on('keydown', this.listKeyPress, this);
19495         }
19496         
19497         this.fireEvent('expand', this);
19498     },
19499
19500     // private
19501     // Implements the default empty TriggerField.onTriggerClick function
19502     onTriggerClick : function(){
19503         if(this.disabled){
19504             return;
19505         }
19506         if(this.isExpanded()){
19507             this.collapse();
19508             if (!this.blockFocus) {
19509                 this.el.focus();
19510             }
19511             
19512         }else {
19513             this.hasFocus = true;
19514             if(this.triggerAction == 'all') {
19515                 this.doQuery(this.allQuery, true);
19516             } else {
19517                 this.doQuery(this.getRawValue());
19518             }
19519             if (!this.blockFocus) {
19520                 this.el.focus();
19521             }
19522         }
19523     },
19524     listKeyPress : function(e)
19525     {
19526         //Roo.log('listkeypress');
19527         // scroll to first matching element based on key pres..
19528         if (e.isSpecialKey()) {
19529             return false;
19530         }
19531         var k = String.fromCharCode(e.getKey()).toUpperCase();
19532         //Roo.log(k);
19533         var match  = false;
19534         var csel = this.view.getSelectedNodes();
19535         var cselitem = false;
19536         if (csel.length) {
19537             var ix = this.view.indexOf(csel[0]);
19538             cselitem  = this.store.getAt(ix);
19539             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19540                 cselitem = false;
19541             }
19542             
19543         }
19544         
19545         this.store.each(function(v) { 
19546             if (cselitem) {
19547                 // start at existing selection.
19548                 if (cselitem.id == v.id) {
19549                     cselitem = false;
19550                 }
19551                 return;
19552             }
19553                 
19554             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19555                 match = this.store.indexOf(v);
19556                 return false;
19557             }
19558         }, this);
19559         
19560         if (match === false) {
19561             return true; // no more action?
19562         }
19563         // scroll to?
19564         this.view.select(match);
19565         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19566         sn.scrollIntoView(sn.dom.parentNode, false);
19567     }
19568
19569     /** 
19570     * @cfg {Boolean} grow 
19571     * @hide 
19572     */
19573     /** 
19574     * @cfg {Number} growMin 
19575     * @hide 
19576     */
19577     /** 
19578     * @cfg {Number} growMax 
19579     * @hide 
19580     */
19581     /**
19582      * @hide
19583      * @method autoSize
19584      */
19585 });/*
19586  * Copyright(c) 2010-2012, Roo J Solutions Limited
19587  *
19588  * Licence LGPL
19589  *
19590  */
19591
19592 /**
19593  * @class Roo.form.ComboBoxArray
19594  * @extends Roo.form.TextField
19595  * A facebook style adder... for lists of email / people / countries  etc...
19596  * pick multiple items from a combo box, and shows each one.
19597  *
19598  *  Fred [x]  Brian [x]  [Pick another |v]
19599  *
19600  *
19601  *  For this to work: it needs various extra information
19602  *    - normal combo problay has
19603  *      name, hiddenName
19604  *    + displayField, valueField
19605  *
19606  *    For our purpose...
19607  *
19608  *
19609  *   If we change from 'extends' to wrapping...
19610  *   
19611  *  
19612  *
19613  
19614  
19615  * @constructor
19616  * Create a new ComboBoxArray.
19617  * @param {Object} config Configuration options
19618  */
19619  
19620
19621 Roo.form.ComboBoxArray = function(config)
19622 {
19623     this.addEvents({
19624         /**
19625          * @event beforeremove
19626          * Fires before remove the value from the list
19627              * @param {Roo.form.ComboBoxArray} _self This combo box array
19628              * @param {Roo.form.ComboBoxArray.Item} item removed item
19629              */
19630         'beforeremove' : true,
19631         /**
19632          * @event remove
19633          * Fires when remove the value from the list
19634              * @param {Roo.form.ComboBoxArray} _self This combo box array
19635              * @param {Roo.form.ComboBoxArray.Item} item removed item
19636              */
19637         'remove' : true
19638         
19639         
19640     });
19641     
19642     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19643     
19644     this.items = new Roo.util.MixedCollection(false);
19645     
19646     // construct the child combo...
19647     
19648     
19649     
19650     
19651    
19652     
19653 }
19654
19655  
19656 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19657
19658     /**
19659      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19660      */
19661     
19662     lastData : false,
19663     
19664     // behavies liek a hiddne field
19665     inputType:      'hidden',
19666     /**
19667      * @cfg {Number} width The width of the box that displays the selected element
19668      */ 
19669     width:          300,
19670
19671     
19672     
19673     /**
19674      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19675      */
19676     name : false,
19677     /**
19678      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19679      */
19680     hiddenName : false,
19681     
19682     
19683     // private the array of items that are displayed..
19684     items  : false,
19685     // private - the hidden field el.
19686     hiddenEl : false,
19687     // private - the filed el..
19688     el : false,
19689     
19690     //validateValue : function() { return true; }, // all values are ok!
19691     //onAddClick: function() { },
19692     
19693     onRender : function(ct, position) 
19694     {
19695         
19696         // create the standard hidden element
19697         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19698         
19699         
19700         // give fake names to child combo;
19701         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19702         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19703         
19704         this.combo = Roo.factory(this.combo, Roo.form);
19705         this.combo.onRender(ct, position);
19706         if (typeof(this.combo.width) != 'undefined') {
19707             this.combo.onResize(this.combo.width,0);
19708         }
19709         
19710         this.combo.initEvents();
19711         
19712         // assigned so form know we need to do this..
19713         this.store          = this.combo.store;
19714         this.valueField     = this.combo.valueField;
19715         this.displayField   = this.combo.displayField ;
19716         
19717         
19718         this.combo.wrap.addClass('x-cbarray-grp');
19719         
19720         var cbwrap = this.combo.wrap.createChild(
19721             {tag: 'div', cls: 'x-cbarray-cb'},
19722             this.combo.el.dom
19723         );
19724         
19725              
19726         this.hiddenEl = this.combo.wrap.createChild({
19727             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19728         });
19729         this.el = this.combo.wrap.createChild({
19730             tag: 'input',  type:'hidden' , name: this.name, value : ''
19731         });
19732          //   this.el.dom.removeAttribute("name");
19733         
19734         
19735         this.outerWrap = this.combo.wrap;
19736         this.wrap = cbwrap;
19737         
19738         this.outerWrap.setWidth(this.width);
19739         this.outerWrap.dom.removeChild(this.el.dom);
19740         
19741         this.wrap.dom.appendChild(this.el.dom);
19742         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19743         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19744         
19745         this.combo.trigger.setStyle('position','relative');
19746         this.combo.trigger.setStyle('left', '0px');
19747         this.combo.trigger.setStyle('top', '2px');
19748         
19749         this.combo.el.setStyle('vertical-align', 'text-bottom');
19750         
19751         //this.trigger.setStyle('vertical-align', 'top');
19752         
19753         // this should use the code from combo really... on('add' ....)
19754         if (this.adder) {
19755             
19756         
19757             this.adder = this.outerWrap.createChild(
19758                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19759             var _t = this;
19760             this.adder.on('click', function(e) {
19761                 _t.fireEvent('adderclick', this, e);
19762             }, _t);
19763         }
19764         //var _t = this;
19765         //this.adder.on('click', this.onAddClick, _t);
19766         
19767         
19768         this.combo.on('select', function(cb, rec, ix) {
19769             this.addItem(rec.data);
19770             
19771             cb.setValue('');
19772             cb.el.dom.value = '';
19773             //cb.lastData = rec.data;
19774             // add to list
19775             
19776         }, this);
19777         
19778         
19779     },
19780     
19781     
19782     getName: function()
19783     {
19784         // returns hidden if it's set..
19785         if (!this.rendered) {return ''};
19786         return  this.hiddenName ? this.hiddenName : this.name;
19787         
19788     },
19789     
19790     
19791     onResize: function(w, h){
19792         
19793         return;
19794         // not sure if this is needed..
19795         //this.combo.onResize(w,h);
19796         
19797         if(typeof w != 'number'){
19798             // we do not handle it!?!?
19799             return;
19800         }
19801         var tw = this.combo.trigger.getWidth();
19802         tw += this.addicon ? this.addicon.getWidth() : 0;
19803         tw += this.editicon ? this.editicon.getWidth() : 0;
19804         var x = w - tw;
19805         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19806             
19807         this.combo.trigger.setStyle('left', '0px');
19808         
19809         if(this.list && this.listWidth === undefined){
19810             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19811             this.list.setWidth(lw);
19812             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19813         }
19814         
19815     
19816         
19817     },
19818     
19819     addItem: function(rec)
19820     {
19821         var valueField = this.combo.valueField;
19822         var displayField = this.combo.displayField;
19823         if (this.items.indexOfKey(rec[valueField]) > -1) {
19824             //console.log("GOT " + rec.data.id);
19825             return;
19826         }
19827         
19828         var x = new Roo.form.ComboBoxArray.Item({
19829             //id : rec[this.idField],
19830             data : rec,
19831             displayField : displayField ,
19832             tipField : displayField ,
19833             cb : this
19834         });
19835         // use the 
19836         this.items.add(rec[valueField],x);
19837         // add it before the element..
19838         this.updateHiddenEl();
19839         x.render(this.outerWrap, this.wrap.dom);
19840         // add the image handler..
19841     },
19842     
19843     updateHiddenEl : function()
19844     {
19845         this.validate();
19846         if (!this.hiddenEl) {
19847             return;
19848         }
19849         var ar = [];
19850         var idField = this.combo.valueField;
19851         
19852         this.items.each(function(f) {
19853             ar.push(f.data[idField]);
19854            
19855         });
19856         this.hiddenEl.dom.value = ar.join(',');
19857         this.validate();
19858     },
19859     
19860     reset : function()
19861     {
19862         this.items.clear();
19863         
19864         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19865            el.remove();
19866         });
19867         
19868         this.el.dom.value = '';
19869         if (this.hiddenEl) {
19870             this.hiddenEl.dom.value = '';
19871         }
19872         
19873     },
19874     getValue: function()
19875     {
19876         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19877     },
19878     setValue: function(v) // not a valid action - must use addItems..
19879     {
19880          
19881         this.reset();
19882         
19883         
19884         
19885         if (this.store.isLocal && (typeof(v) == 'string')) {
19886             // then we can use the store to find the values..
19887             // comma seperated at present.. this needs to allow JSON based encoding..
19888             this.hiddenEl.value  = v;
19889             var v_ar = [];
19890             Roo.each(v.split(','), function(k) {
19891                 Roo.log("CHECK " + this.valueField + ',' + k);
19892                 var li = this.store.query(this.valueField, k);
19893                 if (!li.length) {
19894                     return;
19895                 }
19896                 var add = {};
19897                 add[this.valueField] = k;
19898                 add[this.displayField] = li.item(0).data[this.displayField];
19899                 
19900                 this.addItem(add);
19901             }, this) 
19902              
19903         }
19904         if (typeof(v) == 'object' ) {
19905             // then let's assume it's an array of objects..
19906             Roo.each(v, function(l) {
19907                 this.addItem(l);
19908             }, this);
19909              
19910         }
19911         
19912         
19913     },
19914     setFromData: function(v)
19915     {
19916         // this recieves an object, if setValues is called.
19917         this.reset();
19918         this.el.dom.value = v[this.displayField];
19919         this.hiddenEl.dom.value = v[this.valueField];
19920         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19921             return;
19922         }
19923         var kv = v[this.valueField];
19924         var dv = v[this.displayField];
19925         kv = typeof(kv) != 'string' ? '' : kv;
19926         dv = typeof(dv) != 'string' ? '' : dv;
19927         
19928         
19929         var keys = kv.split(',');
19930         var display = dv.split(',');
19931         for (var i = 0 ; i < keys.length; i++) {
19932             
19933             add = {};
19934             add[this.valueField] = keys[i];
19935             add[this.displayField] = display[i];
19936             this.addItem(add);
19937         }
19938       
19939         
19940     },
19941     
19942     /**
19943      * Validates the combox array value
19944      * @return {Boolean} True if the value is valid, else false
19945      */
19946     validate : function(){
19947         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19948             this.clearInvalid();
19949             return true;
19950         }
19951         return false;
19952     },
19953     
19954     validateValue : function(value){
19955         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19956         
19957     },
19958     
19959     /*@
19960      * overide
19961      * 
19962      */
19963     isDirty : function() {
19964         if(this.disabled) {
19965             return false;
19966         }
19967         
19968         try {
19969             var d = Roo.decode(String(this.originalValue));
19970         } catch (e) {
19971             return String(this.getValue()) !== String(this.originalValue);
19972         }
19973         
19974         var originalValue = [];
19975         
19976         for (var i = 0; i < d.length; i++){
19977             originalValue.push(d[i][this.valueField]);
19978         }
19979         
19980         return String(this.getValue()) !== String(originalValue.join(','));
19981         
19982     }
19983     
19984 });
19985
19986
19987
19988 /**
19989  * @class Roo.form.ComboBoxArray.Item
19990  * @extends Roo.BoxComponent
19991  * A selected item in the list
19992  *  Fred [x]  Brian [x]  [Pick another |v]
19993  * 
19994  * @constructor
19995  * Create a new item.
19996  * @param {Object} config Configuration options
19997  */
19998  
19999 Roo.form.ComboBoxArray.Item = function(config) {
20000     config.id = Roo.id();
20001     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20002 }
20003
20004 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20005     data : {},
20006     cb: false,
20007     displayField : false,
20008     tipField : false,
20009     
20010     
20011     defaultAutoCreate : {
20012         tag: 'div',
20013         cls: 'x-cbarray-item',
20014         cn : [ 
20015             { tag: 'div' },
20016             {
20017                 tag: 'img',
20018                 width:16,
20019                 height : 16,
20020                 src : Roo.BLANK_IMAGE_URL ,
20021                 align: 'center'
20022             }
20023         ]
20024         
20025     },
20026     
20027  
20028     onRender : function(ct, position)
20029     {
20030         Roo.form.Field.superclass.onRender.call(this, ct, position);
20031         
20032         if(!this.el){
20033             var cfg = this.getAutoCreate();
20034             this.el = ct.createChild(cfg, position);
20035         }
20036         
20037         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20038         
20039         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20040             this.cb.renderer(this.data) :
20041             String.format('{0}',this.data[this.displayField]);
20042         
20043             
20044         this.el.child('div').dom.setAttribute('qtip',
20045                         String.format('{0}',this.data[this.tipField])
20046         );
20047         
20048         this.el.child('img').on('click', this.remove, this);
20049         
20050     },
20051    
20052     remove : function()
20053     {
20054         if(this.cb.disabled){
20055             return;
20056         }
20057         
20058         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20059             this.cb.items.remove(this);
20060             this.el.child('img').un('click', this.remove, this);
20061             this.el.remove();
20062             this.cb.updateHiddenEl();
20063
20064             this.cb.fireEvent('remove', this.cb, this);
20065         }
20066         
20067     }
20068 });/*
20069  * Based on:
20070  * Ext JS Library 1.1.1
20071  * Copyright(c) 2006-2007, Ext JS, LLC.
20072  *
20073  * Originally Released Under LGPL - original licence link has changed is not relivant.
20074  *
20075  * Fork - LGPL
20076  * <script type="text/javascript">
20077  */
20078 /**
20079  * @class Roo.form.Checkbox
20080  * @extends Roo.form.Field
20081  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20082  * @constructor
20083  * Creates a new Checkbox
20084  * @param {Object} config Configuration options
20085  */
20086 Roo.form.Checkbox = function(config){
20087     Roo.form.Checkbox.superclass.constructor.call(this, config);
20088     this.addEvents({
20089         /**
20090          * @event check
20091          * Fires when the checkbox is checked or unchecked.
20092              * @param {Roo.form.Checkbox} this This checkbox
20093              * @param {Boolean} checked The new checked value
20094              */
20095         check : true
20096     });
20097 };
20098
20099 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20100     /**
20101      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20102      */
20103     focusClass : undefined,
20104     /**
20105      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20106      */
20107     fieldClass: "x-form-field",
20108     /**
20109      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20110      */
20111     checked: false,
20112     /**
20113      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20114      * {tag: "input", type: "checkbox", autocomplete: "off"})
20115      */
20116     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20117     /**
20118      * @cfg {String} boxLabel The text that appears beside the checkbox
20119      */
20120     boxLabel : "",
20121     /**
20122      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20123      */  
20124     inputValue : '1',
20125     /**
20126      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20127      */
20128      valueOff: '0', // value when not checked..
20129
20130     actionMode : 'viewEl', 
20131     //
20132     // private
20133     itemCls : 'x-menu-check-item x-form-item',
20134     groupClass : 'x-menu-group-item',
20135     inputType : 'hidden',
20136     
20137     
20138     inSetChecked: false, // check that we are not calling self...
20139     
20140     inputElement: false, // real input element?
20141     basedOn: false, // ????
20142     
20143     isFormField: true, // not sure where this is needed!!!!
20144
20145     onResize : function(){
20146         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20147         if(!this.boxLabel){
20148             this.el.alignTo(this.wrap, 'c-c');
20149         }
20150     },
20151
20152     initEvents : function(){
20153         Roo.form.Checkbox.superclass.initEvents.call(this);
20154         this.el.on("click", this.onClick,  this);
20155         this.el.on("change", this.onClick,  this);
20156     },
20157
20158
20159     getResizeEl : function(){
20160         return this.wrap;
20161     },
20162
20163     getPositionEl : function(){
20164         return this.wrap;
20165     },
20166
20167     // private
20168     onRender : function(ct, position){
20169         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20170         /*
20171         if(this.inputValue !== undefined){
20172             this.el.dom.value = this.inputValue;
20173         }
20174         */
20175         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20176         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20177         var viewEl = this.wrap.createChild({ 
20178             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20179         this.viewEl = viewEl;   
20180         this.wrap.on('click', this.onClick,  this); 
20181         
20182         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20183         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20184         
20185         
20186         
20187         if(this.boxLabel){
20188             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20189         //    viewEl.on('click', this.onClick,  this); 
20190         }
20191         //if(this.checked){
20192             this.setChecked(this.checked);
20193         //}else{
20194             //this.checked = this.el.dom;
20195         //}
20196
20197     },
20198
20199     // private
20200     initValue : Roo.emptyFn,
20201
20202     /**
20203      * Returns the checked state of the checkbox.
20204      * @return {Boolean} True if checked, else false
20205      */
20206     getValue : function(){
20207         if(this.el){
20208             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20209         }
20210         return this.valueOff;
20211         
20212     },
20213
20214         // private
20215     onClick : function(){ 
20216         if (this.disabled) {
20217             return;
20218         }
20219         this.setChecked(!this.checked);
20220
20221         //if(this.el.dom.checked != this.checked){
20222         //    this.setValue(this.el.dom.checked);
20223        // }
20224     },
20225
20226     /**
20227      * Sets the checked state of the checkbox.
20228      * On is always based on a string comparison between inputValue and the param.
20229      * @param {Boolean/String} value - the value to set 
20230      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20231      */
20232     setValue : function(v,suppressEvent){
20233         
20234         
20235         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20236         //if(this.el && this.el.dom){
20237         //    this.el.dom.checked = this.checked;
20238         //    this.el.dom.defaultChecked = this.checked;
20239         //}
20240         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20241         //this.fireEvent("check", this, this.checked);
20242     },
20243     // private..
20244     setChecked : function(state,suppressEvent)
20245     {
20246         if (this.inSetChecked) {
20247             this.checked = state;
20248             return;
20249         }
20250         
20251     
20252         if(this.wrap){
20253             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20254         }
20255         this.checked = state;
20256         if(suppressEvent !== true){
20257             this.fireEvent('check', this, state);
20258         }
20259         this.inSetChecked = true;
20260         this.el.dom.value = state ? this.inputValue : this.valueOff;
20261         this.inSetChecked = false;
20262         
20263     },
20264     // handle setting of hidden value by some other method!!?!?
20265     setFromHidden: function()
20266     {
20267         if(!this.el){
20268             return;
20269         }
20270         //console.log("SET FROM HIDDEN");
20271         //alert('setFrom hidden');
20272         this.setValue(this.el.dom.value);
20273     },
20274     
20275     onDestroy : function()
20276     {
20277         if(this.viewEl){
20278             Roo.get(this.viewEl).remove();
20279         }
20280          
20281         Roo.form.Checkbox.superclass.onDestroy.call(this);
20282     },
20283     
20284     setBoxLabel : function(str)
20285     {
20286         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20287     }
20288
20289 });/*
20290  * Based on:
20291  * Ext JS Library 1.1.1
20292  * Copyright(c) 2006-2007, Ext JS, LLC.
20293  *
20294  * Originally Released Under LGPL - original licence link has changed is not relivant.
20295  *
20296  * Fork - LGPL
20297  * <script type="text/javascript">
20298  */
20299  
20300 /**
20301  * @class Roo.form.Radio
20302  * @extends Roo.form.Checkbox
20303  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20304  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20305  * @constructor
20306  * Creates a new Radio
20307  * @param {Object} config Configuration options
20308  */
20309 Roo.form.Radio = function(){
20310     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20311 };
20312 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20313     inputType: 'radio',
20314
20315     /**
20316      * If this radio is part of a group, it will return the selected value
20317      * @return {String}
20318      */
20319     getGroupValue : function(){
20320         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20321     },
20322     
20323     
20324     onRender : function(ct, position){
20325         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20326         
20327         if(this.inputValue !== undefined){
20328             this.el.dom.value = this.inputValue;
20329         }
20330          
20331         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20332         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20333         //var viewEl = this.wrap.createChild({ 
20334         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20335         //this.viewEl = viewEl;   
20336         //this.wrap.on('click', this.onClick,  this); 
20337         
20338         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20339         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20340         
20341         
20342         
20343         if(this.boxLabel){
20344             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20345         //    viewEl.on('click', this.onClick,  this); 
20346         }
20347          if(this.checked){
20348             this.el.dom.checked =   'checked' ;
20349         }
20350          
20351     } 
20352     
20353     
20354 });//<script type="text/javascript">
20355
20356 /*
20357  * Based  Ext JS Library 1.1.1
20358  * Copyright(c) 2006-2007, Ext JS, LLC.
20359  * LGPL
20360  *
20361  */
20362  
20363 /**
20364  * @class Roo.HtmlEditorCore
20365  * @extends Roo.Component
20366  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20367  *
20368  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20369  */
20370
20371 Roo.HtmlEditorCore = function(config){
20372     
20373     
20374     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20375     
20376     
20377     this.addEvents({
20378         /**
20379          * @event initialize
20380          * Fires when the editor is fully initialized (including the iframe)
20381          * @param {Roo.HtmlEditorCore} this
20382          */
20383         initialize: true,
20384         /**
20385          * @event activate
20386          * Fires when the editor is first receives the focus. Any insertion must wait
20387          * until after this event.
20388          * @param {Roo.HtmlEditorCore} this
20389          */
20390         activate: true,
20391          /**
20392          * @event beforesync
20393          * Fires before the textarea is updated with content from the editor iframe. Return false
20394          * to cancel the sync.
20395          * @param {Roo.HtmlEditorCore} this
20396          * @param {String} html
20397          */
20398         beforesync: true,
20399          /**
20400          * @event beforepush
20401          * Fires before the iframe editor is updated with content from the textarea. Return false
20402          * to cancel the push.
20403          * @param {Roo.HtmlEditorCore} this
20404          * @param {String} html
20405          */
20406         beforepush: true,
20407          /**
20408          * @event sync
20409          * Fires when the textarea is updated with content from the editor iframe.
20410          * @param {Roo.HtmlEditorCore} this
20411          * @param {String} html
20412          */
20413         sync: true,
20414          /**
20415          * @event push
20416          * Fires when the iframe editor is updated with content from the textarea.
20417          * @param {Roo.HtmlEditorCore} this
20418          * @param {String} html
20419          */
20420         push: true,
20421         
20422         /**
20423          * @event editorevent
20424          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20425          * @param {Roo.HtmlEditorCore} this
20426          */
20427         editorevent: true
20428         
20429     });
20430     
20431     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20432     
20433     // defaults : white / black...
20434     this.applyBlacklists();
20435     
20436     
20437     
20438 };
20439
20440
20441 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20442
20443
20444      /**
20445      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20446      */
20447     
20448     owner : false,
20449     
20450      /**
20451      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20452      *                        Roo.resizable.
20453      */
20454     resizable : false,
20455      /**
20456      * @cfg {Number} height (in pixels)
20457      */   
20458     height: 300,
20459    /**
20460      * @cfg {Number} width (in pixels)
20461      */   
20462     width: 500,
20463     
20464     /**
20465      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20466      * 
20467      */
20468     stylesheets: false,
20469     
20470     // id of frame..
20471     frameId: false,
20472     
20473     // private properties
20474     validationEvent : false,
20475     deferHeight: true,
20476     initialized : false,
20477     activated : false,
20478     sourceEditMode : false,
20479     onFocus : Roo.emptyFn,
20480     iframePad:3,
20481     hideMode:'offsets',
20482     
20483     clearUp: true,
20484     
20485     // blacklist + whitelisted elements..
20486     black: false,
20487     white: false,
20488      
20489     
20490
20491     /**
20492      * Protected method that will not generally be called directly. It
20493      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20494      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20495      */
20496     getDocMarkup : function(){
20497         // body styles..
20498         var st = '';
20499         
20500         // inherit styels from page...?? 
20501         if (this.stylesheets === false) {
20502             
20503             Roo.get(document.head).select('style').each(function(node) {
20504                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20505             });
20506             
20507             Roo.get(document.head).select('link').each(function(node) { 
20508                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20509             });
20510             
20511         } else if (!this.stylesheets.length) {
20512                 // simple..
20513                 st = '<style type="text/css">' +
20514                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20515                    '</style>';
20516         } else { 
20517             st = '<style type="text/css">' +
20518                     this.stylesheets +
20519                 '</style>';
20520         }
20521         
20522         st +=  '<style type="text/css">' +
20523             'IMG { cursor: pointer } ' +
20524         '</style>';
20525
20526         
20527         return '<html><head>' + st  +
20528             //<style type="text/css">' +
20529             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20530             //'</style>' +
20531             ' </head><body class="roo-htmleditor-body"></body></html>';
20532     },
20533
20534     // private
20535     onRender : function(ct, position)
20536     {
20537         var _t = this;
20538         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20539         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20540         
20541         
20542         this.el.dom.style.border = '0 none';
20543         this.el.dom.setAttribute('tabIndex', -1);
20544         this.el.addClass('x-hidden hide');
20545         
20546         
20547         
20548         if(Roo.isIE){ // fix IE 1px bogus margin
20549             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20550         }
20551        
20552         
20553         this.frameId = Roo.id();
20554         
20555          
20556         
20557         var iframe = this.owner.wrap.createChild({
20558             tag: 'iframe',
20559             cls: 'form-control', // bootstrap..
20560             id: this.frameId,
20561             name: this.frameId,
20562             frameBorder : 'no',
20563             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20564         }, this.el
20565         );
20566         
20567         
20568         this.iframe = iframe.dom;
20569
20570          this.assignDocWin();
20571         
20572         this.doc.designMode = 'on';
20573        
20574         this.doc.open();
20575         this.doc.write(this.getDocMarkup());
20576         this.doc.close();
20577
20578         
20579         var task = { // must defer to wait for browser to be ready
20580             run : function(){
20581                 //console.log("run task?" + this.doc.readyState);
20582                 this.assignDocWin();
20583                 if(this.doc.body || this.doc.readyState == 'complete'){
20584                     try {
20585                         this.doc.designMode="on";
20586                     } catch (e) {
20587                         return;
20588                     }
20589                     Roo.TaskMgr.stop(task);
20590                     this.initEditor.defer(10, this);
20591                 }
20592             },
20593             interval : 10,
20594             duration: 10000,
20595             scope: this
20596         };
20597         Roo.TaskMgr.start(task);
20598
20599     },
20600
20601     // private
20602     onResize : function(w, h)
20603     {
20604          Roo.log('resize: ' +w + ',' + h );
20605         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20606         if(!this.iframe){
20607             return;
20608         }
20609         if(typeof w == 'number'){
20610             
20611             this.iframe.style.width = w + 'px';
20612         }
20613         if(typeof h == 'number'){
20614             
20615             this.iframe.style.height = h + 'px';
20616             if(this.doc){
20617                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20618             }
20619         }
20620         
20621     },
20622
20623     /**
20624      * Toggles the editor between standard and source edit mode.
20625      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20626      */
20627     toggleSourceEdit : function(sourceEditMode){
20628         
20629         this.sourceEditMode = sourceEditMode === true;
20630         
20631         if(this.sourceEditMode){
20632  
20633             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20634             
20635         }else{
20636             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20637             //this.iframe.className = '';
20638             this.deferFocus();
20639         }
20640         //this.setSize(this.owner.wrap.getSize());
20641         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20642     },
20643
20644     
20645   
20646
20647     /**
20648      * Protected method that will not generally be called directly. If you need/want
20649      * custom HTML cleanup, this is the method you should override.
20650      * @param {String} html The HTML to be cleaned
20651      * return {String} The cleaned HTML
20652      */
20653     cleanHtml : function(html){
20654         html = String(html);
20655         if(html.length > 5){
20656             if(Roo.isSafari){ // strip safari nonsense
20657                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20658             }
20659         }
20660         if(html == '&nbsp;'){
20661             html = '';
20662         }
20663         return html;
20664     },
20665
20666     /**
20667      * HTML Editor -> Textarea
20668      * Protected method that will not generally be called directly. Syncs the contents
20669      * of the editor iframe with the textarea.
20670      */
20671     syncValue : function(){
20672         if(this.initialized){
20673             var bd = (this.doc.body || this.doc.documentElement);
20674             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20675             var html = bd.innerHTML;
20676             if(Roo.isSafari){
20677                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20678                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20679                 if(m && m[1]){
20680                     html = '<div style="'+m[0]+'">' + html + '</div>';
20681                 }
20682             }
20683             html = this.cleanHtml(html);
20684             // fix up the special chars.. normaly like back quotes in word...
20685             // however we do not want to do this with chinese..
20686             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20687                 var cc = b.charCodeAt();
20688                 if (
20689                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20690                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20691                     (cc >= 0xf900 && cc < 0xfb00 )
20692                 ) {
20693                         return b;
20694                 }
20695                 return "&#"+cc+";" 
20696             });
20697             if(this.owner.fireEvent('beforesync', this, html) !== false){
20698                 this.el.dom.value = html;
20699                 this.owner.fireEvent('sync', this, html);
20700             }
20701         }
20702     },
20703
20704     /**
20705      * Protected method that will not generally be called directly. Pushes the value of the textarea
20706      * into the iframe editor.
20707      */
20708     pushValue : function(){
20709         if(this.initialized){
20710             var v = this.el.dom.value.trim();
20711             
20712 //            if(v.length < 1){
20713 //                v = '&#160;';
20714 //            }
20715             
20716             if(this.owner.fireEvent('beforepush', this, v) !== false){
20717                 var d = (this.doc.body || this.doc.documentElement);
20718                 d.innerHTML = v;
20719                 this.cleanUpPaste();
20720                 this.el.dom.value = d.innerHTML;
20721                 this.owner.fireEvent('push', this, v);
20722             }
20723         }
20724     },
20725
20726     // private
20727     deferFocus : function(){
20728         this.focus.defer(10, this);
20729     },
20730
20731     // doc'ed in Field
20732     focus : function(){
20733         if(this.win && !this.sourceEditMode){
20734             this.win.focus();
20735         }else{
20736             this.el.focus();
20737         }
20738     },
20739     
20740     assignDocWin: function()
20741     {
20742         var iframe = this.iframe;
20743         
20744          if(Roo.isIE){
20745             this.doc = iframe.contentWindow.document;
20746             this.win = iframe.contentWindow;
20747         } else {
20748 //            if (!Roo.get(this.frameId)) {
20749 //                return;
20750 //            }
20751 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20752 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20753             
20754             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20755                 return;
20756             }
20757             
20758             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20759             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20760         }
20761     },
20762     
20763     // private
20764     initEditor : function(){
20765         //console.log("INIT EDITOR");
20766         this.assignDocWin();
20767         
20768         
20769         
20770         this.doc.designMode="on";
20771         this.doc.open();
20772         this.doc.write(this.getDocMarkup());
20773         this.doc.close();
20774         
20775         var dbody = (this.doc.body || this.doc.documentElement);
20776         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20777         // this copies styles from the containing element into thsi one..
20778         // not sure why we need all of this..
20779         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20780         
20781         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20782         //ss['background-attachment'] = 'fixed'; // w3c
20783         dbody.bgProperties = 'fixed'; // ie
20784         //Roo.DomHelper.applyStyles(dbody, ss);
20785         Roo.EventManager.on(this.doc, {
20786             //'mousedown': this.onEditorEvent,
20787             'mouseup': this.onEditorEvent,
20788             'dblclick': this.onEditorEvent,
20789             'click': this.onEditorEvent,
20790             'keyup': this.onEditorEvent,
20791             buffer:100,
20792             scope: this
20793         });
20794         if(Roo.isGecko){
20795             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20796         }
20797         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20798             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20799         }
20800         this.initialized = true;
20801
20802         this.owner.fireEvent('initialize', this);
20803         this.pushValue();
20804     },
20805
20806     // private
20807     onDestroy : function(){
20808         
20809         
20810         
20811         if(this.rendered){
20812             
20813             //for (var i =0; i < this.toolbars.length;i++) {
20814             //    // fixme - ask toolbars for heights?
20815             //    this.toolbars[i].onDestroy();
20816            // }
20817             
20818             //this.wrap.dom.innerHTML = '';
20819             //this.wrap.remove();
20820         }
20821     },
20822
20823     // private
20824     onFirstFocus : function(){
20825         
20826         this.assignDocWin();
20827         
20828         
20829         this.activated = true;
20830          
20831     
20832         if(Roo.isGecko){ // prevent silly gecko errors
20833             this.win.focus();
20834             var s = this.win.getSelection();
20835             if(!s.focusNode || s.focusNode.nodeType != 3){
20836                 var r = s.getRangeAt(0);
20837                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20838                 r.collapse(true);
20839                 this.deferFocus();
20840             }
20841             try{
20842                 this.execCmd('useCSS', true);
20843                 this.execCmd('styleWithCSS', false);
20844             }catch(e){}
20845         }
20846         this.owner.fireEvent('activate', this);
20847     },
20848
20849     // private
20850     adjustFont: function(btn){
20851         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20852         //if(Roo.isSafari){ // safari
20853         //    adjust *= 2;
20854        // }
20855         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20856         if(Roo.isSafari){ // safari
20857             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20858             v =  (v < 10) ? 10 : v;
20859             v =  (v > 48) ? 48 : v;
20860             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20861             
20862         }
20863         
20864         
20865         v = Math.max(1, v+adjust);
20866         
20867         this.execCmd('FontSize', v  );
20868     },
20869
20870     onEditorEvent : function(e)
20871     {
20872         this.owner.fireEvent('editorevent', this, e);
20873       //  this.updateToolbar();
20874         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20875     },
20876
20877     insertTag : function(tg)
20878     {
20879         // could be a bit smarter... -> wrap the current selected tRoo..
20880         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20881             
20882             range = this.createRange(this.getSelection());
20883             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20884             wrappingNode.appendChild(range.extractContents());
20885             range.insertNode(wrappingNode);
20886
20887             return;
20888             
20889             
20890             
20891         }
20892         this.execCmd("formatblock",   tg);
20893         
20894     },
20895     
20896     insertText : function(txt)
20897     {
20898         
20899         
20900         var range = this.createRange();
20901         range.deleteContents();
20902                //alert(Sender.getAttribute('label'));
20903                
20904         range.insertNode(this.doc.createTextNode(txt));
20905     } ,
20906     
20907      
20908
20909     /**
20910      * Executes a Midas editor command on the editor document and performs necessary focus and
20911      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20912      * @param {String} cmd The Midas command
20913      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20914      */
20915     relayCmd : function(cmd, value){
20916         this.win.focus();
20917         this.execCmd(cmd, value);
20918         this.owner.fireEvent('editorevent', this);
20919         //this.updateToolbar();
20920         this.owner.deferFocus();
20921     },
20922
20923     /**
20924      * Executes a Midas editor command directly on the editor document.
20925      * For visual commands, you should use {@link #relayCmd} instead.
20926      * <b>This should only be called after the editor is initialized.</b>
20927      * @param {String} cmd The Midas command
20928      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20929      */
20930     execCmd : function(cmd, value){
20931         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20932         this.syncValue();
20933     },
20934  
20935  
20936    
20937     /**
20938      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20939      * to insert tRoo.
20940      * @param {String} text | dom node.. 
20941      */
20942     insertAtCursor : function(text)
20943     {
20944         
20945         if(!this.activated){
20946             return;
20947         }
20948         /*
20949         if(Roo.isIE){
20950             this.win.focus();
20951             var r = this.doc.selection.createRange();
20952             if(r){
20953                 r.collapse(true);
20954                 r.pasteHTML(text);
20955                 this.syncValue();
20956                 this.deferFocus();
20957             
20958             }
20959             return;
20960         }
20961         */
20962         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20963             this.win.focus();
20964             
20965             
20966             // from jquery ui (MIT licenced)
20967             var range, node;
20968             var win = this.win;
20969             
20970             if (win.getSelection && win.getSelection().getRangeAt) {
20971                 range = win.getSelection().getRangeAt(0);
20972                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20973                 range.insertNode(node);
20974             } else if (win.document.selection && win.document.selection.createRange) {
20975                 // no firefox support
20976                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20977                 win.document.selection.createRange().pasteHTML(txt);
20978             } else {
20979                 // no firefox support
20980                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20981                 this.execCmd('InsertHTML', txt);
20982             } 
20983             
20984             this.syncValue();
20985             
20986             this.deferFocus();
20987         }
20988     },
20989  // private
20990     mozKeyPress : function(e){
20991         if(e.ctrlKey){
20992             var c = e.getCharCode(), cmd;
20993           
20994             if(c > 0){
20995                 c = String.fromCharCode(c).toLowerCase();
20996                 switch(c){
20997                     case 'b':
20998                         cmd = 'bold';
20999                         break;
21000                     case 'i':
21001                         cmd = 'italic';
21002                         break;
21003                     
21004                     case 'u':
21005                         cmd = 'underline';
21006                         break;
21007                     
21008                     case 'v':
21009                         this.cleanUpPaste.defer(100, this);
21010                         return;
21011                         
21012                 }
21013                 if(cmd){
21014                     this.win.focus();
21015                     this.execCmd(cmd);
21016                     this.deferFocus();
21017                     e.preventDefault();
21018                 }
21019                 
21020             }
21021         }
21022     },
21023
21024     // private
21025     fixKeys : function(){ // load time branching for fastest keydown performance
21026         if(Roo.isIE){
21027             return function(e){
21028                 var k = e.getKey(), r;
21029                 if(k == e.TAB){
21030                     e.stopEvent();
21031                     r = this.doc.selection.createRange();
21032                     if(r){
21033                         r.collapse(true);
21034                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21035                         this.deferFocus();
21036                     }
21037                     return;
21038                 }
21039                 
21040                 if(k == e.ENTER){
21041                     r = this.doc.selection.createRange();
21042                     if(r){
21043                         var target = r.parentElement();
21044                         if(!target || target.tagName.toLowerCase() != 'li'){
21045                             e.stopEvent();
21046                             r.pasteHTML('<br />');
21047                             r.collapse(false);
21048                             r.select();
21049                         }
21050                     }
21051                 }
21052                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21053                     this.cleanUpPaste.defer(100, this);
21054                     return;
21055                 }
21056                 
21057                 
21058             };
21059         }else if(Roo.isOpera){
21060             return function(e){
21061                 var k = e.getKey();
21062                 if(k == e.TAB){
21063                     e.stopEvent();
21064                     this.win.focus();
21065                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21066                     this.deferFocus();
21067                 }
21068                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21069                     this.cleanUpPaste.defer(100, this);
21070                     return;
21071                 }
21072                 
21073             };
21074         }else if(Roo.isSafari){
21075             return function(e){
21076                 var k = e.getKey();
21077                 
21078                 if(k == e.TAB){
21079                     e.stopEvent();
21080                     this.execCmd('InsertText','\t');
21081                     this.deferFocus();
21082                     return;
21083                 }
21084                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21085                     this.cleanUpPaste.defer(100, this);
21086                     return;
21087                 }
21088                 
21089              };
21090         }
21091     }(),
21092     
21093     getAllAncestors: function()
21094     {
21095         var p = this.getSelectedNode();
21096         var a = [];
21097         if (!p) {
21098             a.push(p); // push blank onto stack..
21099             p = this.getParentElement();
21100         }
21101         
21102         
21103         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21104             a.push(p);
21105             p = p.parentNode;
21106         }
21107         a.push(this.doc.body);
21108         return a;
21109     },
21110     lastSel : false,
21111     lastSelNode : false,
21112     
21113     
21114     getSelection : function() 
21115     {
21116         this.assignDocWin();
21117         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21118     },
21119     
21120     getSelectedNode: function() 
21121     {
21122         // this may only work on Gecko!!!
21123         
21124         // should we cache this!!!!
21125         
21126         
21127         
21128          
21129         var range = this.createRange(this.getSelection()).cloneRange();
21130         
21131         if (Roo.isIE) {
21132             var parent = range.parentElement();
21133             while (true) {
21134                 var testRange = range.duplicate();
21135                 testRange.moveToElementText(parent);
21136                 if (testRange.inRange(range)) {
21137                     break;
21138                 }
21139                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21140                     break;
21141                 }
21142                 parent = parent.parentElement;
21143             }
21144             return parent;
21145         }
21146         
21147         // is ancestor a text element.
21148         var ac =  range.commonAncestorContainer;
21149         if (ac.nodeType == 3) {
21150             ac = ac.parentNode;
21151         }
21152         
21153         var ar = ac.childNodes;
21154          
21155         var nodes = [];
21156         var other_nodes = [];
21157         var has_other_nodes = false;
21158         for (var i=0;i<ar.length;i++) {
21159             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21160                 continue;
21161             }
21162             // fullly contained node.
21163             
21164             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21165                 nodes.push(ar[i]);
21166                 continue;
21167             }
21168             
21169             // probably selected..
21170             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21171                 other_nodes.push(ar[i]);
21172                 continue;
21173             }
21174             // outer..
21175             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21176                 continue;
21177             }
21178             
21179             
21180             has_other_nodes = true;
21181         }
21182         if (!nodes.length && other_nodes.length) {
21183             nodes= other_nodes;
21184         }
21185         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21186             return false;
21187         }
21188         
21189         return nodes[0];
21190     },
21191     createRange: function(sel)
21192     {
21193         // this has strange effects when using with 
21194         // top toolbar - not sure if it's a great idea.
21195         //this.editor.contentWindow.focus();
21196         if (typeof sel != "undefined") {
21197             try {
21198                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21199             } catch(e) {
21200                 return this.doc.createRange();
21201             }
21202         } else {
21203             return this.doc.createRange();
21204         }
21205     },
21206     getParentElement: function()
21207     {
21208         
21209         this.assignDocWin();
21210         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21211         
21212         var range = this.createRange(sel);
21213          
21214         try {
21215             var p = range.commonAncestorContainer;
21216             while (p.nodeType == 3) { // text node
21217                 p = p.parentNode;
21218             }
21219             return p;
21220         } catch (e) {
21221             return null;
21222         }
21223     
21224     },
21225     /***
21226      *
21227      * Range intersection.. the hard stuff...
21228      *  '-1' = before
21229      *  '0' = hits..
21230      *  '1' = after.
21231      *         [ -- selected range --- ]
21232      *   [fail]                        [fail]
21233      *
21234      *    basically..
21235      *      if end is before start or  hits it. fail.
21236      *      if start is after end or hits it fail.
21237      *
21238      *   if either hits (but other is outside. - then it's not 
21239      *   
21240      *    
21241      **/
21242     
21243     
21244     // @see http://www.thismuchiknow.co.uk/?p=64.
21245     rangeIntersectsNode : function(range, node)
21246     {
21247         var nodeRange = node.ownerDocument.createRange();
21248         try {
21249             nodeRange.selectNode(node);
21250         } catch (e) {
21251             nodeRange.selectNodeContents(node);
21252         }
21253     
21254         var rangeStartRange = range.cloneRange();
21255         rangeStartRange.collapse(true);
21256     
21257         var rangeEndRange = range.cloneRange();
21258         rangeEndRange.collapse(false);
21259     
21260         var nodeStartRange = nodeRange.cloneRange();
21261         nodeStartRange.collapse(true);
21262     
21263         var nodeEndRange = nodeRange.cloneRange();
21264         nodeEndRange.collapse(false);
21265     
21266         return rangeStartRange.compareBoundaryPoints(
21267                  Range.START_TO_START, nodeEndRange) == -1 &&
21268                rangeEndRange.compareBoundaryPoints(
21269                  Range.START_TO_START, nodeStartRange) == 1;
21270         
21271          
21272     },
21273     rangeCompareNode : function(range, node)
21274     {
21275         var nodeRange = node.ownerDocument.createRange();
21276         try {
21277             nodeRange.selectNode(node);
21278         } catch (e) {
21279             nodeRange.selectNodeContents(node);
21280         }
21281         
21282         
21283         range.collapse(true);
21284     
21285         nodeRange.collapse(true);
21286      
21287         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21288         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21289          
21290         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21291         
21292         var nodeIsBefore   =  ss == 1;
21293         var nodeIsAfter    = ee == -1;
21294         
21295         if (nodeIsBefore && nodeIsAfter) {
21296             return 0; // outer
21297         }
21298         if (!nodeIsBefore && nodeIsAfter) {
21299             return 1; //right trailed.
21300         }
21301         
21302         if (nodeIsBefore && !nodeIsAfter) {
21303             return 2;  // left trailed.
21304         }
21305         // fully contined.
21306         return 3;
21307     },
21308
21309     // private? - in a new class?
21310     cleanUpPaste :  function()
21311     {
21312         // cleans up the whole document..
21313         Roo.log('cleanuppaste');
21314         
21315         this.cleanUpChildren(this.doc.body);
21316         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21317         if (clean != this.doc.body.innerHTML) {
21318             this.doc.body.innerHTML = clean;
21319         }
21320         
21321     },
21322     
21323     cleanWordChars : function(input) {// change the chars to hex code
21324         var he = Roo.HtmlEditorCore;
21325         
21326         var output = input;
21327         Roo.each(he.swapCodes, function(sw) { 
21328             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21329             
21330             output = output.replace(swapper, sw[1]);
21331         });
21332         
21333         return output;
21334     },
21335     
21336     
21337     cleanUpChildren : function (n)
21338     {
21339         if (!n.childNodes.length) {
21340             return;
21341         }
21342         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21343            this.cleanUpChild(n.childNodes[i]);
21344         }
21345     },
21346     
21347     
21348         
21349     
21350     cleanUpChild : function (node)
21351     {
21352         var ed = this;
21353         //console.log(node);
21354         if (node.nodeName == "#text") {
21355             // clean up silly Windows -- stuff?
21356             return; 
21357         }
21358         if (node.nodeName == "#comment") {
21359             node.parentNode.removeChild(node);
21360             // clean up silly Windows -- stuff?
21361             return; 
21362         }
21363         var lcname = node.tagName.toLowerCase();
21364         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21365         // whitelist of tags..
21366         
21367         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21368             // remove node.
21369             node.parentNode.removeChild(node);
21370             return;
21371             
21372         }
21373         
21374         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21375         
21376         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21377         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21378         
21379         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21380         //    remove_keep_children = true;
21381         //}
21382         
21383         if (remove_keep_children) {
21384             this.cleanUpChildren(node);
21385             // inserts everything just before this node...
21386             while (node.childNodes.length) {
21387                 var cn = node.childNodes[0];
21388                 node.removeChild(cn);
21389                 node.parentNode.insertBefore(cn, node);
21390             }
21391             node.parentNode.removeChild(node);
21392             return;
21393         }
21394         
21395         if (!node.attributes || !node.attributes.length) {
21396             this.cleanUpChildren(node);
21397             return;
21398         }
21399         
21400         function cleanAttr(n,v)
21401         {
21402             
21403             if (v.match(/^\./) || v.match(/^\//)) {
21404                 return;
21405             }
21406             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21407                 return;
21408             }
21409             if (v.match(/^#/)) {
21410                 return;
21411             }
21412 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21413             node.removeAttribute(n);
21414             
21415         }
21416         
21417         var cwhite = this.cwhite;
21418         var cblack = this.cblack;
21419             
21420         function cleanStyle(n,v)
21421         {
21422             if (v.match(/expression/)) { //XSS?? should we even bother..
21423                 node.removeAttribute(n);
21424                 return;
21425             }
21426             
21427             var parts = v.split(/;/);
21428             var clean = [];
21429             
21430             Roo.each(parts, function(p) {
21431                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21432                 if (!p.length) {
21433                     return true;
21434                 }
21435                 var l = p.split(':').shift().replace(/\s+/g,'');
21436                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21437                 
21438                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21439 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21440                     //node.removeAttribute(n);
21441                     return true;
21442                 }
21443                 //Roo.log()
21444                 // only allow 'c whitelisted system attributes'
21445                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21446 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21447                     //node.removeAttribute(n);
21448                     return true;
21449                 }
21450                 
21451                 
21452                  
21453                 
21454                 clean.push(p);
21455                 return true;
21456             });
21457             if (clean.length) { 
21458                 node.setAttribute(n, clean.join(';'));
21459             } else {
21460                 node.removeAttribute(n);
21461             }
21462             
21463         }
21464         
21465         
21466         for (var i = node.attributes.length-1; i > -1 ; i--) {
21467             var a = node.attributes[i];
21468             //console.log(a);
21469             
21470             if (a.name.toLowerCase().substr(0,2)=='on')  {
21471                 node.removeAttribute(a.name);
21472                 continue;
21473             }
21474             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21475                 node.removeAttribute(a.name);
21476                 continue;
21477             }
21478             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21479                 cleanAttr(a.name,a.value); // fixme..
21480                 continue;
21481             }
21482             if (a.name == 'style') {
21483                 cleanStyle(a.name,a.value);
21484                 continue;
21485             }
21486             /// clean up MS crap..
21487             // tecnically this should be a list of valid class'es..
21488             
21489             
21490             if (a.name == 'class') {
21491                 if (a.value.match(/^Mso/)) {
21492                     node.className = '';
21493                 }
21494                 
21495                 if (a.value.match(/^body$/)) {
21496                     node.className = '';
21497                 }
21498                 continue;
21499             }
21500             
21501             // style cleanup!?
21502             // class cleanup?
21503             
21504         }
21505         
21506         
21507         this.cleanUpChildren(node);
21508         
21509         
21510     },
21511     
21512     /**
21513      * Clean up MS wordisms...
21514      */
21515     cleanWord : function(node)
21516     {
21517         
21518         
21519         if (!node) {
21520             this.cleanWord(this.doc.body);
21521             return;
21522         }
21523         if (node.nodeName == "#text") {
21524             // clean up silly Windows -- stuff?
21525             return; 
21526         }
21527         if (node.nodeName == "#comment") {
21528             node.parentNode.removeChild(node);
21529             // clean up silly Windows -- stuff?
21530             return; 
21531         }
21532         
21533         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21534             node.parentNode.removeChild(node);
21535             return;
21536         }
21537         
21538         // remove - but keep children..
21539         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21540             while (node.childNodes.length) {
21541                 var cn = node.childNodes[0];
21542                 node.removeChild(cn);
21543                 node.parentNode.insertBefore(cn, node);
21544             }
21545             node.parentNode.removeChild(node);
21546             this.iterateChildren(node, this.cleanWord);
21547             return;
21548         }
21549         // clean styles
21550         if (node.className.length) {
21551             
21552             var cn = node.className.split(/\W+/);
21553             var cna = [];
21554             Roo.each(cn, function(cls) {
21555                 if (cls.match(/Mso[a-zA-Z]+/)) {
21556                     return;
21557                 }
21558                 cna.push(cls);
21559             });
21560             node.className = cna.length ? cna.join(' ') : '';
21561             if (!cna.length) {
21562                 node.removeAttribute("class");
21563             }
21564         }
21565         
21566         if (node.hasAttribute("lang")) {
21567             node.removeAttribute("lang");
21568         }
21569         
21570         if (node.hasAttribute("style")) {
21571             
21572             var styles = node.getAttribute("style").split(";");
21573             var nstyle = [];
21574             Roo.each(styles, function(s) {
21575                 if (!s.match(/:/)) {
21576                     return;
21577                 }
21578                 var kv = s.split(":");
21579                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21580                     return;
21581                 }
21582                 // what ever is left... we allow.
21583                 nstyle.push(s);
21584             });
21585             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21586             if (!nstyle.length) {
21587                 node.removeAttribute('style');
21588             }
21589         }
21590         this.iterateChildren(node, this.cleanWord);
21591         
21592         
21593         
21594     },
21595     /**
21596      * iterateChildren of a Node, calling fn each time, using this as the scole..
21597      * @param {DomNode} node node to iterate children of.
21598      * @param {Function} fn method of this class to call on each item.
21599      */
21600     iterateChildren : function(node, fn)
21601     {
21602         if (!node.childNodes.length) {
21603                 return;
21604         }
21605         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21606            fn.call(this, node.childNodes[i])
21607         }
21608     },
21609     
21610     
21611     /**
21612      * cleanTableWidths.
21613      *
21614      * Quite often pasting from word etc.. results in tables with column and widths.
21615      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21616      *
21617      */
21618     cleanTableWidths : function(node)
21619     {
21620          
21621          
21622         if (!node) {
21623             this.cleanTableWidths(this.doc.body);
21624             return;
21625         }
21626         
21627         // ignore list...
21628         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21629             return; 
21630         }
21631         Roo.log(node.tagName);
21632         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21633             this.iterateChildren(node, this.cleanTableWidths);
21634             return;
21635         }
21636         if (node.hasAttribute('width')) {
21637             node.removeAttribute('width');
21638         }
21639         
21640          
21641         if (node.hasAttribute("style")) {
21642             // pretty basic...
21643             
21644             var styles = node.getAttribute("style").split(";");
21645             var nstyle = [];
21646             Roo.each(styles, function(s) {
21647                 if (!s.match(/:/)) {
21648                     return;
21649                 }
21650                 var kv = s.split(":");
21651                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21652                     return;
21653                 }
21654                 // what ever is left... we allow.
21655                 nstyle.push(s);
21656             });
21657             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21658             if (!nstyle.length) {
21659                 node.removeAttribute('style');
21660             }
21661         }
21662         
21663         this.iterateChildren(node, this.cleanTableWidths);
21664         
21665         
21666     },
21667     
21668     
21669     
21670     
21671     domToHTML : function(currentElement, depth, nopadtext) {
21672         
21673         depth = depth || 0;
21674         nopadtext = nopadtext || false;
21675     
21676         if (!currentElement) {
21677             return this.domToHTML(this.doc.body);
21678         }
21679         
21680         //Roo.log(currentElement);
21681         var j;
21682         var allText = false;
21683         var nodeName = currentElement.nodeName;
21684         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21685         
21686         if  (nodeName == '#text') {
21687             
21688             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21689         }
21690         
21691         
21692         var ret = '';
21693         if (nodeName != 'BODY') {
21694              
21695             var i = 0;
21696             // Prints the node tagName, such as <A>, <IMG>, etc
21697             if (tagName) {
21698                 var attr = [];
21699                 for(i = 0; i < currentElement.attributes.length;i++) {
21700                     // quoting?
21701                     var aname = currentElement.attributes.item(i).name;
21702                     if (!currentElement.attributes.item(i).value.length) {
21703                         continue;
21704                     }
21705                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21706                 }
21707                 
21708                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21709             } 
21710             else {
21711                 
21712                 // eack
21713             }
21714         } else {
21715             tagName = false;
21716         }
21717         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21718             return ret;
21719         }
21720         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21721             nopadtext = true;
21722         }
21723         
21724         
21725         // Traverse the tree
21726         i = 0;
21727         var currentElementChild = currentElement.childNodes.item(i);
21728         var allText = true;
21729         var innerHTML  = '';
21730         lastnode = '';
21731         while (currentElementChild) {
21732             // Formatting code (indent the tree so it looks nice on the screen)
21733             var nopad = nopadtext;
21734             if (lastnode == 'SPAN') {
21735                 nopad  = true;
21736             }
21737             // text
21738             if  (currentElementChild.nodeName == '#text') {
21739                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21740                 toadd = nopadtext ? toadd : toadd.trim();
21741                 if (!nopad && toadd.length > 80) {
21742                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21743                 }
21744                 innerHTML  += toadd;
21745                 
21746                 i++;
21747                 currentElementChild = currentElement.childNodes.item(i);
21748                 lastNode = '';
21749                 continue;
21750             }
21751             allText = false;
21752             
21753             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21754                 
21755             // Recursively traverse the tree structure of the child node
21756             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21757             lastnode = currentElementChild.nodeName;
21758             i++;
21759             currentElementChild=currentElement.childNodes.item(i);
21760         }
21761         
21762         ret += innerHTML;
21763         
21764         if (!allText) {
21765                 // The remaining code is mostly for formatting the tree
21766             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21767         }
21768         
21769         
21770         if (tagName) {
21771             ret+= "</"+tagName+">";
21772         }
21773         return ret;
21774         
21775     },
21776         
21777     applyBlacklists : function()
21778     {
21779         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21780         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21781         
21782         this.white = [];
21783         this.black = [];
21784         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21785             if (b.indexOf(tag) > -1) {
21786                 return;
21787             }
21788             this.white.push(tag);
21789             
21790         }, this);
21791         
21792         Roo.each(w, function(tag) {
21793             if (b.indexOf(tag) > -1) {
21794                 return;
21795             }
21796             if (this.white.indexOf(tag) > -1) {
21797                 return;
21798             }
21799             this.white.push(tag);
21800             
21801         }, this);
21802         
21803         
21804         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21805             if (w.indexOf(tag) > -1) {
21806                 return;
21807             }
21808             this.black.push(tag);
21809             
21810         }, this);
21811         
21812         Roo.each(b, function(tag) {
21813             if (w.indexOf(tag) > -1) {
21814                 return;
21815             }
21816             if (this.black.indexOf(tag) > -1) {
21817                 return;
21818             }
21819             this.black.push(tag);
21820             
21821         }, this);
21822         
21823         
21824         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21825         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21826         
21827         this.cwhite = [];
21828         this.cblack = [];
21829         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21830             if (b.indexOf(tag) > -1) {
21831                 return;
21832             }
21833             this.cwhite.push(tag);
21834             
21835         }, this);
21836         
21837         Roo.each(w, function(tag) {
21838             if (b.indexOf(tag) > -1) {
21839                 return;
21840             }
21841             if (this.cwhite.indexOf(tag) > -1) {
21842                 return;
21843             }
21844             this.cwhite.push(tag);
21845             
21846         }, this);
21847         
21848         
21849         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21850             if (w.indexOf(tag) > -1) {
21851                 return;
21852             }
21853             this.cblack.push(tag);
21854             
21855         }, this);
21856         
21857         Roo.each(b, function(tag) {
21858             if (w.indexOf(tag) > -1) {
21859                 return;
21860             }
21861             if (this.cblack.indexOf(tag) > -1) {
21862                 return;
21863             }
21864             this.cblack.push(tag);
21865             
21866         }, this);
21867     },
21868     
21869     setStylesheets : function(stylesheets)
21870     {
21871         if(typeof(stylesheets) == 'string'){
21872             Roo.get(this.iframe.contentDocument.head).createChild({
21873                 tag : 'link',
21874                 rel : 'stylesheet',
21875                 type : 'text/css',
21876                 href : stylesheets
21877             });
21878             
21879             return;
21880         }
21881         var _this = this;
21882      
21883         Roo.each(stylesheets, function(s) {
21884             if(!s.length){
21885                 return;
21886             }
21887             
21888             Roo.get(_this.iframe.contentDocument.head).createChild({
21889                 tag : 'link',
21890                 rel : 'stylesheet',
21891                 type : 'text/css',
21892                 href : s
21893             });
21894         });
21895
21896         
21897     },
21898     
21899     removeStylesheets : function()
21900     {
21901         var _this = this;
21902         
21903         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21904             s.remove();
21905         });
21906     },
21907     
21908     setStyle : function(style)
21909     {
21910         Roo.get(this.iframe.contentDocument.head).createChild({
21911             tag : 'style',
21912             type : 'text/css',
21913             html : style
21914         });
21915
21916         return;
21917     }
21918     
21919     // hide stuff that is not compatible
21920     /**
21921      * @event blur
21922      * @hide
21923      */
21924     /**
21925      * @event change
21926      * @hide
21927      */
21928     /**
21929      * @event focus
21930      * @hide
21931      */
21932     /**
21933      * @event specialkey
21934      * @hide
21935      */
21936     /**
21937      * @cfg {String} fieldClass @hide
21938      */
21939     /**
21940      * @cfg {String} focusClass @hide
21941      */
21942     /**
21943      * @cfg {String} autoCreate @hide
21944      */
21945     /**
21946      * @cfg {String} inputType @hide
21947      */
21948     /**
21949      * @cfg {String} invalidClass @hide
21950      */
21951     /**
21952      * @cfg {String} invalidText @hide
21953      */
21954     /**
21955      * @cfg {String} msgFx @hide
21956      */
21957     /**
21958      * @cfg {String} validateOnBlur @hide
21959      */
21960 });
21961
21962 Roo.HtmlEditorCore.white = [
21963         'area', 'br', 'img', 'input', 'hr', 'wbr',
21964         
21965        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21966        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21967        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21968        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21969        'table',   'ul',         'xmp', 
21970        
21971        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21972       'thead',   'tr', 
21973      
21974       'dir', 'menu', 'ol', 'ul', 'dl',
21975        
21976       'embed',  'object'
21977 ];
21978
21979
21980 Roo.HtmlEditorCore.black = [
21981     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21982         'applet', // 
21983         'base',   'basefont', 'bgsound', 'blink',  'body', 
21984         'frame',  'frameset', 'head',    'html',   'ilayer', 
21985         'iframe', 'layer',  'link',     'meta',    'object',   
21986         'script', 'style' ,'title',  'xml' // clean later..
21987 ];
21988 Roo.HtmlEditorCore.clean = [
21989     'script', 'style', 'title', 'xml'
21990 ];
21991 Roo.HtmlEditorCore.remove = [
21992     'font'
21993 ];
21994 // attributes..
21995
21996 Roo.HtmlEditorCore.ablack = [
21997     'on'
21998 ];
21999     
22000 Roo.HtmlEditorCore.aclean = [ 
22001     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22002 ];
22003
22004 // protocols..
22005 Roo.HtmlEditorCore.pwhite= [
22006         'http',  'https',  'mailto'
22007 ];
22008
22009 // white listed style attributes.
22010 Roo.HtmlEditorCore.cwhite= [
22011       //  'text-align', /// default is to allow most things..
22012       
22013          
22014 //        'font-size'//??
22015 ];
22016
22017 // black listed style attributes.
22018 Roo.HtmlEditorCore.cblack= [
22019       //  'font-size' -- this can be set by the project 
22020 ];
22021
22022
22023 Roo.HtmlEditorCore.swapCodes   =[ 
22024     [    8211, "--" ], 
22025     [    8212, "--" ], 
22026     [    8216,  "'" ],  
22027     [    8217, "'" ],  
22028     [    8220, '"' ],  
22029     [    8221, '"' ],  
22030     [    8226, "*" ],  
22031     [    8230, "..." ]
22032 ]; 
22033
22034     //<script type="text/javascript">
22035
22036 /*
22037  * Ext JS Library 1.1.1
22038  * Copyright(c) 2006-2007, Ext JS, LLC.
22039  * Licence LGPL
22040  * 
22041  */
22042  
22043  
22044 Roo.form.HtmlEditor = function(config){
22045     
22046     
22047     
22048     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22049     
22050     if (!this.toolbars) {
22051         this.toolbars = [];
22052     }
22053     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22054     
22055     
22056 };
22057
22058 /**
22059  * @class Roo.form.HtmlEditor
22060  * @extends Roo.form.Field
22061  * Provides a lightweight HTML Editor component.
22062  *
22063  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22064  * 
22065  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22066  * supported by this editor.</b><br/><br/>
22067  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22068  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22069  */
22070 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22071     /**
22072      * @cfg {Boolean} clearUp
22073      */
22074     clearUp : true,
22075       /**
22076      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22077      */
22078     toolbars : false,
22079    
22080      /**
22081      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22082      *                        Roo.resizable.
22083      */
22084     resizable : false,
22085      /**
22086      * @cfg {Number} height (in pixels)
22087      */   
22088     height: 300,
22089    /**
22090      * @cfg {Number} width (in pixels)
22091      */   
22092     width: 500,
22093     
22094     /**
22095      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22096      * 
22097      */
22098     stylesheets: false,
22099     
22100     
22101      /**
22102      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22103      * 
22104      */
22105     cblack: false,
22106     /**
22107      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22108      * 
22109      */
22110     cwhite: false,
22111     
22112      /**
22113      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22114      * 
22115      */
22116     black: false,
22117     /**
22118      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22119      * 
22120      */
22121     white: false,
22122     
22123     // id of frame..
22124     frameId: false,
22125     
22126     // private properties
22127     validationEvent : false,
22128     deferHeight: true,
22129     initialized : false,
22130     activated : false,
22131     
22132     onFocus : Roo.emptyFn,
22133     iframePad:3,
22134     hideMode:'offsets',
22135     
22136     actionMode : 'container', // defaults to hiding it...
22137     
22138     defaultAutoCreate : { // modified by initCompnoent..
22139         tag: "textarea",
22140         style:"width:500px;height:300px;",
22141         autocomplete: "new-password"
22142     },
22143
22144     // private
22145     initComponent : function(){
22146         this.addEvents({
22147             /**
22148              * @event initialize
22149              * Fires when the editor is fully initialized (including the iframe)
22150              * @param {HtmlEditor} this
22151              */
22152             initialize: true,
22153             /**
22154              * @event activate
22155              * Fires when the editor is first receives the focus. Any insertion must wait
22156              * until after this event.
22157              * @param {HtmlEditor} this
22158              */
22159             activate: true,
22160              /**
22161              * @event beforesync
22162              * Fires before the textarea is updated with content from the editor iframe. Return false
22163              * to cancel the sync.
22164              * @param {HtmlEditor} this
22165              * @param {String} html
22166              */
22167             beforesync: true,
22168              /**
22169              * @event beforepush
22170              * Fires before the iframe editor is updated with content from the textarea. Return false
22171              * to cancel the push.
22172              * @param {HtmlEditor} this
22173              * @param {String} html
22174              */
22175             beforepush: true,
22176              /**
22177              * @event sync
22178              * Fires when the textarea is updated with content from the editor iframe.
22179              * @param {HtmlEditor} this
22180              * @param {String} html
22181              */
22182             sync: true,
22183              /**
22184              * @event push
22185              * Fires when the iframe editor is updated with content from the textarea.
22186              * @param {HtmlEditor} this
22187              * @param {String} html
22188              */
22189             push: true,
22190              /**
22191              * @event editmodechange
22192              * Fires when the editor switches edit modes
22193              * @param {HtmlEditor} this
22194              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22195              */
22196             editmodechange: true,
22197             /**
22198              * @event editorevent
22199              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22200              * @param {HtmlEditor} this
22201              */
22202             editorevent: true,
22203             /**
22204              * @event firstfocus
22205              * Fires when on first focus - needed by toolbars..
22206              * @param {HtmlEditor} this
22207              */
22208             firstfocus: true,
22209             /**
22210              * @event autosave
22211              * Auto save the htmlEditor value as a file into Events
22212              * @param {HtmlEditor} this
22213              */
22214             autosave: true,
22215             /**
22216              * @event savedpreview
22217              * preview the saved version of htmlEditor
22218              * @param {HtmlEditor} this
22219              */
22220             savedpreview: true,
22221             
22222             /**
22223             * @event stylesheetsclick
22224             * Fires when press the Sytlesheets button
22225             * @param {Roo.HtmlEditorCore} this
22226             */
22227             stylesheetsclick: true
22228         });
22229         this.defaultAutoCreate =  {
22230             tag: "textarea",
22231             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22232             autocomplete: "new-password"
22233         };
22234     },
22235
22236     /**
22237      * Protected method that will not generally be called directly. It
22238      * is called when the editor creates its toolbar. Override this method if you need to
22239      * add custom toolbar buttons.
22240      * @param {HtmlEditor} editor
22241      */
22242     createToolbar : function(editor){
22243         Roo.log("create toolbars");
22244         if (!editor.toolbars || !editor.toolbars.length) {
22245             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22246         }
22247         
22248         for (var i =0 ; i < editor.toolbars.length;i++) {
22249             editor.toolbars[i] = Roo.factory(
22250                     typeof(editor.toolbars[i]) == 'string' ?
22251                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22252                 Roo.form.HtmlEditor);
22253             editor.toolbars[i].init(editor);
22254         }
22255          
22256         
22257     },
22258
22259      
22260     // private
22261     onRender : function(ct, position)
22262     {
22263         var _t = this;
22264         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22265         
22266         this.wrap = this.el.wrap({
22267             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22268         });
22269         
22270         this.editorcore.onRender(ct, position);
22271          
22272         if (this.resizable) {
22273             this.resizeEl = new Roo.Resizable(this.wrap, {
22274                 pinned : true,
22275                 wrap: true,
22276                 dynamic : true,
22277                 minHeight : this.height,
22278                 height: this.height,
22279                 handles : this.resizable,
22280                 width: this.width,
22281                 listeners : {
22282                     resize : function(r, w, h) {
22283                         _t.onResize(w,h); // -something
22284                     }
22285                 }
22286             });
22287             
22288         }
22289         this.createToolbar(this);
22290        
22291         
22292         if(!this.width){
22293             this.setSize(this.wrap.getSize());
22294         }
22295         if (this.resizeEl) {
22296             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22297             // should trigger onReize..
22298         }
22299         
22300         this.keyNav = new Roo.KeyNav(this.el, {
22301             
22302             "tab" : function(e){
22303                 e.preventDefault();
22304                 
22305                 var value = this.getValue();
22306                 
22307                 var start = this.el.dom.selectionStart;
22308                 var end = this.el.dom.selectionEnd;
22309                 
22310                 if(!e.shiftKey){
22311                     
22312                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22313                     this.el.dom.setSelectionRange(end + 1, end + 1);
22314                     return;
22315                 }
22316                 
22317                 var f = value.substring(0, start).split("\t");
22318                 
22319                 if(f.pop().length != 0){
22320                     return;
22321                 }
22322                 
22323                 this.setValue(f.join("\t") + value.substring(end));
22324                 this.el.dom.setSelectionRange(start - 1, start - 1);
22325                 
22326             },
22327             
22328             "home" : function(e){
22329                 e.preventDefault();
22330                 
22331                 var curr = this.el.dom.selectionStart;
22332                 var lines = this.getValue().split("\n");
22333                 
22334                 if(!lines.length){
22335                     return;
22336                 }
22337                 
22338                 if(e.ctrlKey){
22339                     this.el.dom.setSelectionRange(0, 0);
22340                     return;
22341                 }
22342                 
22343                 var pos = 0;
22344                 
22345                 for (var i = 0; i < lines.length;i++) {
22346                     pos += lines[i].length;
22347                     
22348                     if(i != 0){
22349                         pos += 1;
22350                     }
22351                     
22352                     if(pos < curr){
22353                         continue;
22354                     }
22355                     
22356                     pos -= lines[i].length;
22357                     
22358                     break;
22359                 }
22360                 
22361                 if(!e.shiftKey){
22362                     this.el.dom.setSelectionRange(pos, pos);
22363                     return;
22364                 }
22365                 
22366                 this.el.dom.selectionStart = pos;
22367                 this.el.dom.selectionEnd = curr;
22368             },
22369             
22370             "end" : function(e){
22371                 e.preventDefault();
22372                 
22373                 var curr = this.el.dom.selectionStart;
22374                 var lines = this.getValue().split("\n");
22375                 
22376                 if(!lines.length){
22377                     return;
22378                 }
22379                 
22380                 if(e.ctrlKey){
22381                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22382                     return;
22383                 }
22384                 
22385                 var pos = 0;
22386                 
22387                 for (var i = 0; i < lines.length;i++) {
22388                     
22389                     pos += lines[i].length;
22390                     
22391                     if(i != 0){
22392                         pos += 1;
22393                     }
22394                     
22395                     if(pos < curr){
22396                         continue;
22397                     }
22398                     
22399                     break;
22400                 }
22401                 
22402                 if(!e.shiftKey){
22403                     this.el.dom.setSelectionRange(pos, pos);
22404                     return;
22405                 }
22406                 
22407                 this.el.dom.selectionStart = curr;
22408                 this.el.dom.selectionEnd = pos;
22409             },
22410
22411             scope : this,
22412
22413             doRelay : function(foo, bar, hname){
22414                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22415             },
22416
22417             forceKeyDown: true
22418         });
22419         
22420 //        if(this.autosave && this.w){
22421 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22422 //        }
22423     },
22424
22425     // private
22426     onResize : function(w, h)
22427     {
22428         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22429         var ew = false;
22430         var eh = false;
22431         
22432         if(this.el ){
22433             if(typeof w == 'number'){
22434                 var aw = w - this.wrap.getFrameWidth('lr');
22435                 this.el.setWidth(this.adjustWidth('textarea', aw));
22436                 ew = aw;
22437             }
22438             if(typeof h == 'number'){
22439                 var tbh = 0;
22440                 for (var i =0; i < this.toolbars.length;i++) {
22441                     // fixme - ask toolbars for heights?
22442                     tbh += this.toolbars[i].tb.el.getHeight();
22443                     if (this.toolbars[i].footer) {
22444                         tbh += this.toolbars[i].footer.el.getHeight();
22445                     }
22446                 }
22447                 
22448                 
22449                 
22450                 
22451                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22452                 ah -= 5; // knock a few pixes off for look..
22453 //                Roo.log(ah);
22454                 this.el.setHeight(this.adjustWidth('textarea', ah));
22455                 var eh = ah;
22456             }
22457         }
22458         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22459         this.editorcore.onResize(ew,eh);
22460         
22461     },
22462
22463     /**
22464      * Toggles the editor between standard and source edit mode.
22465      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22466      */
22467     toggleSourceEdit : function(sourceEditMode)
22468     {
22469         this.editorcore.toggleSourceEdit(sourceEditMode);
22470         
22471         if(this.editorcore.sourceEditMode){
22472             Roo.log('editor - showing textarea');
22473             
22474 //            Roo.log('in');
22475 //            Roo.log(this.syncValue());
22476             this.editorcore.syncValue();
22477             this.el.removeClass('x-hidden');
22478             this.el.dom.removeAttribute('tabIndex');
22479             this.el.focus();
22480             
22481             for (var i = 0; i < this.toolbars.length; i++) {
22482                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22483                     this.toolbars[i].tb.hide();
22484                     this.toolbars[i].footer.hide();
22485                 }
22486             }
22487             
22488         }else{
22489             Roo.log('editor - hiding textarea');
22490 //            Roo.log('out')
22491 //            Roo.log(this.pushValue()); 
22492             this.editorcore.pushValue();
22493             
22494             this.el.addClass('x-hidden');
22495             this.el.dom.setAttribute('tabIndex', -1);
22496             
22497             for (var i = 0; i < this.toolbars.length; i++) {
22498                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22499                     this.toolbars[i].tb.show();
22500                     this.toolbars[i].footer.show();
22501                 }
22502             }
22503             
22504             //this.deferFocus();
22505         }
22506         
22507         this.setSize(this.wrap.getSize());
22508         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22509         
22510         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22511     },
22512  
22513     // private (for BoxComponent)
22514     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22515
22516     // private (for BoxComponent)
22517     getResizeEl : function(){
22518         return this.wrap;
22519     },
22520
22521     // private (for BoxComponent)
22522     getPositionEl : function(){
22523         return this.wrap;
22524     },
22525
22526     // private
22527     initEvents : function(){
22528         this.originalValue = this.getValue();
22529     },
22530
22531     /**
22532      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22533      * @method
22534      */
22535     markInvalid : Roo.emptyFn,
22536     /**
22537      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22538      * @method
22539      */
22540     clearInvalid : Roo.emptyFn,
22541
22542     setValue : function(v){
22543         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22544         this.editorcore.pushValue();
22545     },
22546
22547      
22548     // private
22549     deferFocus : function(){
22550         this.focus.defer(10, this);
22551     },
22552
22553     // doc'ed in Field
22554     focus : function(){
22555         this.editorcore.focus();
22556         
22557     },
22558       
22559
22560     // private
22561     onDestroy : function(){
22562         
22563         
22564         
22565         if(this.rendered){
22566             
22567             for (var i =0; i < this.toolbars.length;i++) {
22568                 // fixme - ask toolbars for heights?
22569                 this.toolbars[i].onDestroy();
22570             }
22571             
22572             this.wrap.dom.innerHTML = '';
22573             this.wrap.remove();
22574         }
22575     },
22576
22577     // private
22578     onFirstFocus : function(){
22579         //Roo.log("onFirstFocus");
22580         this.editorcore.onFirstFocus();
22581          for (var i =0; i < this.toolbars.length;i++) {
22582             this.toolbars[i].onFirstFocus();
22583         }
22584         
22585     },
22586     
22587     // private
22588     syncValue : function()
22589     {
22590         this.editorcore.syncValue();
22591     },
22592     
22593     pushValue : function()
22594     {
22595         this.editorcore.pushValue();
22596     },
22597     
22598     setStylesheets : function(stylesheets)
22599     {
22600         this.editorcore.setStylesheets(stylesheets);
22601     },
22602     
22603     removeStylesheets : function()
22604     {
22605         this.editorcore.removeStylesheets();
22606     }
22607      
22608     
22609     // hide stuff that is not compatible
22610     /**
22611      * @event blur
22612      * @hide
22613      */
22614     /**
22615      * @event change
22616      * @hide
22617      */
22618     /**
22619      * @event focus
22620      * @hide
22621      */
22622     /**
22623      * @event specialkey
22624      * @hide
22625      */
22626     /**
22627      * @cfg {String} fieldClass @hide
22628      */
22629     /**
22630      * @cfg {String} focusClass @hide
22631      */
22632     /**
22633      * @cfg {String} autoCreate @hide
22634      */
22635     /**
22636      * @cfg {String} inputType @hide
22637      */
22638     /**
22639      * @cfg {String} invalidClass @hide
22640      */
22641     /**
22642      * @cfg {String} invalidText @hide
22643      */
22644     /**
22645      * @cfg {String} msgFx @hide
22646      */
22647     /**
22648      * @cfg {String} validateOnBlur @hide
22649      */
22650 });
22651  
22652     // <script type="text/javascript">
22653 /*
22654  * Based on
22655  * Ext JS Library 1.1.1
22656  * Copyright(c) 2006-2007, Ext JS, LLC.
22657  *  
22658  
22659  */
22660
22661 /**
22662  * @class Roo.form.HtmlEditorToolbar1
22663  * Basic Toolbar
22664  * 
22665  * Usage:
22666  *
22667  new Roo.form.HtmlEditor({
22668     ....
22669     toolbars : [
22670         new Roo.form.HtmlEditorToolbar1({
22671             disable : { fonts: 1 , format: 1, ..., ... , ...],
22672             btns : [ .... ]
22673         })
22674     }
22675      
22676  * 
22677  * @cfg {Object} disable List of elements to disable..
22678  * @cfg {Array} btns List of additional buttons.
22679  * 
22680  * 
22681  * NEEDS Extra CSS? 
22682  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22683  */
22684  
22685 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22686 {
22687     
22688     Roo.apply(this, config);
22689     
22690     // default disabled, based on 'good practice'..
22691     this.disable = this.disable || {};
22692     Roo.applyIf(this.disable, {
22693         fontSize : true,
22694         colors : true,
22695         specialElements : true
22696     });
22697     
22698     
22699     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22700     // dont call parent... till later.
22701 }
22702
22703 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22704     
22705     tb: false,
22706     
22707     rendered: false,
22708     
22709     editor : false,
22710     editorcore : false,
22711     /**
22712      * @cfg {Object} disable  List of toolbar elements to disable
22713          
22714      */
22715     disable : false,
22716     
22717     
22718      /**
22719      * @cfg {String} createLinkText The default text for the create link prompt
22720      */
22721     createLinkText : 'Please enter the URL for the link:',
22722     /**
22723      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22724      */
22725     defaultLinkValue : 'http:/'+'/',
22726    
22727     
22728       /**
22729      * @cfg {Array} fontFamilies An array of available font families
22730      */
22731     fontFamilies : [
22732         'Arial',
22733         'Courier New',
22734         'Tahoma',
22735         'Times New Roman',
22736         'Verdana'
22737     ],
22738     
22739     specialChars : [
22740            "&#169;",
22741           "&#174;",     
22742           "&#8482;",    
22743           "&#163;" ,    
22744          // "&#8212;",    
22745           "&#8230;",    
22746           "&#247;" ,    
22747         //  "&#225;" ,     ?? a acute?
22748            "&#8364;"    , //Euro
22749        //   "&#8220;"    ,
22750         //  "&#8221;"    ,
22751         //  "&#8226;"    ,
22752           "&#176;"  //   , // degrees
22753
22754          // "&#233;"     , // e ecute
22755          // "&#250;"     , // u ecute?
22756     ],
22757     
22758     specialElements : [
22759         {
22760             text: "Insert Table",
22761             xtype: 'MenuItem',
22762             xns : Roo.Menu,
22763             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22764                 
22765         },
22766         {    
22767             text: "Insert Image",
22768             xtype: 'MenuItem',
22769             xns : Roo.Menu,
22770             ihtml : '<img src="about:blank"/>'
22771             
22772         }
22773         
22774          
22775     ],
22776     
22777     
22778     inputElements : [ 
22779             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22780             "input:submit", "input:button", "select", "textarea", "label" ],
22781     formats : [
22782         ["p"] ,  
22783         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22784         ["pre"],[ "code"], 
22785         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22786         ['div'],['span']
22787     ],
22788     
22789     cleanStyles : [
22790         "font-size"
22791     ],
22792      /**
22793      * @cfg {String} defaultFont default font to use.
22794      */
22795     defaultFont: 'tahoma',
22796    
22797     fontSelect : false,
22798     
22799     
22800     formatCombo : false,
22801     
22802     init : function(editor)
22803     {
22804         this.editor = editor;
22805         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22806         var editorcore = this.editorcore;
22807         
22808         var _t = this;
22809         
22810         var fid = editorcore.frameId;
22811         var etb = this;
22812         function btn(id, toggle, handler){
22813             var xid = fid + '-'+ id ;
22814             return {
22815                 id : xid,
22816                 cmd : id,
22817                 cls : 'x-btn-icon x-edit-'+id,
22818                 enableToggle:toggle !== false,
22819                 scope: _t, // was editor...
22820                 handler:handler||_t.relayBtnCmd,
22821                 clickEvent:'mousedown',
22822                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22823                 tabIndex:-1
22824             };
22825         }
22826         
22827         
22828         
22829         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22830         this.tb = tb;
22831          // stop form submits
22832         tb.el.on('click', function(e){
22833             e.preventDefault(); // what does this do?
22834         });
22835
22836         if(!this.disable.font) { // && !Roo.isSafari){
22837             /* why no safari for fonts 
22838             editor.fontSelect = tb.el.createChild({
22839                 tag:'select',
22840                 tabIndex: -1,
22841                 cls:'x-font-select',
22842                 html: this.createFontOptions()
22843             });
22844             
22845             editor.fontSelect.on('change', function(){
22846                 var font = editor.fontSelect.dom.value;
22847                 editor.relayCmd('fontname', font);
22848                 editor.deferFocus();
22849             }, editor);
22850             
22851             tb.add(
22852                 editor.fontSelect.dom,
22853                 '-'
22854             );
22855             */
22856             
22857         };
22858         if(!this.disable.formats){
22859             this.formatCombo = new Roo.form.ComboBox({
22860                 store: new Roo.data.SimpleStore({
22861                     id : 'tag',
22862                     fields: ['tag'],
22863                     data : this.formats // from states.js
22864                 }),
22865                 blockFocus : true,
22866                 name : '',
22867                 //autoCreate : {tag: "div",  size: "20"},
22868                 displayField:'tag',
22869                 typeAhead: false,
22870                 mode: 'local',
22871                 editable : false,
22872                 triggerAction: 'all',
22873                 emptyText:'Add tag',
22874                 selectOnFocus:true,
22875                 width:135,
22876                 listeners : {
22877                     'select': function(c, r, i) {
22878                         editorcore.insertTag(r.get('tag'));
22879                         editor.focus();
22880                     }
22881                 }
22882
22883             });
22884             tb.addField(this.formatCombo);
22885             
22886         }
22887         
22888         if(!this.disable.format){
22889             tb.add(
22890                 btn('bold'),
22891                 btn('italic'),
22892                 btn('underline'),
22893                 btn('strikethrough')
22894             );
22895         };
22896         if(!this.disable.fontSize){
22897             tb.add(
22898                 '-',
22899                 
22900                 
22901                 btn('increasefontsize', false, editorcore.adjustFont),
22902                 btn('decreasefontsize', false, editorcore.adjustFont)
22903             );
22904         };
22905         
22906         
22907         if(!this.disable.colors){
22908             tb.add(
22909                 '-', {
22910                     id:editorcore.frameId +'-forecolor',
22911                     cls:'x-btn-icon x-edit-forecolor',
22912                     clickEvent:'mousedown',
22913                     tooltip: this.buttonTips['forecolor'] || undefined,
22914                     tabIndex:-1,
22915                     menu : new Roo.menu.ColorMenu({
22916                         allowReselect: true,
22917                         focus: Roo.emptyFn,
22918                         value:'000000',
22919                         plain:true,
22920                         selectHandler: function(cp, color){
22921                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22922                             editor.deferFocus();
22923                         },
22924                         scope: editorcore,
22925                         clickEvent:'mousedown'
22926                     })
22927                 }, {
22928                     id:editorcore.frameId +'backcolor',
22929                     cls:'x-btn-icon x-edit-backcolor',
22930                     clickEvent:'mousedown',
22931                     tooltip: this.buttonTips['backcolor'] || undefined,
22932                     tabIndex:-1,
22933                     menu : new Roo.menu.ColorMenu({
22934                         focus: Roo.emptyFn,
22935                         value:'FFFFFF',
22936                         plain:true,
22937                         allowReselect: true,
22938                         selectHandler: function(cp, color){
22939                             if(Roo.isGecko){
22940                                 editorcore.execCmd('useCSS', false);
22941                                 editorcore.execCmd('hilitecolor', color);
22942                                 editorcore.execCmd('useCSS', true);
22943                                 editor.deferFocus();
22944                             }else{
22945                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22946                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22947                                 editor.deferFocus();
22948                             }
22949                         },
22950                         scope:editorcore,
22951                         clickEvent:'mousedown'
22952                     })
22953                 }
22954             );
22955         };
22956         // now add all the items...
22957         
22958
22959         if(!this.disable.alignments){
22960             tb.add(
22961                 '-',
22962                 btn('justifyleft'),
22963                 btn('justifycenter'),
22964                 btn('justifyright')
22965             );
22966         };
22967
22968         //if(!Roo.isSafari){
22969             if(!this.disable.links){
22970                 tb.add(
22971                     '-',
22972                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22973                 );
22974             };
22975
22976             if(!this.disable.lists){
22977                 tb.add(
22978                     '-',
22979                     btn('insertorderedlist'),
22980                     btn('insertunorderedlist')
22981                 );
22982             }
22983             if(!this.disable.sourceEdit){
22984                 tb.add(
22985                     '-',
22986                     btn('sourceedit', true, function(btn){
22987                         this.toggleSourceEdit(btn.pressed);
22988                     })
22989                 );
22990             }
22991         //}
22992         
22993         var smenu = { };
22994         // special menu.. - needs to be tidied up..
22995         if (!this.disable.special) {
22996             smenu = {
22997                 text: "&#169;",
22998                 cls: 'x-edit-none',
22999                 
23000                 menu : {
23001                     items : []
23002                 }
23003             };
23004             for (var i =0; i < this.specialChars.length; i++) {
23005                 smenu.menu.items.push({
23006                     
23007                     html: this.specialChars[i],
23008                     handler: function(a,b) {
23009                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23010                         //editor.insertAtCursor(a.html);
23011                         
23012                     },
23013                     tabIndex:-1
23014                 });
23015             }
23016             
23017             
23018             tb.add(smenu);
23019             
23020             
23021         }
23022         
23023         var cmenu = { };
23024         if (!this.disable.cleanStyles) {
23025             cmenu = {
23026                 cls: 'x-btn-icon x-btn-clear',
23027                 
23028                 menu : {
23029                     items : []
23030                 }
23031             };
23032             for (var i =0; i < this.cleanStyles.length; i++) {
23033                 cmenu.menu.items.push({
23034                     actiontype : this.cleanStyles[i],
23035                     html: 'Remove ' + this.cleanStyles[i],
23036                     handler: function(a,b) {
23037 //                        Roo.log(a);
23038 //                        Roo.log(b);
23039                         var c = Roo.get(editorcore.doc.body);
23040                         c.select('[style]').each(function(s) {
23041                             s.dom.style.removeProperty(a.actiontype);
23042                         });
23043                         editorcore.syncValue();
23044                     },
23045                     tabIndex:-1
23046                 });
23047             }
23048              cmenu.menu.items.push({
23049                 actiontype : 'tablewidths',
23050                 html: 'Remove Table Widths',
23051                 handler: function(a,b) {
23052                     editorcore.cleanTableWidths();
23053                     editorcore.syncValue();
23054                 },
23055                 tabIndex:-1
23056             });
23057             cmenu.menu.items.push({
23058                 actiontype : 'word',
23059                 html: 'Remove MS Word Formating',
23060                 handler: function(a,b) {
23061                     editorcore.cleanWord();
23062                     editorcore.syncValue();
23063                 },
23064                 tabIndex:-1
23065             });
23066             
23067             cmenu.menu.items.push({
23068                 actiontype : 'all',
23069                 html: 'Remove All Styles',
23070                 handler: function(a,b) {
23071                     
23072                     var c = Roo.get(editorcore.doc.body);
23073                     c.select('[style]').each(function(s) {
23074                         s.dom.removeAttribute('style');
23075                     });
23076                     editorcore.syncValue();
23077                 },
23078                 tabIndex:-1
23079             });
23080             
23081             cmenu.menu.items.push({
23082                 actiontype : 'all',
23083                 html: 'Remove All CSS Classes',
23084                 handler: function(a,b) {
23085                     
23086                     var c = Roo.get(editorcore.doc.body);
23087                     c.select('[class]').each(function(s) {
23088                         s.dom.className = '';
23089                     });
23090                     editorcore.syncValue();
23091                 },
23092                 tabIndex:-1
23093             });
23094             
23095              cmenu.menu.items.push({
23096                 actiontype : 'tidy',
23097                 html: 'Tidy HTML Source',
23098                 handler: function(a,b) {
23099                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23100                     editorcore.syncValue();
23101                 },
23102                 tabIndex:-1
23103             });
23104             
23105             
23106             tb.add(cmenu);
23107         }
23108          
23109         if (!this.disable.specialElements) {
23110             var semenu = {
23111                 text: "Other;",
23112                 cls: 'x-edit-none',
23113                 menu : {
23114                     items : []
23115                 }
23116             };
23117             for (var i =0; i < this.specialElements.length; i++) {
23118                 semenu.menu.items.push(
23119                     Roo.apply({ 
23120                         handler: function(a,b) {
23121                             editor.insertAtCursor(this.ihtml);
23122                         }
23123                     }, this.specialElements[i])
23124                 );
23125                     
23126             }
23127             
23128             tb.add(semenu);
23129             
23130             
23131         }
23132          
23133         
23134         if (this.btns) {
23135             for(var i =0; i< this.btns.length;i++) {
23136                 var b = Roo.factory(this.btns[i],Roo.form);
23137                 b.cls =  'x-edit-none';
23138                 
23139                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23140                     b.cls += ' x-init-enable';
23141                 }
23142                 
23143                 b.scope = editorcore;
23144                 tb.add(b);
23145             }
23146         
23147         }
23148         
23149         
23150         
23151         // disable everything...
23152         
23153         this.tb.items.each(function(item){
23154             
23155            if(
23156                 item.id != editorcore.frameId+ '-sourceedit' && 
23157                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23158             ){
23159                 
23160                 item.disable();
23161             }
23162         });
23163         this.rendered = true;
23164         
23165         // the all the btns;
23166         editor.on('editorevent', this.updateToolbar, this);
23167         // other toolbars need to implement this..
23168         //editor.on('editmodechange', this.updateToolbar, this);
23169     },
23170     
23171     
23172     relayBtnCmd : function(btn) {
23173         this.editorcore.relayCmd(btn.cmd);
23174     },
23175     // private used internally
23176     createLink : function(){
23177         Roo.log("create link?");
23178         var url = prompt(this.createLinkText, this.defaultLinkValue);
23179         if(url && url != 'http:/'+'/'){
23180             this.editorcore.relayCmd('createlink', url);
23181         }
23182     },
23183
23184     
23185     /**
23186      * Protected method that will not generally be called directly. It triggers
23187      * a toolbar update by reading the markup state of the current selection in the editor.
23188      */
23189     updateToolbar: function(){
23190
23191         if(!this.editorcore.activated){
23192             this.editor.onFirstFocus();
23193             return;
23194         }
23195
23196         var btns = this.tb.items.map, 
23197             doc = this.editorcore.doc,
23198             frameId = this.editorcore.frameId;
23199
23200         if(!this.disable.font && !Roo.isSafari){
23201             /*
23202             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23203             if(name != this.fontSelect.dom.value){
23204                 this.fontSelect.dom.value = name;
23205             }
23206             */
23207         }
23208         if(!this.disable.format){
23209             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23210             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23211             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23212             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23213         }
23214         if(!this.disable.alignments){
23215             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23216             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23217             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23218         }
23219         if(!Roo.isSafari && !this.disable.lists){
23220             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23221             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23222         }
23223         
23224         var ans = this.editorcore.getAllAncestors();
23225         if (this.formatCombo) {
23226             
23227             
23228             var store = this.formatCombo.store;
23229             this.formatCombo.setValue("");
23230             for (var i =0; i < ans.length;i++) {
23231                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23232                     // select it..
23233                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23234                     break;
23235                 }
23236             }
23237         }
23238         
23239         
23240         
23241         // hides menus... - so this cant be on a menu...
23242         Roo.menu.MenuMgr.hideAll();
23243
23244         //this.editorsyncValue();
23245     },
23246    
23247     
23248     createFontOptions : function(){
23249         var buf = [], fs = this.fontFamilies, ff, lc;
23250         
23251         
23252         
23253         for(var i = 0, len = fs.length; i< len; i++){
23254             ff = fs[i];
23255             lc = ff.toLowerCase();
23256             buf.push(
23257                 '<option value="',lc,'" style="font-family:',ff,';"',
23258                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23259                     ff,
23260                 '</option>'
23261             );
23262         }
23263         return buf.join('');
23264     },
23265     
23266     toggleSourceEdit : function(sourceEditMode){
23267         
23268         Roo.log("toolbar toogle");
23269         if(sourceEditMode === undefined){
23270             sourceEditMode = !this.sourceEditMode;
23271         }
23272         this.sourceEditMode = sourceEditMode === true;
23273         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23274         // just toggle the button?
23275         if(btn.pressed !== this.sourceEditMode){
23276             btn.toggle(this.sourceEditMode);
23277             return;
23278         }
23279         
23280         if(sourceEditMode){
23281             Roo.log("disabling buttons");
23282             this.tb.items.each(function(item){
23283                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23284                     item.disable();
23285                 }
23286             });
23287           
23288         }else{
23289             Roo.log("enabling buttons");
23290             if(this.editorcore.initialized){
23291                 this.tb.items.each(function(item){
23292                     item.enable();
23293                 });
23294             }
23295             
23296         }
23297         Roo.log("calling toggole on editor");
23298         // tell the editor that it's been pressed..
23299         this.editor.toggleSourceEdit(sourceEditMode);
23300        
23301     },
23302      /**
23303      * Object collection of toolbar tooltips for the buttons in the editor. The key
23304      * is the command id associated with that button and the value is a valid QuickTips object.
23305      * For example:
23306 <pre><code>
23307 {
23308     bold : {
23309         title: 'Bold (Ctrl+B)',
23310         text: 'Make the selected text bold.',
23311         cls: 'x-html-editor-tip'
23312     },
23313     italic : {
23314         title: 'Italic (Ctrl+I)',
23315         text: 'Make the selected text italic.',
23316         cls: 'x-html-editor-tip'
23317     },
23318     ...
23319 </code></pre>
23320     * @type Object
23321      */
23322     buttonTips : {
23323         bold : {
23324             title: 'Bold (Ctrl+B)',
23325             text: 'Make the selected text bold.',
23326             cls: 'x-html-editor-tip'
23327         },
23328         italic : {
23329             title: 'Italic (Ctrl+I)',
23330             text: 'Make the selected text italic.',
23331             cls: 'x-html-editor-tip'
23332         },
23333         underline : {
23334             title: 'Underline (Ctrl+U)',
23335             text: 'Underline the selected text.',
23336             cls: 'x-html-editor-tip'
23337         },
23338         strikethrough : {
23339             title: 'Strikethrough',
23340             text: 'Strikethrough the selected text.',
23341             cls: 'x-html-editor-tip'
23342         },
23343         increasefontsize : {
23344             title: 'Grow Text',
23345             text: 'Increase the font size.',
23346             cls: 'x-html-editor-tip'
23347         },
23348         decreasefontsize : {
23349             title: 'Shrink Text',
23350             text: 'Decrease the font size.',
23351             cls: 'x-html-editor-tip'
23352         },
23353         backcolor : {
23354             title: 'Text Highlight Color',
23355             text: 'Change the background color of the selected text.',
23356             cls: 'x-html-editor-tip'
23357         },
23358         forecolor : {
23359             title: 'Font Color',
23360             text: 'Change the color of the selected text.',
23361             cls: 'x-html-editor-tip'
23362         },
23363         justifyleft : {
23364             title: 'Align Text Left',
23365             text: 'Align text to the left.',
23366             cls: 'x-html-editor-tip'
23367         },
23368         justifycenter : {
23369             title: 'Center Text',
23370             text: 'Center text in the editor.',
23371             cls: 'x-html-editor-tip'
23372         },
23373         justifyright : {
23374             title: 'Align Text Right',
23375             text: 'Align text to the right.',
23376             cls: 'x-html-editor-tip'
23377         },
23378         insertunorderedlist : {
23379             title: 'Bullet List',
23380             text: 'Start a bulleted list.',
23381             cls: 'x-html-editor-tip'
23382         },
23383         insertorderedlist : {
23384             title: 'Numbered List',
23385             text: 'Start a numbered list.',
23386             cls: 'x-html-editor-tip'
23387         },
23388         createlink : {
23389             title: 'Hyperlink',
23390             text: 'Make the selected text a hyperlink.',
23391             cls: 'x-html-editor-tip'
23392         },
23393         sourceedit : {
23394             title: 'Source Edit',
23395             text: 'Switch to source editing mode.',
23396             cls: 'x-html-editor-tip'
23397         }
23398     },
23399     // private
23400     onDestroy : function(){
23401         if(this.rendered){
23402             
23403             this.tb.items.each(function(item){
23404                 if(item.menu){
23405                     item.menu.removeAll();
23406                     if(item.menu.el){
23407                         item.menu.el.destroy();
23408                     }
23409                 }
23410                 item.destroy();
23411             });
23412              
23413         }
23414     },
23415     onFirstFocus: function() {
23416         this.tb.items.each(function(item){
23417            item.enable();
23418         });
23419     }
23420 });
23421
23422
23423
23424
23425 // <script type="text/javascript">
23426 /*
23427  * Based on
23428  * Ext JS Library 1.1.1
23429  * Copyright(c) 2006-2007, Ext JS, LLC.
23430  *  
23431  
23432  */
23433
23434  
23435 /**
23436  * @class Roo.form.HtmlEditor.ToolbarContext
23437  * Context Toolbar
23438  * 
23439  * Usage:
23440  *
23441  new Roo.form.HtmlEditor({
23442     ....
23443     toolbars : [
23444         { xtype: 'ToolbarStandard', styles : {} }
23445         { xtype: 'ToolbarContext', disable : {} }
23446     ]
23447 })
23448
23449      
23450  * 
23451  * @config : {Object} disable List of elements to disable.. (not done yet.)
23452  * @config : {Object} styles  Map of styles available.
23453  * 
23454  */
23455
23456 Roo.form.HtmlEditor.ToolbarContext = function(config)
23457 {
23458     
23459     Roo.apply(this, config);
23460     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23461     // dont call parent... till later.
23462     this.styles = this.styles || {};
23463 }
23464
23465  
23466
23467 Roo.form.HtmlEditor.ToolbarContext.types = {
23468     'IMG' : {
23469         width : {
23470             title: "Width",
23471             width: 40
23472         },
23473         height:  {
23474             title: "Height",
23475             width: 40
23476         },
23477         align: {
23478             title: "Align",
23479             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23480             width : 80
23481             
23482         },
23483         border: {
23484             title: "Border",
23485             width: 40
23486         },
23487         alt: {
23488             title: "Alt",
23489             width: 120
23490         },
23491         src : {
23492             title: "Src",
23493             width: 220
23494         }
23495         
23496     },
23497     'A' : {
23498         name : {
23499             title: "Name",
23500             width: 50
23501         },
23502         target:  {
23503             title: "Target",
23504             width: 120
23505         },
23506         href:  {
23507             title: "Href",
23508             width: 220
23509         } // border?
23510         
23511     },
23512     'TABLE' : {
23513         rows : {
23514             title: "Rows",
23515             width: 20
23516         },
23517         cols : {
23518             title: "Cols",
23519             width: 20
23520         },
23521         width : {
23522             title: "Width",
23523             width: 40
23524         },
23525         height : {
23526             title: "Height",
23527             width: 40
23528         },
23529         border : {
23530             title: "Border",
23531             width: 20
23532         }
23533     },
23534     'TD' : {
23535         width : {
23536             title: "Width",
23537             width: 40
23538         },
23539         height : {
23540             title: "Height",
23541             width: 40
23542         },   
23543         align: {
23544             title: "Align",
23545             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23546             width: 80
23547         },
23548         valign: {
23549             title: "Valign",
23550             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23551             width: 80
23552         },
23553         colspan: {
23554             title: "Colspan",
23555             width: 20
23556             
23557         },
23558          'font-family'  : {
23559             title : "Font",
23560             style : 'fontFamily',
23561             displayField: 'display',
23562             optname : 'font-family',
23563             width: 140
23564         }
23565     },
23566     'INPUT' : {
23567         name : {
23568             title: "name",
23569             width: 120
23570         },
23571         value : {
23572             title: "Value",
23573             width: 120
23574         },
23575         width : {
23576             title: "Width",
23577             width: 40
23578         }
23579     },
23580     'LABEL' : {
23581         'for' : {
23582             title: "For",
23583             width: 120
23584         }
23585     },
23586     'TEXTAREA' : {
23587           name : {
23588             title: "name",
23589             width: 120
23590         },
23591         rows : {
23592             title: "Rows",
23593             width: 20
23594         },
23595         cols : {
23596             title: "Cols",
23597             width: 20
23598         }
23599     },
23600     'SELECT' : {
23601         name : {
23602             title: "name",
23603             width: 120
23604         },
23605         selectoptions : {
23606             title: "Options",
23607             width: 200
23608         }
23609     },
23610     
23611     // should we really allow this??
23612     // should this just be 
23613     'BODY' : {
23614         title : {
23615             title: "Title",
23616             width: 200,
23617             disabled : true
23618         }
23619     },
23620     'SPAN' : {
23621         'font-family'  : {
23622             title : "Font",
23623             style : 'fontFamily',
23624             displayField: 'display',
23625             optname : 'font-family',
23626             width: 140
23627         }
23628     },
23629     'DIV' : {
23630         'font-family'  : {
23631             title : "Font",
23632             style : 'fontFamily',
23633             displayField: 'display',
23634             optname : 'font-family',
23635             width: 140
23636         }
23637     },
23638      'P' : {
23639         'font-family'  : {
23640             title : "Font",
23641             style : 'fontFamily',
23642             displayField: 'display',
23643             optname : 'font-family',
23644             width: 140
23645         }
23646     },
23647     
23648     '*' : {
23649         // empty..
23650     }
23651
23652 };
23653
23654 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23655 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23656
23657 Roo.form.HtmlEditor.ToolbarContext.options = {
23658         'font-family'  : [ 
23659                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23660                 [ 'Courier New', 'Courier New'],
23661                 [ 'Tahoma', 'Tahoma'],
23662                 [ 'Times New Roman,serif', 'Times'],
23663                 [ 'Verdana','Verdana' ]
23664         ]
23665 };
23666
23667 // fixme - these need to be configurable..
23668  
23669
23670 //Roo.form.HtmlEditor.ToolbarContext.types
23671
23672
23673 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23674     
23675     tb: false,
23676     
23677     rendered: false,
23678     
23679     editor : false,
23680     editorcore : false,
23681     /**
23682      * @cfg {Object} disable  List of toolbar elements to disable
23683          
23684      */
23685     disable : false,
23686     /**
23687      * @cfg {Object} styles List of styles 
23688      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23689      *
23690      * These must be defined in the page, so they get rendered correctly..
23691      * .headline { }
23692      * TD.underline { }
23693      * 
23694      */
23695     styles : false,
23696     
23697     options: false,
23698     
23699     toolbars : false,
23700     
23701     init : function(editor)
23702     {
23703         this.editor = editor;
23704         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23705         var editorcore = this.editorcore;
23706         
23707         var fid = editorcore.frameId;
23708         var etb = this;
23709         function btn(id, toggle, handler){
23710             var xid = fid + '-'+ id ;
23711             return {
23712                 id : xid,
23713                 cmd : id,
23714                 cls : 'x-btn-icon x-edit-'+id,
23715                 enableToggle:toggle !== false,
23716                 scope: editorcore, // was editor...
23717                 handler:handler||editorcore.relayBtnCmd,
23718                 clickEvent:'mousedown',
23719                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23720                 tabIndex:-1
23721             };
23722         }
23723         // create a new element.
23724         var wdiv = editor.wrap.createChild({
23725                 tag: 'div'
23726             }, editor.wrap.dom.firstChild.nextSibling, true);
23727         
23728         // can we do this more than once??
23729         
23730          // stop form submits
23731       
23732  
23733         // disable everything...
23734         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23735         this.toolbars = {};
23736            
23737         for (var i in  ty) {
23738           
23739             this.toolbars[i] = this.buildToolbar(ty[i],i);
23740         }
23741         this.tb = this.toolbars.BODY;
23742         this.tb.el.show();
23743         this.buildFooter();
23744         this.footer.show();
23745         editor.on('hide', function( ) { this.footer.hide() }, this);
23746         editor.on('show', function( ) { this.footer.show() }, this);
23747         
23748          
23749         this.rendered = true;
23750         
23751         // the all the btns;
23752         editor.on('editorevent', this.updateToolbar, this);
23753         // other toolbars need to implement this..
23754         //editor.on('editmodechange', this.updateToolbar, this);
23755     },
23756     
23757     
23758     
23759     /**
23760      * Protected method that will not generally be called directly. It triggers
23761      * a toolbar update by reading the markup state of the current selection in the editor.
23762      *
23763      * Note you can force an update by calling on('editorevent', scope, false)
23764      */
23765     updateToolbar: function(editor,ev,sel){
23766
23767         //Roo.log(ev);
23768         // capture mouse up - this is handy for selecting images..
23769         // perhaps should go somewhere else...
23770         if(!this.editorcore.activated){
23771              this.editor.onFirstFocus();
23772             return;
23773         }
23774         
23775         
23776         
23777         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23778         // selectNode - might want to handle IE?
23779         if (ev &&
23780             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23781             ev.target && ev.target.tagName == 'IMG') {
23782             // they have click on an image...
23783             // let's see if we can change the selection...
23784             sel = ev.target;
23785          
23786               var nodeRange = sel.ownerDocument.createRange();
23787             try {
23788                 nodeRange.selectNode(sel);
23789             } catch (e) {
23790                 nodeRange.selectNodeContents(sel);
23791             }
23792             //nodeRange.collapse(true);
23793             var s = this.editorcore.win.getSelection();
23794             s.removeAllRanges();
23795             s.addRange(nodeRange);
23796         }  
23797         
23798       
23799         var updateFooter = sel ? false : true;
23800         
23801         
23802         var ans = this.editorcore.getAllAncestors();
23803         
23804         // pick
23805         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23806         
23807         if (!sel) { 
23808             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23809             sel = sel ? sel : this.editorcore.doc.body;
23810             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23811             
23812         }
23813         // pick a menu that exists..
23814         var tn = sel.tagName.toUpperCase();
23815         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23816         
23817         tn = sel.tagName.toUpperCase();
23818         
23819         var lastSel = this.tb.selectedNode;
23820         
23821         this.tb.selectedNode = sel;
23822         
23823         // if current menu does not match..
23824         
23825         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23826                 
23827             this.tb.el.hide();
23828             ///console.log("show: " + tn);
23829             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23830             this.tb.el.show();
23831             // update name
23832             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23833             
23834             
23835             // update attributes
23836             if (this.tb.fields) {
23837                 this.tb.fields.each(function(e) {
23838                     if (e.stylename) {
23839                         e.setValue(sel.style[e.stylename]);
23840                         return;
23841                     } 
23842                    e.setValue(sel.getAttribute(e.attrname));
23843                 });
23844             }
23845             
23846             var hasStyles = false;
23847             for(var i in this.styles) {
23848                 hasStyles = true;
23849                 break;
23850             }
23851             
23852             // update styles
23853             if (hasStyles) { 
23854                 var st = this.tb.fields.item(0);
23855                 
23856                 st.store.removeAll();
23857                
23858                 
23859                 var cn = sel.className.split(/\s+/);
23860                 
23861                 var avs = [];
23862                 if (this.styles['*']) {
23863                     
23864                     Roo.each(this.styles['*'], function(v) {
23865                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23866                     });
23867                 }
23868                 if (this.styles[tn]) { 
23869                     Roo.each(this.styles[tn], function(v) {
23870                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23871                     });
23872                 }
23873                 
23874                 st.store.loadData(avs);
23875                 st.collapse();
23876                 st.setValue(cn);
23877             }
23878             // flag our selected Node.
23879             this.tb.selectedNode = sel;
23880            
23881            
23882             Roo.menu.MenuMgr.hideAll();
23883
23884         }
23885         
23886         if (!updateFooter) {
23887             //this.footDisp.dom.innerHTML = ''; 
23888             return;
23889         }
23890         // update the footer
23891         //
23892         var html = '';
23893         
23894         this.footerEls = ans.reverse();
23895         Roo.each(this.footerEls, function(a,i) {
23896             if (!a) { return; }
23897             html += html.length ? ' &gt; '  :  '';
23898             
23899             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23900             
23901         });
23902        
23903         // 
23904         var sz = this.footDisp.up('td').getSize();
23905         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23906         this.footDisp.dom.style.marginLeft = '5px';
23907         
23908         this.footDisp.dom.style.overflow = 'hidden';
23909         
23910         this.footDisp.dom.innerHTML = html;
23911             
23912         //this.editorsyncValue();
23913     },
23914      
23915     
23916    
23917        
23918     // private
23919     onDestroy : function(){
23920         if(this.rendered){
23921             
23922             this.tb.items.each(function(item){
23923                 if(item.menu){
23924                     item.menu.removeAll();
23925                     if(item.menu.el){
23926                         item.menu.el.destroy();
23927                     }
23928                 }
23929                 item.destroy();
23930             });
23931              
23932         }
23933     },
23934     onFirstFocus: function() {
23935         // need to do this for all the toolbars..
23936         this.tb.items.each(function(item){
23937            item.enable();
23938         });
23939     },
23940     buildToolbar: function(tlist, nm)
23941     {
23942         var editor = this.editor;
23943         var editorcore = this.editorcore;
23944          // create a new element.
23945         var wdiv = editor.wrap.createChild({
23946                 tag: 'div'
23947             }, editor.wrap.dom.firstChild.nextSibling, true);
23948         
23949        
23950         var tb = new Roo.Toolbar(wdiv);
23951         // add the name..
23952         
23953         tb.add(nm+ ":&nbsp;");
23954         
23955         var styles = [];
23956         for(var i in this.styles) {
23957             styles.push(i);
23958         }
23959         
23960         // styles...
23961         if (styles && styles.length) {
23962             
23963             // this needs a multi-select checkbox...
23964             tb.addField( new Roo.form.ComboBox({
23965                 store: new Roo.data.SimpleStore({
23966                     id : 'val',
23967                     fields: ['val', 'selected'],
23968                     data : [] 
23969                 }),
23970                 name : '-roo-edit-className',
23971                 attrname : 'className',
23972                 displayField: 'val',
23973                 typeAhead: false,
23974                 mode: 'local',
23975                 editable : false,
23976                 triggerAction: 'all',
23977                 emptyText:'Select Style',
23978                 selectOnFocus:true,
23979                 width: 130,
23980                 listeners : {
23981                     'select': function(c, r, i) {
23982                         // initial support only for on class per el..
23983                         tb.selectedNode.className =  r ? r.get('val') : '';
23984                         editorcore.syncValue();
23985                     }
23986                 }
23987     
23988             }));
23989         }
23990         
23991         var tbc = Roo.form.HtmlEditor.ToolbarContext;
23992         var tbops = tbc.options;
23993         
23994         for (var i in tlist) {
23995             
23996             var item = tlist[i];
23997             tb.add(item.title + ":&nbsp;");
23998             
23999             
24000             //optname == used so you can configure the options available..
24001             var opts = item.opts ? item.opts : false;
24002             if (item.optname) {
24003                 opts = tbops[item.optname];
24004            
24005             }
24006             
24007             if (opts) {
24008                 // opts == pulldown..
24009                 tb.addField( new Roo.form.ComboBox({
24010                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24011                         id : 'val',
24012                         fields: ['val', 'display'],
24013                         data : opts  
24014                     }),
24015                     name : '-roo-edit-' + i,
24016                     attrname : i,
24017                     stylename : item.style ? item.style : false,
24018                     displayField: item.displayField ? item.displayField : 'val',
24019                     valueField :  'val',
24020                     typeAhead: false,
24021                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24022                     editable : false,
24023                     triggerAction: 'all',
24024                     emptyText:'Select',
24025                     selectOnFocus:true,
24026                     width: item.width ? item.width  : 130,
24027                     listeners : {
24028                         'select': function(c, r, i) {
24029                             if (c.stylename) {
24030                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24031                                 return;
24032                             }
24033                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24034                         }
24035                     }
24036
24037                 }));
24038                 continue;
24039                     
24040                  
24041                 
24042                 tb.addField( new Roo.form.TextField({
24043                     name: i,
24044                     width: 100,
24045                     //allowBlank:false,
24046                     value: ''
24047                 }));
24048                 continue;
24049             }
24050             tb.addField( new Roo.form.TextField({
24051                 name: '-roo-edit-' + i,
24052                 attrname : i,
24053                 
24054                 width: item.width,
24055                 //allowBlank:true,
24056                 value: '',
24057                 listeners: {
24058                     'change' : function(f, nv, ov) {
24059                         tb.selectedNode.setAttribute(f.attrname, nv);
24060                         editorcore.syncValue();
24061                     }
24062                 }
24063             }));
24064              
24065         }
24066         
24067         var _this = this;
24068         
24069         if(nm == 'BODY'){
24070             tb.addSeparator();
24071         
24072             tb.addButton( {
24073                 text: 'Stylesheets',
24074
24075                 listeners : {
24076                     click : function ()
24077                     {
24078                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24079                     }
24080                 }
24081             });
24082         }
24083         
24084         tb.addFill();
24085         tb.addButton( {
24086             text: 'Remove Tag',
24087     
24088             listeners : {
24089                 click : function ()
24090                 {
24091                     // remove
24092                     // undo does not work.
24093                      
24094                     var sn = tb.selectedNode;
24095                     
24096                     var pn = sn.parentNode;
24097                     
24098                     var stn =  sn.childNodes[0];
24099                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24100                     while (sn.childNodes.length) {
24101                         var node = sn.childNodes[0];
24102                         sn.removeChild(node);
24103                         //Roo.log(node);
24104                         pn.insertBefore(node, sn);
24105                         
24106                     }
24107                     pn.removeChild(sn);
24108                     var range = editorcore.createRange();
24109         
24110                     range.setStart(stn,0);
24111                     range.setEnd(en,0); //????
24112                     //range.selectNode(sel);
24113                     
24114                     
24115                     var selection = editorcore.getSelection();
24116                     selection.removeAllRanges();
24117                     selection.addRange(range);
24118                     
24119                     
24120                     
24121                     //_this.updateToolbar(null, null, pn);
24122                     _this.updateToolbar(null, null, null);
24123                     _this.footDisp.dom.innerHTML = ''; 
24124                 }
24125             }
24126             
24127                     
24128                 
24129             
24130         });
24131         
24132         
24133         tb.el.on('click', function(e){
24134             e.preventDefault(); // what does this do?
24135         });
24136         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24137         tb.el.hide();
24138         tb.name = nm;
24139         // dont need to disable them... as they will get hidden
24140         return tb;
24141          
24142         
24143     },
24144     buildFooter : function()
24145     {
24146         
24147         var fel = this.editor.wrap.createChild();
24148         this.footer = new Roo.Toolbar(fel);
24149         // toolbar has scrolly on left / right?
24150         var footDisp= new Roo.Toolbar.Fill();
24151         var _t = this;
24152         this.footer.add(
24153             {
24154                 text : '&lt;',
24155                 xtype: 'Button',
24156                 handler : function() {
24157                     _t.footDisp.scrollTo('left',0,true)
24158                 }
24159             }
24160         );
24161         this.footer.add( footDisp );
24162         this.footer.add( 
24163             {
24164                 text : '&gt;',
24165                 xtype: 'Button',
24166                 handler : function() {
24167                     // no animation..
24168                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24169                 }
24170             }
24171         );
24172         var fel = Roo.get(footDisp.el);
24173         fel.addClass('x-editor-context');
24174         this.footDispWrap = fel; 
24175         this.footDispWrap.overflow  = 'hidden';
24176         
24177         this.footDisp = fel.createChild();
24178         this.footDispWrap.on('click', this.onContextClick, this)
24179         
24180         
24181     },
24182     onContextClick : function (ev,dom)
24183     {
24184         ev.preventDefault();
24185         var  cn = dom.className;
24186         //Roo.log(cn);
24187         if (!cn.match(/x-ed-loc-/)) {
24188             return;
24189         }
24190         var n = cn.split('-').pop();
24191         var ans = this.footerEls;
24192         var sel = ans[n];
24193         
24194          // pick
24195         var range = this.editorcore.createRange();
24196         
24197         range.selectNodeContents(sel);
24198         //range.selectNode(sel);
24199         
24200         
24201         var selection = this.editorcore.getSelection();
24202         selection.removeAllRanges();
24203         selection.addRange(range);
24204         
24205         
24206         
24207         this.updateToolbar(null, null, sel);
24208         
24209         
24210     }
24211     
24212     
24213     
24214     
24215     
24216 });
24217
24218
24219
24220
24221
24222 /*
24223  * Based on:
24224  * Ext JS Library 1.1.1
24225  * Copyright(c) 2006-2007, Ext JS, LLC.
24226  *
24227  * Originally Released Under LGPL - original licence link has changed is not relivant.
24228  *
24229  * Fork - LGPL
24230  * <script type="text/javascript">
24231  */
24232  
24233 /**
24234  * @class Roo.form.BasicForm
24235  * @extends Roo.util.Observable
24236  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24237  * @constructor
24238  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24239  * @param {Object} config Configuration options
24240  */
24241 Roo.form.BasicForm = function(el, config){
24242     this.allItems = [];
24243     this.childForms = [];
24244     Roo.apply(this, config);
24245     /*
24246      * The Roo.form.Field items in this form.
24247      * @type MixedCollection
24248      */
24249      
24250      
24251     this.items = new Roo.util.MixedCollection(false, function(o){
24252         return o.id || (o.id = Roo.id());
24253     });
24254     this.addEvents({
24255         /**
24256          * @event beforeaction
24257          * Fires before any action is performed. Return false to cancel the action.
24258          * @param {Form} this
24259          * @param {Action} action The action to be performed
24260          */
24261         beforeaction: true,
24262         /**
24263          * @event actionfailed
24264          * Fires when an action fails.
24265          * @param {Form} this
24266          * @param {Action} action The action that failed
24267          */
24268         actionfailed : true,
24269         /**
24270          * @event actioncomplete
24271          * Fires when an action is completed.
24272          * @param {Form} this
24273          * @param {Action} action The action that completed
24274          */
24275         actioncomplete : true
24276     });
24277     if(el){
24278         this.initEl(el);
24279     }
24280     Roo.form.BasicForm.superclass.constructor.call(this);
24281 };
24282
24283 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24284     /**
24285      * @cfg {String} method
24286      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24287      */
24288     /**
24289      * @cfg {DataReader} reader
24290      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24291      * This is optional as there is built-in support for processing JSON.
24292      */
24293     /**
24294      * @cfg {DataReader} errorReader
24295      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24296      * This is completely optional as there is built-in support for processing JSON.
24297      */
24298     /**
24299      * @cfg {String} url
24300      * The URL to use for form actions if one isn't supplied in the action options.
24301      */
24302     /**
24303      * @cfg {Boolean} fileUpload
24304      * Set to true if this form is a file upload.
24305      */
24306      
24307     /**
24308      * @cfg {Object} baseParams
24309      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24310      */
24311      /**
24312      
24313     /**
24314      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24315      */
24316     timeout: 30,
24317
24318     // private
24319     activeAction : null,
24320
24321     /**
24322      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24323      * or setValues() data instead of when the form was first created.
24324      */
24325     trackResetOnLoad : false,
24326     
24327     
24328     /**
24329      * childForms - used for multi-tab forms
24330      * @type {Array}
24331      */
24332     childForms : false,
24333     
24334     /**
24335      * allItems - full list of fields.
24336      * @type {Array}
24337      */
24338     allItems : false,
24339     
24340     /**
24341      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24342      * element by passing it or its id or mask the form itself by passing in true.
24343      * @type Mixed
24344      */
24345     waitMsgTarget : false,
24346
24347     // private
24348     initEl : function(el){
24349         this.el = Roo.get(el);
24350         this.id = this.el.id || Roo.id();
24351         this.el.on('submit', this.onSubmit, this);
24352         this.el.addClass('x-form');
24353     },
24354
24355     // private
24356     onSubmit : function(e){
24357         e.stopEvent();
24358     },
24359
24360     /**
24361      * Returns true if client-side validation on the form is successful.
24362      * @return Boolean
24363      */
24364     isValid : function(){
24365         var valid = true;
24366         this.items.each(function(f){
24367            if(!f.validate()){
24368                valid = false;
24369            }
24370         });
24371         return valid;
24372     },
24373
24374     /**
24375      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24376      * @return Boolean
24377      */
24378     isDirty : function(){
24379         var dirty = false;
24380         this.items.each(function(f){
24381            if(f.isDirty()){
24382                dirty = true;
24383                return false;
24384            }
24385         });
24386         return dirty;
24387     },
24388     
24389     /**
24390      * Returns true if any fields in this form have changed since their original load. (New version)
24391      * @return Boolean
24392      */
24393     
24394     hasChanged : function()
24395     {
24396         var dirty = false;
24397         this.items.each(function(f){
24398            if(f.hasChanged()){
24399                dirty = true;
24400                return false;
24401            }
24402         });
24403         return dirty;
24404         
24405     },
24406     /**
24407      * Resets all hasChanged to 'false' -
24408      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24409      * So hasChanged storage is only to be used for this purpose
24410      * @return Boolean
24411      */
24412     resetHasChanged : function()
24413     {
24414         this.items.each(function(f){
24415            f.resetHasChanged();
24416         });
24417         
24418     },
24419     
24420     
24421     /**
24422      * Performs a predefined action (submit or load) or custom actions you define on this form.
24423      * @param {String} actionName The name of the action type
24424      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24425      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24426      * accept other config options):
24427      * <pre>
24428 Property          Type             Description
24429 ----------------  ---------------  ----------------------------------------------------------------------------------
24430 url               String           The url for the action (defaults to the form's url)
24431 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24432 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24433 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24434                                    validate the form on the client (defaults to false)
24435      * </pre>
24436      * @return {BasicForm} this
24437      */
24438     doAction : function(action, options){
24439         if(typeof action == 'string'){
24440             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24441         }
24442         if(this.fireEvent('beforeaction', this, action) !== false){
24443             this.beforeAction(action);
24444             action.run.defer(100, action);
24445         }
24446         return this;
24447     },
24448
24449     /**
24450      * Shortcut to do a submit action.
24451      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24452      * @return {BasicForm} this
24453      */
24454     submit : function(options){
24455         this.doAction('submit', options);
24456         return this;
24457     },
24458
24459     /**
24460      * Shortcut to do a load action.
24461      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24462      * @return {BasicForm} this
24463      */
24464     load : function(options){
24465         this.doAction('load', options);
24466         return this;
24467     },
24468
24469     /**
24470      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24471      * @param {Record} record The record to edit
24472      * @return {BasicForm} this
24473      */
24474     updateRecord : function(record){
24475         record.beginEdit();
24476         var fs = record.fields;
24477         fs.each(function(f){
24478             var field = this.findField(f.name);
24479             if(field){
24480                 record.set(f.name, field.getValue());
24481             }
24482         }, this);
24483         record.endEdit();
24484         return this;
24485     },
24486
24487     /**
24488      * Loads an Roo.data.Record into this form.
24489      * @param {Record} record The record to load
24490      * @return {BasicForm} this
24491      */
24492     loadRecord : function(record){
24493         this.setValues(record.data);
24494         return this;
24495     },
24496
24497     // private
24498     beforeAction : function(action){
24499         var o = action.options;
24500         
24501        
24502         if(this.waitMsgTarget === true){
24503             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24504         }else if(this.waitMsgTarget){
24505             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24506             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24507         }else {
24508             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24509         }
24510          
24511     },
24512
24513     // private
24514     afterAction : function(action, success){
24515         this.activeAction = null;
24516         var o = action.options;
24517         
24518         if(this.waitMsgTarget === true){
24519             this.el.unmask();
24520         }else if(this.waitMsgTarget){
24521             this.waitMsgTarget.unmask();
24522         }else{
24523             Roo.MessageBox.updateProgress(1);
24524             Roo.MessageBox.hide();
24525         }
24526          
24527         if(success){
24528             if(o.reset){
24529                 this.reset();
24530             }
24531             Roo.callback(o.success, o.scope, [this, action]);
24532             this.fireEvent('actioncomplete', this, action);
24533             
24534         }else{
24535             
24536             // failure condition..
24537             // we have a scenario where updates need confirming.
24538             // eg. if a locking scenario exists..
24539             // we look for { errors : { needs_confirm : true }} in the response.
24540             if (
24541                 (typeof(action.result) != 'undefined')  &&
24542                 (typeof(action.result.errors) != 'undefined')  &&
24543                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24544            ){
24545                 var _t = this;
24546                 Roo.MessageBox.confirm(
24547                     "Change requires confirmation",
24548                     action.result.errorMsg,
24549                     function(r) {
24550                         if (r != 'yes') {
24551                             return;
24552                         }
24553                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24554                     }
24555                     
24556                 );
24557                 
24558                 
24559                 
24560                 return;
24561             }
24562             
24563             Roo.callback(o.failure, o.scope, [this, action]);
24564             // show an error message if no failed handler is set..
24565             if (!this.hasListener('actionfailed')) {
24566                 Roo.MessageBox.alert("Error",
24567                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24568                         action.result.errorMsg :
24569                         "Saving Failed, please check your entries or try again"
24570                 );
24571             }
24572             
24573             this.fireEvent('actionfailed', this, action);
24574         }
24575         
24576     },
24577
24578     /**
24579      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24580      * @param {String} id The value to search for
24581      * @return Field
24582      */
24583     findField : function(id){
24584         var field = this.items.get(id);
24585         if(!field){
24586             this.items.each(function(f){
24587                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24588                     field = f;
24589                     return false;
24590                 }
24591             });
24592         }
24593         return field || null;
24594     },
24595
24596     /**
24597      * Add a secondary form to this one, 
24598      * Used to provide tabbed forms. One form is primary, with hidden values 
24599      * which mirror the elements from the other forms.
24600      * 
24601      * @param {Roo.form.Form} form to add.
24602      * 
24603      */
24604     addForm : function(form)
24605     {
24606        
24607         if (this.childForms.indexOf(form) > -1) {
24608             // already added..
24609             return;
24610         }
24611         this.childForms.push(form);
24612         var n = '';
24613         Roo.each(form.allItems, function (fe) {
24614             
24615             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24616             if (this.findField(n)) { // already added..
24617                 return;
24618             }
24619             var add = new Roo.form.Hidden({
24620                 name : n
24621             });
24622             add.render(this.el);
24623             
24624             this.add( add );
24625         }, this);
24626         
24627     },
24628     /**
24629      * Mark fields in this form invalid in bulk.
24630      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24631      * @return {BasicForm} this
24632      */
24633     markInvalid : function(errors){
24634         if(errors instanceof Array){
24635             for(var i = 0, len = errors.length; i < len; i++){
24636                 var fieldError = errors[i];
24637                 var f = this.findField(fieldError.id);
24638                 if(f){
24639                     f.markInvalid(fieldError.msg);
24640                 }
24641             }
24642         }else{
24643             var field, id;
24644             for(id in errors){
24645                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24646                     field.markInvalid(errors[id]);
24647                 }
24648             }
24649         }
24650         Roo.each(this.childForms || [], function (f) {
24651             f.markInvalid(errors);
24652         });
24653         
24654         return this;
24655     },
24656
24657     /**
24658      * Set values for fields in this form in bulk.
24659      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24660      * @return {BasicForm} this
24661      */
24662     setValues : function(values){
24663         if(values instanceof Array){ // array of objects
24664             for(var i = 0, len = values.length; i < len; i++){
24665                 var v = values[i];
24666                 var f = this.findField(v.id);
24667                 if(f){
24668                     f.setValue(v.value);
24669                     if(this.trackResetOnLoad){
24670                         f.originalValue = f.getValue();
24671                     }
24672                 }
24673             }
24674         }else{ // object hash
24675             var field, id;
24676             for(id in values){
24677                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24678                     
24679                     if (field.setFromData && 
24680                         field.valueField && 
24681                         field.displayField &&
24682                         // combos' with local stores can 
24683                         // be queried via setValue()
24684                         // to set their value..
24685                         (field.store && !field.store.isLocal)
24686                         ) {
24687                         // it's a combo
24688                         var sd = { };
24689                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24690                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24691                         field.setFromData(sd);
24692                         
24693                     } else {
24694                         field.setValue(values[id]);
24695                     }
24696                     
24697                     
24698                     if(this.trackResetOnLoad){
24699                         field.originalValue = field.getValue();
24700                     }
24701                 }
24702             }
24703         }
24704         this.resetHasChanged();
24705         
24706         
24707         Roo.each(this.childForms || [], function (f) {
24708             f.setValues(values);
24709             f.resetHasChanged();
24710         });
24711                 
24712         return this;
24713     },
24714
24715     /**
24716      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24717      * they are returned as an array.
24718      * @param {Boolean} asString
24719      * @return {Object}
24720      */
24721     getValues : function(asString){
24722         if (this.childForms) {
24723             // copy values from the child forms
24724             Roo.each(this.childForms, function (f) {
24725                 this.setValues(f.getValues());
24726             }, this);
24727         }
24728         
24729         
24730         
24731         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24732         if(asString === true){
24733             return fs;
24734         }
24735         return Roo.urlDecode(fs);
24736     },
24737     
24738     /**
24739      * Returns the fields in this form as an object with key/value pairs. 
24740      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24741      * @return {Object}
24742      */
24743     getFieldValues : function(with_hidden)
24744     {
24745         if (this.childForms) {
24746             // copy values from the child forms
24747             // should this call getFieldValues - probably not as we do not currently copy
24748             // hidden fields when we generate..
24749             Roo.each(this.childForms, function (f) {
24750                 this.setValues(f.getValues());
24751             }, this);
24752         }
24753         
24754         var ret = {};
24755         this.items.each(function(f){
24756             if (!f.getName()) {
24757                 return;
24758             }
24759             var v = f.getValue();
24760             if (f.inputType =='radio') {
24761                 if (typeof(ret[f.getName()]) == 'undefined') {
24762                     ret[f.getName()] = ''; // empty..
24763                 }
24764                 
24765                 if (!f.el.dom.checked) {
24766                     return;
24767                     
24768                 }
24769                 v = f.el.dom.value;
24770                 
24771             }
24772             
24773             // not sure if this supported any more..
24774             if ((typeof(v) == 'object') && f.getRawValue) {
24775                 v = f.getRawValue() ; // dates..
24776             }
24777             // combo boxes where name != hiddenName...
24778             if (f.name != f.getName()) {
24779                 ret[f.name] = f.getRawValue();
24780             }
24781             ret[f.getName()] = v;
24782         });
24783         
24784         return ret;
24785     },
24786
24787     /**
24788      * Clears all invalid messages in this form.
24789      * @return {BasicForm} this
24790      */
24791     clearInvalid : function(){
24792         this.items.each(function(f){
24793            f.clearInvalid();
24794         });
24795         
24796         Roo.each(this.childForms || [], function (f) {
24797             f.clearInvalid();
24798         });
24799         
24800         
24801         return this;
24802     },
24803
24804     /**
24805      * Resets this form.
24806      * @return {BasicForm} this
24807      */
24808     reset : function(){
24809         this.items.each(function(f){
24810             f.reset();
24811         });
24812         
24813         Roo.each(this.childForms || [], function (f) {
24814             f.reset();
24815         });
24816         this.resetHasChanged();
24817         
24818         return this;
24819     },
24820
24821     /**
24822      * Add Roo.form components to this form.
24823      * @param {Field} field1
24824      * @param {Field} field2 (optional)
24825      * @param {Field} etc (optional)
24826      * @return {BasicForm} this
24827      */
24828     add : function(){
24829         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24830         return this;
24831     },
24832
24833
24834     /**
24835      * Removes a field from the items collection (does NOT remove its markup).
24836      * @param {Field} field
24837      * @return {BasicForm} this
24838      */
24839     remove : function(field){
24840         this.items.remove(field);
24841         return this;
24842     },
24843
24844     /**
24845      * Looks at the fields in this form, checks them for an id attribute,
24846      * and calls applyTo on the existing dom element with that id.
24847      * @return {BasicForm} this
24848      */
24849     render : function(){
24850         this.items.each(function(f){
24851             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24852                 f.applyTo(f.id);
24853             }
24854         });
24855         return this;
24856     },
24857
24858     /**
24859      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24860      * @param {Object} values
24861      * @return {BasicForm} this
24862      */
24863     applyToFields : function(o){
24864         this.items.each(function(f){
24865            Roo.apply(f, o);
24866         });
24867         return this;
24868     },
24869
24870     /**
24871      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24872      * @param {Object} values
24873      * @return {BasicForm} this
24874      */
24875     applyIfToFields : function(o){
24876         this.items.each(function(f){
24877            Roo.applyIf(f, o);
24878         });
24879         return this;
24880     }
24881 });
24882
24883 // back compat
24884 Roo.BasicForm = Roo.form.BasicForm;/*
24885  * Based on:
24886  * Ext JS Library 1.1.1
24887  * Copyright(c) 2006-2007, Ext JS, LLC.
24888  *
24889  * Originally Released Under LGPL - original licence link has changed is not relivant.
24890  *
24891  * Fork - LGPL
24892  * <script type="text/javascript">
24893  */
24894
24895 /**
24896  * @class Roo.form.Form
24897  * @extends Roo.form.BasicForm
24898  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24899  * @constructor
24900  * @param {Object} config Configuration options
24901  */
24902 Roo.form.Form = function(config){
24903     var xitems =  [];
24904     if (config.items) {
24905         xitems = config.items;
24906         delete config.items;
24907     }
24908    
24909     
24910     Roo.form.Form.superclass.constructor.call(this, null, config);
24911     this.url = this.url || this.action;
24912     if(!this.root){
24913         this.root = new Roo.form.Layout(Roo.applyIf({
24914             id: Roo.id()
24915         }, config));
24916     }
24917     this.active = this.root;
24918     /**
24919      * Array of all the buttons that have been added to this form via {@link addButton}
24920      * @type Array
24921      */
24922     this.buttons = [];
24923     this.allItems = [];
24924     this.addEvents({
24925         /**
24926          * @event clientvalidation
24927          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24928          * @param {Form} this
24929          * @param {Boolean} valid true if the form has passed client-side validation
24930          */
24931         clientvalidation: true,
24932         /**
24933          * @event rendered
24934          * Fires when the form is rendered
24935          * @param {Roo.form.Form} form
24936          */
24937         rendered : true
24938     });
24939     
24940     if (this.progressUrl) {
24941             // push a hidden field onto the list of fields..
24942             this.addxtype( {
24943                     xns: Roo.form, 
24944                     xtype : 'Hidden', 
24945                     name : 'UPLOAD_IDENTIFIER' 
24946             });
24947         }
24948         
24949     
24950     Roo.each(xitems, this.addxtype, this);
24951     
24952     
24953     
24954 };
24955
24956 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24957     /**
24958      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24959      */
24960     /**
24961      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24962      */
24963     /**
24964      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24965      */
24966     buttonAlign:'center',
24967
24968     /**
24969      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24970      */
24971     minButtonWidth:75,
24972
24973     /**
24974      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24975      * This property cascades to child containers if not set.
24976      */
24977     labelAlign:'left',
24978
24979     /**
24980      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24981      * fires a looping event with that state. This is required to bind buttons to the valid
24982      * state using the config value formBind:true on the button.
24983      */
24984     monitorValid : false,
24985
24986     /**
24987      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24988      */
24989     monitorPoll : 200,
24990     
24991     /**
24992      * @cfg {String} progressUrl - Url to return progress data 
24993      */
24994     
24995     progressUrl : false,
24996   
24997     /**
24998      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
24999      * fields are added and the column is closed. If no fields are passed the column remains open
25000      * until end() is called.
25001      * @param {Object} config The config to pass to the column
25002      * @param {Field} field1 (optional)
25003      * @param {Field} field2 (optional)
25004      * @param {Field} etc (optional)
25005      * @return Column The column container object
25006      */
25007     column : function(c){
25008         var col = new Roo.form.Column(c);
25009         this.start(col);
25010         if(arguments.length > 1){ // duplicate code required because of Opera
25011             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25012             this.end();
25013         }
25014         return col;
25015     },
25016
25017     /**
25018      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25019      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25020      * until end() is called.
25021      * @param {Object} config The config to pass to the fieldset
25022      * @param {Field} field1 (optional)
25023      * @param {Field} field2 (optional)
25024      * @param {Field} etc (optional)
25025      * @return FieldSet The fieldset container object
25026      */
25027     fieldset : function(c){
25028         var fs = new Roo.form.FieldSet(c);
25029         this.start(fs);
25030         if(arguments.length > 1){ // duplicate code required because of Opera
25031             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25032             this.end();
25033         }
25034         return fs;
25035     },
25036
25037     /**
25038      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25039      * fields are added and the container is closed. If no fields are passed the container remains open
25040      * until end() is called.
25041      * @param {Object} config The config to pass to the Layout
25042      * @param {Field} field1 (optional)
25043      * @param {Field} field2 (optional)
25044      * @param {Field} etc (optional)
25045      * @return Layout The container object
25046      */
25047     container : function(c){
25048         var l = new Roo.form.Layout(c);
25049         this.start(l);
25050         if(arguments.length > 1){ // duplicate code required because of Opera
25051             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25052             this.end();
25053         }
25054         return l;
25055     },
25056
25057     /**
25058      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25059      * @param {Object} container A Roo.form.Layout or subclass of Layout
25060      * @return {Form} this
25061      */
25062     start : function(c){
25063         // cascade label info
25064         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25065         this.active.stack.push(c);
25066         c.ownerCt = this.active;
25067         this.active = c;
25068         return this;
25069     },
25070
25071     /**
25072      * Closes the current open container
25073      * @return {Form} this
25074      */
25075     end : function(){
25076         if(this.active == this.root){
25077             return this;
25078         }
25079         this.active = this.active.ownerCt;
25080         return this;
25081     },
25082
25083     /**
25084      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25085      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25086      * as the label of the field.
25087      * @param {Field} field1
25088      * @param {Field} field2 (optional)
25089      * @param {Field} etc. (optional)
25090      * @return {Form} this
25091      */
25092     add : function(){
25093         this.active.stack.push.apply(this.active.stack, arguments);
25094         this.allItems.push.apply(this.allItems,arguments);
25095         var r = [];
25096         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25097             if(a[i].isFormField){
25098                 r.push(a[i]);
25099             }
25100         }
25101         if(r.length > 0){
25102             Roo.form.Form.superclass.add.apply(this, r);
25103         }
25104         return this;
25105     },
25106     
25107
25108     
25109     
25110     
25111      /**
25112      * Find any element that has been added to a form, using it's ID or name
25113      * This can include framesets, columns etc. along with regular fields..
25114      * @param {String} id - id or name to find.
25115      
25116      * @return {Element} e - or false if nothing found.
25117      */
25118     findbyId : function(id)
25119     {
25120         var ret = false;
25121         if (!id) {
25122             return ret;
25123         }
25124         Roo.each(this.allItems, function(f){
25125             if (f.id == id || f.name == id ){
25126                 ret = f;
25127                 return false;
25128             }
25129         });
25130         return ret;
25131     },
25132
25133     
25134     
25135     /**
25136      * Render this form into the passed container. This should only be called once!
25137      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25138      * @return {Form} this
25139      */
25140     render : function(ct)
25141     {
25142         
25143         
25144         
25145         ct = Roo.get(ct);
25146         var o = this.autoCreate || {
25147             tag: 'form',
25148             method : this.method || 'POST',
25149             id : this.id || Roo.id()
25150         };
25151         this.initEl(ct.createChild(o));
25152
25153         this.root.render(this.el);
25154         
25155        
25156              
25157         this.items.each(function(f){
25158             f.render('x-form-el-'+f.id);
25159         });
25160
25161         if(this.buttons.length > 0){
25162             // tables are required to maintain order and for correct IE layout
25163             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25164                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25165                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25166             }}, null, true);
25167             var tr = tb.getElementsByTagName('tr')[0];
25168             for(var i = 0, len = this.buttons.length; i < len; i++) {
25169                 var b = this.buttons[i];
25170                 var td = document.createElement('td');
25171                 td.className = 'x-form-btn-td';
25172                 b.render(tr.appendChild(td));
25173             }
25174         }
25175         if(this.monitorValid){ // initialize after render
25176             this.startMonitoring();
25177         }
25178         this.fireEvent('rendered', this);
25179         return this;
25180     },
25181
25182     /**
25183      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25184      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25185      * object or a valid Roo.DomHelper element config
25186      * @param {Function} handler The function called when the button is clicked
25187      * @param {Object} scope (optional) The scope of the handler function
25188      * @return {Roo.Button}
25189      */
25190     addButton : function(config, handler, scope){
25191         var bc = {
25192             handler: handler,
25193             scope: scope,
25194             minWidth: this.minButtonWidth,
25195             hideParent:true
25196         };
25197         if(typeof config == "string"){
25198             bc.text = config;
25199         }else{
25200             Roo.apply(bc, config);
25201         }
25202         var btn = new Roo.Button(null, bc);
25203         this.buttons.push(btn);
25204         return btn;
25205     },
25206
25207      /**
25208      * Adds a series of form elements (using the xtype property as the factory method.
25209      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25210      * @param {Object} config 
25211      */
25212     
25213     addxtype : function()
25214     {
25215         var ar = Array.prototype.slice.call(arguments, 0);
25216         var ret = false;
25217         for(var i = 0; i < ar.length; i++) {
25218             if (!ar[i]) {
25219                 continue; // skip -- if this happends something invalid got sent, we 
25220                 // should ignore it, as basically that interface element will not show up
25221                 // and that should be pretty obvious!!
25222             }
25223             
25224             if (Roo.form[ar[i].xtype]) {
25225                 ar[i].form = this;
25226                 var fe = Roo.factory(ar[i], Roo.form);
25227                 if (!ret) {
25228                     ret = fe;
25229                 }
25230                 fe.form = this;
25231                 if (fe.store) {
25232                     fe.store.form = this;
25233                 }
25234                 if (fe.isLayout) {  
25235                          
25236                     this.start(fe);
25237                     this.allItems.push(fe);
25238                     if (fe.items && fe.addxtype) {
25239                         fe.addxtype.apply(fe, fe.items);
25240                         delete fe.items;
25241                     }
25242                      this.end();
25243                     continue;
25244                 }
25245                 
25246                 
25247                  
25248                 this.add(fe);
25249               //  console.log('adding ' + ar[i].xtype);
25250             }
25251             if (ar[i].xtype == 'Button') {  
25252                 //console.log('adding button');
25253                 //console.log(ar[i]);
25254                 this.addButton(ar[i]);
25255                 this.allItems.push(fe);
25256                 continue;
25257             }
25258             
25259             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25260                 alert('end is not supported on xtype any more, use items');
25261             //    this.end();
25262             //    //console.log('adding end');
25263             }
25264             
25265         }
25266         return ret;
25267     },
25268     
25269     /**
25270      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25271      * option "monitorValid"
25272      */
25273     startMonitoring : function(){
25274         if(!this.bound){
25275             this.bound = true;
25276             Roo.TaskMgr.start({
25277                 run : this.bindHandler,
25278                 interval : this.monitorPoll || 200,
25279                 scope: this
25280             });
25281         }
25282     },
25283
25284     /**
25285      * Stops monitoring of the valid state of this form
25286      */
25287     stopMonitoring : function(){
25288         this.bound = false;
25289     },
25290
25291     // private
25292     bindHandler : function(){
25293         if(!this.bound){
25294             return false; // stops binding
25295         }
25296         var valid = true;
25297         this.items.each(function(f){
25298             if(!f.isValid(true)){
25299                 valid = false;
25300                 return false;
25301             }
25302         });
25303         for(var i = 0, len = this.buttons.length; i < len; i++){
25304             var btn = this.buttons[i];
25305             if(btn.formBind === true && btn.disabled === valid){
25306                 btn.setDisabled(!valid);
25307             }
25308         }
25309         this.fireEvent('clientvalidation', this, valid);
25310     }
25311     
25312     
25313     
25314     
25315     
25316     
25317     
25318     
25319 });
25320
25321
25322 // back compat
25323 Roo.Form = Roo.form.Form;
25324 /*
25325  * Based on:
25326  * Ext JS Library 1.1.1
25327  * Copyright(c) 2006-2007, Ext JS, LLC.
25328  *
25329  * Originally Released Under LGPL - original licence link has changed is not relivant.
25330  *
25331  * Fork - LGPL
25332  * <script type="text/javascript">
25333  */
25334
25335 // as we use this in bootstrap.
25336 Roo.namespace('Roo.form');
25337  /**
25338  * @class Roo.form.Action
25339  * Internal Class used to handle form actions
25340  * @constructor
25341  * @param {Roo.form.BasicForm} el The form element or its id
25342  * @param {Object} config Configuration options
25343  */
25344
25345  
25346  
25347 // define the action interface
25348 Roo.form.Action = function(form, options){
25349     this.form = form;
25350     this.options = options || {};
25351 };
25352 /**
25353  * Client Validation Failed
25354  * @const 
25355  */
25356 Roo.form.Action.CLIENT_INVALID = 'client';
25357 /**
25358  * Server Validation Failed
25359  * @const 
25360  */
25361 Roo.form.Action.SERVER_INVALID = 'server';
25362  /**
25363  * Connect to Server Failed
25364  * @const 
25365  */
25366 Roo.form.Action.CONNECT_FAILURE = 'connect';
25367 /**
25368  * Reading Data from Server Failed
25369  * @const 
25370  */
25371 Roo.form.Action.LOAD_FAILURE = 'load';
25372
25373 Roo.form.Action.prototype = {
25374     type : 'default',
25375     failureType : undefined,
25376     response : undefined,
25377     result : undefined,
25378
25379     // interface method
25380     run : function(options){
25381
25382     },
25383
25384     // interface method
25385     success : function(response){
25386
25387     },
25388
25389     // interface method
25390     handleResponse : function(response){
25391
25392     },
25393
25394     // default connection failure
25395     failure : function(response){
25396         
25397         this.response = response;
25398         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25399         this.form.afterAction(this, false);
25400     },
25401
25402     processResponse : function(response){
25403         this.response = response;
25404         if(!response.responseText){
25405             return true;
25406         }
25407         this.result = this.handleResponse(response);
25408         return this.result;
25409     },
25410
25411     // utility functions used internally
25412     getUrl : function(appendParams){
25413         var url = this.options.url || this.form.url || this.form.el.dom.action;
25414         if(appendParams){
25415             var p = this.getParams();
25416             if(p){
25417                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25418             }
25419         }
25420         return url;
25421     },
25422
25423     getMethod : function(){
25424         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25425     },
25426
25427     getParams : function(){
25428         var bp = this.form.baseParams;
25429         var p = this.options.params;
25430         if(p){
25431             if(typeof p == "object"){
25432                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25433             }else if(typeof p == 'string' && bp){
25434                 p += '&' + Roo.urlEncode(bp);
25435             }
25436         }else if(bp){
25437             p = Roo.urlEncode(bp);
25438         }
25439         return p;
25440     },
25441
25442     createCallback : function(){
25443         return {
25444             success: this.success,
25445             failure: this.failure,
25446             scope: this,
25447             timeout: (this.form.timeout*1000),
25448             upload: this.form.fileUpload ? this.success : undefined
25449         };
25450     }
25451 };
25452
25453 Roo.form.Action.Submit = function(form, options){
25454     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25455 };
25456
25457 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25458     type : 'submit',
25459
25460     haveProgress : false,
25461     uploadComplete : false,
25462     
25463     // uploadProgress indicator.
25464     uploadProgress : function()
25465     {
25466         if (!this.form.progressUrl) {
25467             return;
25468         }
25469         
25470         if (!this.haveProgress) {
25471             Roo.MessageBox.progress("Uploading", "Uploading");
25472         }
25473         if (this.uploadComplete) {
25474            Roo.MessageBox.hide();
25475            return;
25476         }
25477         
25478         this.haveProgress = true;
25479    
25480         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25481         
25482         var c = new Roo.data.Connection();
25483         c.request({
25484             url : this.form.progressUrl,
25485             params: {
25486                 id : uid
25487             },
25488             method: 'GET',
25489             success : function(req){
25490                //console.log(data);
25491                 var rdata = false;
25492                 var edata;
25493                 try  {
25494                    rdata = Roo.decode(req.responseText)
25495                 } catch (e) {
25496                     Roo.log("Invalid data from server..");
25497                     Roo.log(edata);
25498                     return;
25499                 }
25500                 if (!rdata || !rdata.success) {
25501                     Roo.log(rdata);
25502                     Roo.MessageBox.alert(Roo.encode(rdata));
25503                     return;
25504                 }
25505                 var data = rdata.data;
25506                 
25507                 if (this.uploadComplete) {
25508                    Roo.MessageBox.hide();
25509                    return;
25510                 }
25511                    
25512                 if (data){
25513                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25514                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25515                     );
25516                 }
25517                 this.uploadProgress.defer(2000,this);
25518             },
25519        
25520             failure: function(data) {
25521                 Roo.log('progress url failed ');
25522                 Roo.log(data);
25523             },
25524             scope : this
25525         });
25526            
25527     },
25528     
25529     
25530     run : function()
25531     {
25532         // run get Values on the form, so it syncs any secondary forms.
25533         this.form.getValues();
25534         
25535         var o = this.options;
25536         var method = this.getMethod();
25537         var isPost = method == 'POST';
25538         if(o.clientValidation === false || this.form.isValid()){
25539             
25540             if (this.form.progressUrl) {
25541                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25542                     (new Date() * 1) + '' + Math.random());
25543                     
25544             } 
25545             
25546             
25547             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25548                 form:this.form.el.dom,
25549                 url:this.getUrl(!isPost),
25550                 method: method,
25551                 params:isPost ? this.getParams() : null,
25552                 isUpload: this.form.fileUpload
25553             }));
25554             
25555             this.uploadProgress();
25556
25557         }else if (o.clientValidation !== false){ // client validation failed
25558             this.failureType = Roo.form.Action.CLIENT_INVALID;
25559             this.form.afterAction(this, false);
25560         }
25561     },
25562
25563     success : function(response)
25564     {
25565         this.uploadComplete= true;
25566         if (this.haveProgress) {
25567             Roo.MessageBox.hide();
25568         }
25569         
25570         
25571         var result = this.processResponse(response);
25572         if(result === true || result.success){
25573             this.form.afterAction(this, true);
25574             return;
25575         }
25576         if(result.errors){
25577             this.form.markInvalid(result.errors);
25578             this.failureType = Roo.form.Action.SERVER_INVALID;
25579         }
25580         this.form.afterAction(this, false);
25581     },
25582     failure : function(response)
25583     {
25584         this.uploadComplete= true;
25585         if (this.haveProgress) {
25586             Roo.MessageBox.hide();
25587         }
25588         
25589         this.response = response;
25590         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25591         this.form.afterAction(this, false);
25592     },
25593     
25594     handleResponse : function(response){
25595         if(this.form.errorReader){
25596             var rs = this.form.errorReader.read(response);
25597             var errors = [];
25598             if(rs.records){
25599                 for(var i = 0, len = rs.records.length; i < len; i++) {
25600                     var r = rs.records[i];
25601                     errors[i] = r.data;
25602                 }
25603             }
25604             if(errors.length < 1){
25605                 errors = null;
25606             }
25607             return {
25608                 success : rs.success,
25609                 errors : errors
25610             };
25611         }
25612         var ret = false;
25613         try {
25614             ret = Roo.decode(response.responseText);
25615         } catch (e) {
25616             ret = {
25617                 success: false,
25618                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25619                 errors : []
25620             };
25621         }
25622         return ret;
25623         
25624     }
25625 });
25626
25627
25628 Roo.form.Action.Load = function(form, options){
25629     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25630     this.reader = this.form.reader;
25631 };
25632
25633 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25634     type : 'load',
25635
25636     run : function(){
25637         
25638         Roo.Ajax.request(Roo.apply(
25639                 this.createCallback(), {
25640                     method:this.getMethod(),
25641                     url:this.getUrl(false),
25642                     params:this.getParams()
25643         }));
25644     },
25645
25646     success : function(response){
25647         
25648         var result = this.processResponse(response);
25649         if(result === true || !result.success || !result.data){
25650             this.failureType = Roo.form.Action.LOAD_FAILURE;
25651             this.form.afterAction(this, false);
25652             return;
25653         }
25654         this.form.clearInvalid();
25655         this.form.setValues(result.data);
25656         this.form.afterAction(this, true);
25657     },
25658
25659     handleResponse : function(response){
25660         if(this.form.reader){
25661             var rs = this.form.reader.read(response);
25662             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25663             return {
25664                 success : rs.success,
25665                 data : data
25666             };
25667         }
25668         return Roo.decode(response.responseText);
25669     }
25670 });
25671
25672 Roo.form.Action.ACTION_TYPES = {
25673     'load' : Roo.form.Action.Load,
25674     'submit' : Roo.form.Action.Submit
25675 };/*
25676  * Based on:
25677  * Ext JS Library 1.1.1
25678  * Copyright(c) 2006-2007, Ext JS, LLC.
25679  *
25680  * Originally Released Under LGPL - original licence link has changed is not relivant.
25681  *
25682  * Fork - LGPL
25683  * <script type="text/javascript">
25684  */
25685  
25686 /**
25687  * @class Roo.form.Layout
25688  * @extends Roo.Component
25689  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25690  * @constructor
25691  * @param {Object} config Configuration options
25692  */
25693 Roo.form.Layout = function(config){
25694     var xitems = [];
25695     if (config.items) {
25696         xitems = config.items;
25697         delete config.items;
25698     }
25699     Roo.form.Layout.superclass.constructor.call(this, config);
25700     this.stack = [];
25701     Roo.each(xitems, this.addxtype, this);
25702      
25703 };
25704
25705 Roo.extend(Roo.form.Layout, Roo.Component, {
25706     /**
25707      * @cfg {String/Object} autoCreate
25708      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25709      */
25710     /**
25711      * @cfg {String/Object/Function} style
25712      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25713      * a function which returns such a specification.
25714      */
25715     /**
25716      * @cfg {String} labelAlign
25717      * Valid values are "left," "top" and "right" (defaults to "left")
25718      */
25719     /**
25720      * @cfg {Number} labelWidth
25721      * Fixed width in pixels of all field labels (defaults to undefined)
25722      */
25723     /**
25724      * @cfg {Boolean} clear
25725      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25726      */
25727     clear : true,
25728     /**
25729      * @cfg {String} labelSeparator
25730      * The separator to use after field labels (defaults to ':')
25731      */
25732     labelSeparator : ':',
25733     /**
25734      * @cfg {Boolean} hideLabels
25735      * True to suppress the display of field labels in this layout (defaults to false)
25736      */
25737     hideLabels : false,
25738
25739     // private
25740     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25741     
25742     isLayout : true,
25743     
25744     // private
25745     onRender : function(ct, position){
25746         if(this.el){ // from markup
25747             this.el = Roo.get(this.el);
25748         }else {  // generate
25749             var cfg = this.getAutoCreate();
25750             this.el = ct.createChild(cfg, position);
25751         }
25752         if(this.style){
25753             this.el.applyStyles(this.style);
25754         }
25755         if(this.labelAlign){
25756             this.el.addClass('x-form-label-'+this.labelAlign);
25757         }
25758         if(this.hideLabels){
25759             this.labelStyle = "display:none";
25760             this.elementStyle = "padding-left:0;";
25761         }else{
25762             if(typeof this.labelWidth == 'number'){
25763                 this.labelStyle = "width:"+this.labelWidth+"px;";
25764                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25765             }
25766             if(this.labelAlign == 'top'){
25767                 this.labelStyle = "width:auto;";
25768                 this.elementStyle = "padding-left:0;";
25769             }
25770         }
25771         var stack = this.stack;
25772         var slen = stack.length;
25773         if(slen > 0){
25774             if(!this.fieldTpl){
25775                 var t = new Roo.Template(
25776                     '<div class="x-form-item {5}">',
25777                         '<label for="{0}" style="{2}">{1}{4}</label>',
25778                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25779                         '</div>',
25780                     '</div><div class="x-form-clear-left"></div>'
25781                 );
25782                 t.disableFormats = true;
25783                 t.compile();
25784                 Roo.form.Layout.prototype.fieldTpl = t;
25785             }
25786             for(var i = 0; i < slen; i++) {
25787                 if(stack[i].isFormField){
25788                     this.renderField(stack[i]);
25789                 }else{
25790                     this.renderComponent(stack[i]);
25791                 }
25792             }
25793         }
25794         if(this.clear){
25795             this.el.createChild({cls:'x-form-clear'});
25796         }
25797     },
25798
25799     // private
25800     renderField : function(f){
25801         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25802                f.id, //0
25803                f.fieldLabel, //1
25804                f.labelStyle||this.labelStyle||'', //2
25805                this.elementStyle||'', //3
25806                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25807                f.itemCls||this.itemCls||''  //5
25808        ], true).getPrevSibling());
25809     },
25810
25811     // private
25812     renderComponent : function(c){
25813         c.render(c.isLayout ? this.el : this.el.createChild());    
25814     },
25815     /**
25816      * Adds a object form elements (using the xtype property as the factory method.)
25817      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25818      * @param {Object} config 
25819      */
25820     addxtype : function(o)
25821     {
25822         // create the lement.
25823         o.form = this.form;
25824         var fe = Roo.factory(o, Roo.form);
25825         this.form.allItems.push(fe);
25826         this.stack.push(fe);
25827         
25828         if (fe.isFormField) {
25829             this.form.items.add(fe);
25830         }
25831          
25832         return fe;
25833     }
25834 });
25835
25836 /**
25837  * @class Roo.form.Column
25838  * @extends Roo.form.Layout
25839  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25840  * @constructor
25841  * @param {Object} config Configuration options
25842  */
25843 Roo.form.Column = function(config){
25844     Roo.form.Column.superclass.constructor.call(this, config);
25845 };
25846
25847 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25848     /**
25849      * @cfg {Number/String} width
25850      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25851      */
25852     /**
25853      * @cfg {String/Object} autoCreate
25854      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25855      */
25856
25857     // private
25858     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25859
25860     // private
25861     onRender : function(ct, position){
25862         Roo.form.Column.superclass.onRender.call(this, ct, position);
25863         if(this.width){
25864             this.el.setWidth(this.width);
25865         }
25866     }
25867 });
25868
25869
25870 /**
25871  * @class Roo.form.Row
25872  * @extends Roo.form.Layout
25873  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25874  * @constructor
25875  * @param {Object} config Configuration options
25876  */
25877
25878  
25879 Roo.form.Row = function(config){
25880     Roo.form.Row.superclass.constructor.call(this, config);
25881 };
25882  
25883 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25884       /**
25885      * @cfg {Number/String} width
25886      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25887      */
25888     /**
25889      * @cfg {Number/String} height
25890      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25891      */
25892     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25893     
25894     padWidth : 20,
25895     // private
25896     onRender : function(ct, position){
25897         //console.log('row render');
25898         if(!this.rowTpl){
25899             var t = new Roo.Template(
25900                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25901                     '<label for="{0}" style="{2}">{1}{4}</label>',
25902                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25903                     '</div>',
25904                 '</div>'
25905             );
25906             t.disableFormats = true;
25907             t.compile();
25908             Roo.form.Layout.prototype.rowTpl = t;
25909         }
25910         this.fieldTpl = this.rowTpl;
25911         
25912         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25913         var labelWidth = 100;
25914         
25915         if ((this.labelAlign != 'top')) {
25916             if (typeof this.labelWidth == 'number') {
25917                 labelWidth = this.labelWidth
25918             }
25919             this.padWidth =  20 + labelWidth;
25920             
25921         }
25922         
25923         Roo.form.Column.superclass.onRender.call(this, ct, position);
25924         if(this.width){
25925             this.el.setWidth(this.width);
25926         }
25927         if(this.height){
25928             this.el.setHeight(this.height);
25929         }
25930     },
25931     
25932     // private
25933     renderField : function(f){
25934         f.fieldEl = this.fieldTpl.append(this.el, [
25935                f.id, f.fieldLabel,
25936                f.labelStyle||this.labelStyle||'',
25937                this.elementStyle||'',
25938                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25939                f.itemCls||this.itemCls||'',
25940                f.width ? f.width + this.padWidth : 160 + this.padWidth
25941        ],true);
25942     }
25943 });
25944  
25945
25946 /**
25947  * @class Roo.form.FieldSet
25948  * @extends Roo.form.Layout
25949  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25950  * @constructor
25951  * @param {Object} config Configuration options
25952  */
25953 Roo.form.FieldSet = function(config){
25954     Roo.form.FieldSet.superclass.constructor.call(this, config);
25955 };
25956
25957 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25958     /**
25959      * @cfg {String} legend
25960      * The text to display as the legend for the FieldSet (defaults to '')
25961      */
25962     /**
25963      * @cfg {String/Object} autoCreate
25964      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25965      */
25966
25967     // private
25968     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25969
25970     // private
25971     onRender : function(ct, position){
25972         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25973         if(this.legend){
25974             this.setLegend(this.legend);
25975         }
25976     },
25977
25978     // private
25979     setLegend : function(text){
25980         if(this.rendered){
25981             this.el.child('legend').update(text);
25982         }
25983     }
25984 });/*
25985  * Based on:
25986  * Ext JS Library 1.1.1
25987  * Copyright(c) 2006-2007, Ext JS, LLC.
25988  *
25989  * Originally Released Under LGPL - original licence link has changed is not relivant.
25990  *
25991  * Fork - LGPL
25992  * <script type="text/javascript">
25993  */
25994 /**
25995  * @class Roo.form.VTypes
25996  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
25997  * @singleton
25998  */
25999 Roo.form.VTypes = function(){
26000     // closure these in so they are only created once.
26001     var alpha = /^[a-zA-Z_]+$/;
26002     var alphanum = /^[a-zA-Z0-9_]+$/;
26003     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26004     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26005
26006     // All these messages and functions are configurable
26007     return {
26008         /**
26009          * The function used to validate email addresses
26010          * @param {String} value The email address
26011          */
26012         'email' : function(v){
26013             return email.test(v);
26014         },
26015         /**
26016          * The error text to display when the email validation function returns false
26017          * @type String
26018          */
26019         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26020         /**
26021          * The keystroke filter mask to be applied on email input
26022          * @type RegExp
26023          */
26024         'emailMask' : /[a-z0-9_\.\-@]/i,
26025
26026         /**
26027          * The function used to validate URLs
26028          * @param {String} value The URL
26029          */
26030         'url' : function(v){
26031             return url.test(v);
26032         },
26033         /**
26034          * The error text to display when the url validation function returns false
26035          * @type String
26036          */
26037         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26038         
26039         /**
26040          * The function used to validate alpha values
26041          * @param {String} value The value
26042          */
26043         'alpha' : function(v){
26044             return alpha.test(v);
26045         },
26046         /**
26047          * The error text to display when the alpha validation function returns false
26048          * @type String
26049          */
26050         'alphaText' : 'This field should only contain letters and _',
26051         /**
26052          * The keystroke filter mask to be applied on alpha input
26053          * @type RegExp
26054          */
26055         'alphaMask' : /[a-z_]/i,
26056
26057         /**
26058          * The function used to validate alphanumeric values
26059          * @param {String} value The value
26060          */
26061         'alphanum' : function(v){
26062             return alphanum.test(v);
26063         },
26064         /**
26065          * The error text to display when the alphanumeric validation function returns false
26066          * @type String
26067          */
26068         'alphanumText' : 'This field should only contain letters, numbers and _',
26069         /**
26070          * The keystroke filter mask to be applied on alphanumeric input
26071          * @type RegExp
26072          */
26073         'alphanumMask' : /[a-z0-9_]/i
26074     };
26075 }();//<script type="text/javascript">
26076
26077 /**
26078  * @class Roo.form.FCKeditor
26079  * @extends Roo.form.TextArea
26080  * Wrapper around the FCKEditor http://www.fckeditor.net
26081  * @constructor
26082  * Creates a new FCKeditor
26083  * @param {Object} config Configuration options
26084  */
26085 Roo.form.FCKeditor = function(config){
26086     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26087     this.addEvents({
26088          /**
26089          * @event editorinit
26090          * Fired when the editor is initialized - you can add extra handlers here..
26091          * @param {FCKeditor} this
26092          * @param {Object} the FCK object.
26093          */
26094         editorinit : true
26095     });
26096     
26097     
26098 };
26099 Roo.form.FCKeditor.editors = { };
26100 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26101 {
26102     //defaultAutoCreate : {
26103     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26104     //},
26105     // private
26106     /**
26107      * @cfg {Object} fck options - see fck manual for details.
26108      */
26109     fckconfig : false,
26110     
26111     /**
26112      * @cfg {Object} fck toolbar set (Basic or Default)
26113      */
26114     toolbarSet : 'Basic',
26115     /**
26116      * @cfg {Object} fck BasePath
26117      */ 
26118     basePath : '/fckeditor/',
26119     
26120     
26121     frame : false,
26122     
26123     value : '',
26124     
26125    
26126     onRender : function(ct, position)
26127     {
26128         if(!this.el){
26129             this.defaultAutoCreate = {
26130                 tag: "textarea",
26131                 style:"width:300px;height:60px;",
26132                 autocomplete: "new-password"
26133             };
26134         }
26135         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26136         /*
26137         if(this.grow){
26138             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26139             if(this.preventScrollbars){
26140                 this.el.setStyle("overflow", "hidden");
26141             }
26142             this.el.setHeight(this.growMin);
26143         }
26144         */
26145         //console.log('onrender' + this.getId() );
26146         Roo.form.FCKeditor.editors[this.getId()] = this;
26147          
26148
26149         this.replaceTextarea() ;
26150         
26151     },
26152     
26153     getEditor : function() {
26154         return this.fckEditor;
26155     },
26156     /**
26157      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26158      * @param {Mixed} value The value to set
26159      */
26160     
26161     
26162     setValue : function(value)
26163     {
26164         //console.log('setValue: ' + value);
26165         
26166         if(typeof(value) == 'undefined') { // not sure why this is happending...
26167             return;
26168         }
26169         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26170         
26171         //if(!this.el || !this.getEditor()) {
26172         //    this.value = value;
26173             //this.setValue.defer(100,this,[value]);    
26174         //    return;
26175         //} 
26176         
26177         if(!this.getEditor()) {
26178             return;
26179         }
26180         
26181         this.getEditor().SetData(value);
26182         
26183         //
26184
26185     },
26186
26187     /**
26188      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26189      * @return {Mixed} value The field value
26190      */
26191     getValue : function()
26192     {
26193         
26194         if (this.frame && this.frame.dom.style.display == 'none') {
26195             return Roo.form.FCKeditor.superclass.getValue.call(this);
26196         }
26197         
26198         if(!this.el || !this.getEditor()) {
26199            
26200            // this.getValue.defer(100,this); 
26201             return this.value;
26202         }
26203        
26204         
26205         var value=this.getEditor().GetData();
26206         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26207         return Roo.form.FCKeditor.superclass.getValue.call(this);
26208         
26209
26210     },
26211
26212     /**
26213      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26214      * @return {Mixed} value The field value
26215      */
26216     getRawValue : function()
26217     {
26218         if (this.frame && this.frame.dom.style.display == 'none') {
26219             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26220         }
26221         
26222         if(!this.el || !this.getEditor()) {
26223             //this.getRawValue.defer(100,this); 
26224             return this.value;
26225             return;
26226         }
26227         
26228         
26229         
26230         var value=this.getEditor().GetData();
26231         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26232         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26233          
26234     },
26235     
26236     setSize : function(w,h) {
26237         
26238         
26239         
26240         //if (this.frame && this.frame.dom.style.display == 'none') {
26241         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26242         //    return;
26243         //}
26244         //if(!this.el || !this.getEditor()) {
26245         //    this.setSize.defer(100,this, [w,h]); 
26246         //    return;
26247         //}
26248         
26249         
26250         
26251         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26252         
26253         this.frame.dom.setAttribute('width', w);
26254         this.frame.dom.setAttribute('height', h);
26255         this.frame.setSize(w,h);
26256         
26257     },
26258     
26259     toggleSourceEdit : function(value) {
26260         
26261       
26262          
26263         this.el.dom.style.display = value ? '' : 'none';
26264         this.frame.dom.style.display = value ?  'none' : '';
26265         
26266     },
26267     
26268     
26269     focus: function(tag)
26270     {
26271         if (this.frame.dom.style.display == 'none') {
26272             return Roo.form.FCKeditor.superclass.focus.call(this);
26273         }
26274         if(!this.el || !this.getEditor()) {
26275             this.focus.defer(100,this, [tag]); 
26276             return;
26277         }
26278         
26279         
26280         
26281         
26282         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26283         this.getEditor().Focus();
26284         if (tgs.length) {
26285             if (!this.getEditor().Selection.GetSelection()) {
26286                 this.focus.defer(100,this, [tag]); 
26287                 return;
26288             }
26289             
26290             
26291             var r = this.getEditor().EditorDocument.createRange();
26292             r.setStart(tgs[0],0);
26293             r.setEnd(tgs[0],0);
26294             this.getEditor().Selection.GetSelection().removeAllRanges();
26295             this.getEditor().Selection.GetSelection().addRange(r);
26296             this.getEditor().Focus();
26297         }
26298         
26299     },
26300     
26301     
26302     
26303     replaceTextarea : function()
26304     {
26305         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26306             return ;
26307         }
26308         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26309         //{
26310             // We must check the elements firstly using the Id and then the name.
26311         var oTextarea = document.getElementById( this.getId() );
26312         
26313         var colElementsByName = document.getElementsByName( this.getId() ) ;
26314          
26315         oTextarea.style.display = 'none' ;
26316
26317         if ( oTextarea.tabIndex ) {            
26318             this.TabIndex = oTextarea.tabIndex ;
26319         }
26320         
26321         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26322         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26323         this.frame = Roo.get(this.getId() + '___Frame')
26324     },
26325     
26326     _getConfigHtml : function()
26327     {
26328         var sConfig = '' ;
26329
26330         for ( var o in this.fckconfig ) {
26331             sConfig += sConfig.length > 0  ? '&amp;' : '';
26332             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26333         }
26334
26335         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26336     },
26337     
26338     
26339     _getIFrameHtml : function()
26340     {
26341         var sFile = 'fckeditor.html' ;
26342         /* no idea what this is about..
26343         try
26344         {
26345             if ( (/fcksource=true/i).test( window.top.location.search ) )
26346                 sFile = 'fckeditor.original.html' ;
26347         }
26348         catch (e) { 
26349         */
26350
26351         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26352         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26353         
26354         
26355         var html = '<iframe id="' + this.getId() +
26356             '___Frame" src="' + sLink +
26357             '" width="' + this.width +
26358             '" height="' + this.height + '"' +
26359             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26360             ' frameborder="0" scrolling="no"></iframe>' ;
26361
26362         return html ;
26363     },
26364     
26365     _insertHtmlBefore : function( html, element )
26366     {
26367         if ( element.insertAdjacentHTML )       {
26368             // IE
26369             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26370         } else { // Gecko
26371             var oRange = document.createRange() ;
26372             oRange.setStartBefore( element ) ;
26373             var oFragment = oRange.createContextualFragment( html );
26374             element.parentNode.insertBefore( oFragment, element ) ;
26375         }
26376     }
26377     
26378     
26379   
26380     
26381     
26382     
26383     
26384
26385 });
26386
26387 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26388
26389 function FCKeditor_OnComplete(editorInstance){
26390     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26391     f.fckEditor = editorInstance;
26392     //console.log("loaded");
26393     f.fireEvent('editorinit', f, editorInstance);
26394
26395   
26396
26397  
26398
26399
26400
26401
26402
26403
26404
26405
26406
26407
26408
26409
26410
26411
26412
26413 //<script type="text/javascript">
26414 /**
26415  * @class Roo.form.GridField
26416  * @extends Roo.form.Field
26417  * Embed a grid (or editable grid into a form)
26418  * STATUS ALPHA
26419  * 
26420  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26421  * it needs 
26422  * xgrid.store = Roo.data.Store
26423  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26424  * xgrid.store.reader = Roo.data.JsonReader 
26425  * 
26426  * 
26427  * @constructor
26428  * Creates a new GridField
26429  * @param {Object} config Configuration options
26430  */
26431 Roo.form.GridField = function(config){
26432     Roo.form.GridField.superclass.constructor.call(this, config);
26433      
26434 };
26435
26436 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26437     /**
26438      * @cfg {Number} width  - used to restrict width of grid..
26439      */
26440     width : 100,
26441     /**
26442      * @cfg {Number} height - used to restrict height of grid..
26443      */
26444     height : 50,
26445      /**
26446      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26447          * 
26448          *}
26449      */
26450     xgrid : false, 
26451     /**
26452      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26453      * {tag: "input", type: "checkbox", autocomplete: "off"})
26454      */
26455    // defaultAutoCreate : { tag: 'div' },
26456     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26457     /**
26458      * @cfg {String} addTitle Text to include for adding a title.
26459      */
26460     addTitle : false,
26461     //
26462     onResize : function(){
26463         Roo.form.Field.superclass.onResize.apply(this, arguments);
26464     },
26465
26466     initEvents : function(){
26467         // Roo.form.Checkbox.superclass.initEvents.call(this);
26468         // has no events...
26469        
26470     },
26471
26472
26473     getResizeEl : function(){
26474         return this.wrap;
26475     },
26476
26477     getPositionEl : function(){
26478         return this.wrap;
26479     },
26480
26481     // private
26482     onRender : function(ct, position){
26483         
26484         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26485         var style = this.style;
26486         delete this.style;
26487         
26488         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26489         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26490         this.viewEl = this.wrap.createChild({ tag: 'div' });
26491         if (style) {
26492             this.viewEl.applyStyles(style);
26493         }
26494         if (this.width) {
26495             this.viewEl.setWidth(this.width);
26496         }
26497         if (this.height) {
26498             this.viewEl.setHeight(this.height);
26499         }
26500         //if(this.inputValue !== undefined){
26501         //this.setValue(this.value);
26502         
26503         
26504         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26505         
26506         
26507         this.grid.render();
26508         this.grid.getDataSource().on('remove', this.refreshValue, this);
26509         this.grid.getDataSource().on('update', this.refreshValue, this);
26510         this.grid.on('afteredit', this.refreshValue, this);
26511  
26512     },
26513      
26514     
26515     /**
26516      * Sets the value of the item. 
26517      * @param {String} either an object  or a string..
26518      */
26519     setValue : function(v){
26520         //this.value = v;
26521         v = v || []; // empty set..
26522         // this does not seem smart - it really only affects memoryproxy grids..
26523         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26524             var ds = this.grid.getDataSource();
26525             // assumes a json reader..
26526             var data = {}
26527             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26528             ds.loadData( data);
26529         }
26530         // clear selection so it does not get stale.
26531         if (this.grid.sm) { 
26532             this.grid.sm.clearSelections();
26533         }
26534         
26535         Roo.form.GridField.superclass.setValue.call(this, v);
26536         this.refreshValue();
26537         // should load data in the grid really....
26538     },
26539     
26540     // private
26541     refreshValue: function() {
26542          var val = [];
26543         this.grid.getDataSource().each(function(r) {
26544             val.push(r.data);
26545         });
26546         this.el.dom.value = Roo.encode(val);
26547     }
26548     
26549      
26550     
26551     
26552 });/*
26553  * Based on:
26554  * Ext JS Library 1.1.1
26555  * Copyright(c) 2006-2007, Ext JS, LLC.
26556  *
26557  * Originally Released Under LGPL - original licence link has changed is not relivant.
26558  *
26559  * Fork - LGPL
26560  * <script type="text/javascript">
26561  */
26562 /**
26563  * @class Roo.form.DisplayField
26564  * @extends Roo.form.Field
26565  * A generic Field to display non-editable data.
26566  * @cfg {Boolean} closable (true|false) default false
26567  * @constructor
26568  * Creates a new Display Field item.
26569  * @param {Object} config Configuration options
26570  */
26571 Roo.form.DisplayField = function(config){
26572     Roo.form.DisplayField.superclass.constructor.call(this, config);
26573     
26574     this.addEvents({
26575         /**
26576          * @event close
26577          * Fires after the click the close btn
26578              * @param {Roo.form.DisplayField} this
26579              */
26580         close : true
26581     });
26582 };
26583
26584 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26585     inputType:      'hidden',
26586     allowBlank:     true,
26587     readOnly:         true,
26588     
26589  
26590     /**
26591      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26592      */
26593     focusClass : undefined,
26594     /**
26595      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26596      */
26597     fieldClass: 'x-form-field',
26598     
26599      /**
26600      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26601      */
26602     valueRenderer: undefined,
26603     
26604     width: 100,
26605     /**
26606      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26607      * {tag: "input", type: "checkbox", autocomplete: "off"})
26608      */
26609      
26610  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26611  
26612     closable : false,
26613     
26614     onResize : function(){
26615         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26616         
26617     },
26618
26619     initEvents : function(){
26620         // Roo.form.Checkbox.superclass.initEvents.call(this);
26621         // has no events...
26622         
26623         if(this.closable){
26624             this.closeEl.on('click', this.onClose, this);
26625         }
26626        
26627     },
26628
26629
26630     getResizeEl : function(){
26631         return this.wrap;
26632     },
26633
26634     getPositionEl : function(){
26635         return this.wrap;
26636     },
26637
26638     // private
26639     onRender : function(ct, position){
26640         
26641         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26642         //if(this.inputValue !== undefined){
26643         this.wrap = this.el.wrap();
26644         
26645         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26646         
26647         if(this.closable){
26648             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26649         }
26650         
26651         if (this.bodyStyle) {
26652             this.viewEl.applyStyles(this.bodyStyle);
26653         }
26654         //this.viewEl.setStyle('padding', '2px');
26655         
26656         this.setValue(this.value);
26657         
26658     },
26659 /*
26660     // private
26661     initValue : Roo.emptyFn,
26662
26663   */
26664
26665         // private
26666     onClick : function(){
26667         
26668     },
26669
26670     /**
26671      * Sets the checked state of the checkbox.
26672      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26673      */
26674     setValue : function(v){
26675         this.value = v;
26676         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26677         // this might be called before we have a dom element..
26678         if (!this.viewEl) {
26679             return;
26680         }
26681         this.viewEl.dom.innerHTML = html;
26682         Roo.form.DisplayField.superclass.setValue.call(this, v);
26683
26684     },
26685     
26686     onClose : function(e)
26687     {
26688         e.preventDefault();
26689         
26690         this.fireEvent('close', this);
26691     }
26692 });/*
26693  * 
26694  * Licence- LGPL
26695  * 
26696  */
26697
26698 /**
26699  * @class Roo.form.DayPicker
26700  * @extends Roo.form.Field
26701  * A Day picker show [M] [T] [W] ....
26702  * @constructor
26703  * Creates a new Day Picker
26704  * @param {Object} config Configuration options
26705  */
26706 Roo.form.DayPicker= function(config){
26707     Roo.form.DayPicker.superclass.constructor.call(this, config);
26708      
26709 };
26710
26711 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26712     /**
26713      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26714      */
26715     focusClass : undefined,
26716     /**
26717      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26718      */
26719     fieldClass: "x-form-field",
26720    
26721     /**
26722      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26723      * {tag: "input", type: "checkbox", autocomplete: "off"})
26724      */
26725     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26726     
26727    
26728     actionMode : 'viewEl', 
26729     //
26730     // private
26731  
26732     inputType : 'hidden',
26733     
26734      
26735     inputElement: false, // real input element?
26736     basedOn: false, // ????
26737     
26738     isFormField: true, // not sure where this is needed!!!!
26739
26740     onResize : function(){
26741         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26742         if(!this.boxLabel){
26743             this.el.alignTo(this.wrap, 'c-c');
26744         }
26745     },
26746
26747     initEvents : function(){
26748         Roo.form.Checkbox.superclass.initEvents.call(this);
26749         this.el.on("click", this.onClick,  this);
26750         this.el.on("change", this.onClick,  this);
26751     },
26752
26753
26754     getResizeEl : function(){
26755         return this.wrap;
26756     },
26757
26758     getPositionEl : function(){
26759         return this.wrap;
26760     },
26761
26762     
26763     // private
26764     onRender : function(ct, position){
26765         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26766        
26767         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26768         
26769         var r1 = '<table><tr>';
26770         var r2 = '<tr class="x-form-daypick-icons">';
26771         for (var i=0; i < 7; i++) {
26772             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26773             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26774         }
26775         
26776         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26777         viewEl.select('img').on('click', this.onClick, this);
26778         this.viewEl = viewEl;   
26779         
26780         
26781         // this will not work on Chrome!!!
26782         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26783         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26784         
26785         
26786           
26787
26788     },
26789
26790     // private
26791     initValue : Roo.emptyFn,
26792
26793     /**
26794      * Returns the checked state of the checkbox.
26795      * @return {Boolean} True if checked, else false
26796      */
26797     getValue : function(){
26798         return this.el.dom.value;
26799         
26800     },
26801
26802         // private
26803     onClick : function(e){ 
26804         //this.setChecked(!this.checked);
26805         Roo.get(e.target).toggleClass('x-menu-item-checked');
26806         this.refreshValue();
26807         //if(this.el.dom.checked != this.checked){
26808         //    this.setValue(this.el.dom.checked);
26809        // }
26810     },
26811     
26812     // private
26813     refreshValue : function()
26814     {
26815         var val = '';
26816         this.viewEl.select('img',true).each(function(e,i,n)  {
26817             val += e.is(".x-menu-item-checked") ? String(n) : '';
26818         });
26819         this.setValue(val, true);
26820     },
26821
26822     /**
26823      * Sets the checked state of the checkbox.
26824      * On is always based on a string comparison between inputValue and the param.
26825      * @param {Boolean/String} value - the value to set 
26826      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26827      */
26828     setValue : function(v,suppressEvent){
26829         if (!this.el.dom) {
26830             return;
26831         }
26832         var old = this.el.dom.value ;
26833         this.el.dom.value = v;
26834         if (suppressEvent) {
26835             return ;
26836         }
26837          
26838         // update display..
26839         this.viewEl.select('img',true).each(function(e,i,n)  {
26840             
26841             var on = e.is(".x-menu-item-checked");
26842             var newv = v.indexOf(String(n)) > -1;
26843             if (on != newv) {
26844                 e.toggleClass('x-menu-item-checked');
26845             }
26846             
26847         });
26848         
26849         
26850         this.fireEvent('change', this, v, old);
26851         
26852         
26853     },
26854    
26855     // handle setting of hidden value by some other method!!?!?
26856     setFromHidden: function()
26857     {
26858         if(!this.el){
26859             return;
26860         }
26861         //console.log("SET FROM HIDDEN");
26862         //alert('setFrom hidden');
26863         this.setValue(this.el.dom.value);
26864     },
26865     
26866     onDestroy : function()
26867     {
26868         if(this.viewEl){
26869             Roo.get(this.viewEl).remove();
26870         }
26871          
26872         Roo.form.DayPicker.superclass.onDestroy.call(this);
26873     }
26874
26875 });/*
26876  * RooJS Library 1.1.1
26877  * Copyright(c) 2008-2011  Alan Knowles
26878  *
26879  * License - LGPL
26880  */
26881  
26882
26883 /**
26884  * @class Roo.form.ComboCheck
26885  * @extends Roo.form.ComboBox
26886  * A combobox for multiple select items.
26887  *
26888  * FIXME - could do with a reset button..
26889  * 
26890  * @constructor
26891  * Create a new ComboCheck
26892  * @param {Object} config Configuration options
26893  */
26894 Roo.form.ComboCheck = function(config){
26895     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26896     // should verify some data...
26897     // like
26898     // hiddenName = required..
26899     // displayField = required
26900     // valudField == required
26901     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26902     var _t = this;
26903     Roo.each(req, function(e) {
26904         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26905             throw "Roo.form.ComboCheck : missing value for: " + e;
26906         }
26907     });
26908     
26909     
26910 };
26911
26912 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26913      
26914      
26915     editable : false,
26916      
26917     selectedClass: 'x-menu-item-checked', 
26918     
26919     // private
26920     onRender : function(ct, position){
26921         var _t = this;
26922         
26923         
26924         
26925         if(!this.tpl){
26926             var cls = 'x-combo-list';
26927
26928             
26929             this.tpl =  new Roo.Template({
26930                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26931                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26932                    '<span>{' + this.displayField + '}</span>' +
26933                     '</div>' 
26934                 
26935             });
26936         }
26937  
26938         
26939         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26940         this.view.singleSelect = false;
26941         this.view.multiSelect = true;
26942         this.view.toggleSelect = true;
26943         this.pageTb.add(new Roo.Toolbar.Fill(), {
26944             
26945             text: 'Done',
26946             handler: function()
26947             {
26948                 _t.collapse();
26949             }
26950         });
26951     },
26952     
26953     onViewOver : function(e, t){
26954         // do nothing...
26955         return;
26956         
26957     },
26958     
26959     onViewClick : function(doFocus,index){
26960         return;
26961         
26962     },
26963     select: function () {
26964         //Roo.log("SELECT CALLED");
26965     },
26966      
26967     selectByValue : function(xv, scrollIntoView){
26968         var ar = this.getValueArray();
26969         var sels = [];
26970         
26971         Roo.each(ar, function(v) {
26972             if(v === undefined || v === null){
26973                 return;
26974             }
26975             var r = this.findRecord(this.valueField, v);
26976             if(r){
26977                 sels.push(this.store.indexOf(r))
26978                 
26979             }
26980         },this);
26981         this.view.select(sels);
26982         return false;
26983     },
26984     
26985     
26986     
26987     onSelect : function(record, index){
26988        // Roo.log("onselect Called");
26989        // this is only called by the clear button now..
26990         this.view.clearSelections();
26991         this.setValue('[]');
26992         if (this.value != this.valueBefore) {
26993             this.fireEvent('change', this, this.value, this.valueBefore);
26994             this.valueBefore = this.value;
26995         }
26996     },
26997     getValueArray : function()
26998     {
26999         var ar = [] ;
27000         
27001         try {
27002             //Roo.log(this.value);
27003             if (typeof(this.value) == 'undefined') {
27004                 return [];
27005             }
27006             var ar = Roo.decode(this.value);
27007             return  ar instanceof Array ? ar : []; //?? valid?
27008             
27009         } catch(e) {
27010             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27011             return [];
27012         }
27013          
27014     },
27015     expand : function ()
27016     {
27017         
27018         Roo.form.ComboCheck.superclass.expand.call(this);
27019         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27020         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27021         
27022
27023     },
27024     
27025     collapse : function(){
27026         Roo.form.ComboCheck.superclass.collapse.call(this);
27027         var sl = this.view.getSelectedIndexes();
27028         var st = this.store;
27029         var nv = [];
27030         var tv = [];
27031         var r;
27032         Roo.each(sl, function(i) {
27033             r = st.getAt(i);
27034             nv.push(r.get(this.valueField));
27035         },this);
27036         this.setValue(Roo.encode(nv));
27037         if (this.value != this.valueBefore) {
27038
27039             this.fireEvent('change', this, this.value, this.valueBefore);
27040             this.valueBefore = this.value;
27041         }
27042         
27043     },
27044     
27045     setValue : function(v){
27046         // Roo.log(v);
27047         this.value = v;
27048         
27049         var vals = this.getValueArray();
27050         var tv = [];
27051         Roo.each(vals, function(k) {
27052             var r = this.findRecord(this.valueField, k);
27053             if(r){
27054                 tv.push(r.data[this.displayField]);
27055             }else if(this.valueNotFoundText !== undefined){
27056                 tv.push( this.valueNotFoundText );
27057             }
27058         },this);
27059        // Roo.log(tv);
27060         
27061         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27062         this.hiddenField.value = v;
27063         this.value = v;
27064     }
27065     
27066 });/*
27067  * Based on:
27068  * Ext JS Library 1.1.1
27069  * Copyright(c) 2006-2007, Ext JS, LLC.
27070  *
27071  * Originally Released Under LGPL - original licence link has changed is not relivant.
27072  *
27073  * Fork - LGPL
27074  * <script type="text/javascript">
27075  */
27076  
27077 /**
27078  * @class Roo.form.Signature
27079  * @extends Roo.form.Field
27080  * Signature field.  
27081  * @constructor
27082  * 
27083  * @param {Object} config Configuration options
27084  */
27085
27086 Roo.form.Signature = function(config){
27087     Roo.form.Signature.superclass.constructor.call(this, config);
27088     
27089     this.addEvents({// not in used??
27090          /**
27091          * @event confirm
27092          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27093              * @param {Roo.form.Signature} combo This combo box
27094              */
27095         'confirm' : true,
27096         /**
27097          * @event reset
27098          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27099              * @param {Roo.form.ComboBox} combo This combo box
27100              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27101              */
27102         'reset' : true
27103     });
27104 };
27105
27106 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27107     /**
27108      * @cfg {Object} labels Label to use when rendering a form.
27109      * defaults to 
27110      * labels : { 
27111      *      clear : "Clear",
27112      *      confirm : "Confirm"
27113      *  }
27114      */
27115     labels : { 
27116         clear : "Clear",
27117         confirm : "Confirm"
27118     },
27119     /**
27120      * @cfg {Number} width The signature panel width (defaults to 300)
27121      */
27122     width: 300,
27123     /**
27124      * @cfg {Number} height The signature panel height (defaults to 100)
27125      */
27126     height : 100,
27127     /**
27128      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27129      */
27130     allowBlank : false,
27131     
27132     //private
27133     // {Object} signPanel The signature SVG panel element (defaults to {})
27134     signPanel : {},
27135     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27136     isMouseDown : false,
27137     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27138     isConfirmed : false,
27139     // {String} signatureTmp SVG mapping string (defaults to empty string)
27140     signatureTmp : '',
27141     
27142     
27143     defaultAutoCreate : { // modified by initCompnoent..
27144         tag: "input",
27145         type:"hidden"
27146     },
27147
27148     // private
27149     onRender : function(ct, position){
27150         
27151         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27152         
27153         this.wrap = this.el.wrap({
27154             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27155         });
27156         
27157         this.createToolbar(this);
27158         this.signPanel = this.wrap.createChild({
27159                 tag: 'div',
27160                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27161             }, this.el
27162         );
27163             
27164         this.svgID = Roo.id();
27165         this.svgEl = this.signPanel.createChild({
27166               xmlns : 'http://www.w3.org/2000/svg',
27167               tag : 'svg',
27168               id : this.svgID + "-svg",
27169               width: this.width,
27170               height: this.height,
27171               viewBox: '0 0 '+this.width+' '+this.height,
27172               cn : [
27173                 {
27174                     tag: "rect",
27175                     id: this.svgID + "-svg-r",
27176                     width: this.width,
27177                     height: this.height,
27178                     fill: "#ffa"
27179                 },
27180                 {
27181                     tag: "line",
27182                     id: this.svgID + "-svg-l",
27183                     x1: "0", // start
27184                     y1: (this.height*0.8), // start set the line in 80% of height
27185                     x2: this.width, // end
27186                     y2: (this.height*0.8), // end set the line in 80% of height
27187                     'stroke': "#666",
27188                     'stroke-width': "1",
27189                     'stroke-dasharray': "3",
27190                     'shape-rendering': "crispEdges",
27191                     'pointer-events': "none"
27192                 },
27193                 {
27194                     tag: "path",
27195                     id: this.svgID + "-svg-p",
27196                     'stroke': "navy",
27197                     'stroke-width': "3",
27198                     'fill': "none",
27199                     'pointer-events': 'none'
27200                 }
27201               ]
27202         });
27203         this.createSVG();
27204         this.svgBox = this.svgEl.dom.getScreenCTM();
27205     },
27206     createSVG : function(){ 
27207         var svg = this.signPanel;
27208         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27209         var t = this;
27210
27211         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27212         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27213         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27214         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27215         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27216         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27217         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27218         
27219     },
27220     isTouchEvent : function(e){
27221         return e.type.match(/^touch/);
27222     },
27223     getCoords : function (e) {
27224         var pt    = this.svgEl.dom.createSVGPoint();
27225         pt.x = e.clientX; 
27226         pt.y = e.clientY;
27227         if (this.isTouchEvent(e)) {
27228             pt.x =  e.targetTouches[0].clientX;
27229             pt.y = e.targetTouches[0].clientY;
27230         }
27231         var a = this.svgEl.dom.getScreenCTM();
27232         var b = a.inverse();
27233         var mx = pt.matrixTransform(b);
27234         return mx.x + ',' + mx.y;
27235     },
27236     //mouse event headler 
27237     down : function (e) {
27238         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27239         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27240         
27241         this.isMouseDown = true;
27242         
27243         e.preventDefault();
27244     },
27245     move : function (e) {
27246         if (this.isMouseDown) {
27247             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27248             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27249         }
27250         
27251         e.preventDefault();
27252     },
27253     up : function (e) {
27254         this.isMouseDown = false;
27255         var sp = this.signatureTmp.split(' ');
27256         
27257         if(sp.length > 1){
27258             if(!sp[sp.length-2].match(/^L/)){
27259                 sp.pop();
27260                 sp.pop();
27261                 sp.push("");
27262                 this.signatureTmp = sp.join(" ");
27263             }
27264         }
27265         if(this.getValue() != this.signatureTmp){
27266             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27267             this.isConfirmed = false;
27268         }
27269         e.preventDefault();
27270     },
27271     
27272     /**
27273      * Protected method that will not generally be called directly. It
27274      * is called when the editor creates its toolbar. Override this method if you need to
27275      * add custom toolbar buttons.
27276      * @param {HtmlEditor} editor
27277      */
27278     createToolbar : function(editor){
27279          function btn(id, toggle, handler){
27280             var xid = fid + '-'+ id ;
27281             return {
27282                 id : xid,
27283                 cmd : id,
27284                 cls : 'x-btn-icon x-edit-'+id,
27285                 enableToggle:toggle !== false,
27286                 scope: editor, // was editor...
27287                 handler:handler||editor.relayBtnCmd,
27288                 clickEvent:'mousedown',
27289                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27290                 tabIndex:-1
27291             };
27292         }
27293         
27294         
27295         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27296         this.tb = tb;
27297         this.tb.add(
27298            {
27299                 cls : ' x-signature-btn x-signature-'+id,
27300                 scope: editor, // was editor...
27301                 handler: this.reset,
27302                 clickEvent:'mousedown',
27303                 text: this.labels.clear
27304             },
27305             {
27306                  xtype : 'Fill',
27307                  xns: Roo.Toolbar
27308             }, 
27309             {
27310                 cls : '  x-signature-btn x-signature-'+id,
27311                 scope: editor, // was editor...
27312                 handler: this.confirmHandler,
27313                 clickEvent:'mousedown',
27314                 text: this.labels.confirm
27315             }
27316         );
27317     
27318     },
27319     //public
27320     /**
27321      * when user is clicked confirm then show this image.....
27322      * 
27323      * @return {String} Image Data URI
27324      */
27325     getImageDataURI : function(){
27326         var svg = this.svgEl.dom.parentNode.innerHTML;
27327         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27328         return src; 
27329     },
27330     /**
27331      * 
27332      * @return {Boolean} this.isConfirmed
27333      */
27334     getConfirmed : function(){
27335         return this.isConfirmed;
27336     },
27337     /**
27338      * 
27339      * @return {Number} this.width
27340      */
27341     getWidth : function(){
27342         return this.width;
27343     },
27344     /**
27345      * 
27346      * @return {Number} this.height
27347      */
27348     getHeight : function(){
27349         return this.height;
27350     },
27351     // private
27352     getSignature : function(){
27353         return this.signatureTmp;
27354     },
27355     // private
27356     reset : function(){
27357         this.signatureTmp = '';
27358         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27359         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27360         this.isConfirmed = false;
27361         Roo.form.Signature.superclass.reset.call(this);
27362     },
27363     setSignature : function(s){
27364         this.signatureTmp = s;
27365         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27366         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27367         this.setValue(s);
27368         this.isConfirmed = false;
27369         Roo.form.Signature.superclass.reset.call(this);
27370     }, 
27371     test : function(){
27372 //        Roo.log(this.signPanel.dom.contentWindow.up())
27373     },
27374     //private
27375     setConfirmed : function(){
27376         
27377         
27378         
27379 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27380     },
27381     // private
27382     confirmHandler : function(){
27383         if(!this.getSignature()){
27384             return;
27385         }
27386         
27387         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27388         this.setValue(this.getSignature());
27389         this.isConfirmed = true;
27390         
27391         this.fireEvent('confirm', this);
27392     },
27393     // private
27394     // Subclasses should provide the validation implementation by overriding this
27395     validateValue : function(value){
27396         if(this.allowBlank){
27397             return true;
27398         }
27399         
27400         if(this.isConfirmed){
27401             return true;
27402         }
27403         return false;
27404     }
27405 });/*
27406  * Based on:
27407  * Ext JS Library 1.1.1
27408  * Copyright(c) 2006-2007, Ext JS, LLC.
27409  *
27410  * Originally Released Under LGPL - original licence link has changed is not relivant.
27411  *
27412  * Fork - LGPL
27413  * <script type="text/javascript">
27414  */
27415  
27416
27417 /**
27418  * @class Roo.form.ComboBox
27419  * @extends Roo.form.TriggerField
27420  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27421  * @constructor
27422  * Create a new ComboBox.
27423  * @param {Object} config Configuration options
27424  */
27425 Roo.form.Select = function(config){
27426     Roo.form.Select.superclass.constructor.call(this, config);
27427      
27428 };
27429
27430 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27431     /**
27432      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27433      */
27434     /**
27435      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27436      * rendering into an Roo.Editor, defaults to false)
27437      */
27438     /**
27439      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27440      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27441      */
27442     /**
27443      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27444      */
27445     /**
27446      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27447      * the dropdown list (defaults to undefined, with no header element)
27448      */
27449
27450      /**
27451      * @cfg {String/Roo.Template} tpl The template to use to render the output
27452      */
27453      
27454     // private
27455     defaultAutoCreate : {tag: "select"  },
27456     /**
27457      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27458      */
27459     listWidth: undefined,
27460     /**
27461      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27462      * mode = 'remote' or 'text' if mode = 'local')
27463      */
27464     displayField: undefined,
27465     /**
27466      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27467      * mode = 'remote' or 'value' if mode = 'local'). 
27468      * Note: use of a valueField requires the user make a selection
27469      * in order for a value to be mapped.
27470      */
27471     valueField: undefined,
27472     
27473     
27474     /**
27475      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27476      * field's data value (defaults to the underlying DOM element's name)
27477      */
27478     hiddenName: undefined,
27479     /**
27480      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27481      */
27482     listClass: '',
27483     /**
27484      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27485      */
27486     selectedClass: 'x-combo-selected',
27487     /**
27488      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27489      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27490      * which displays a downward arrow icon).
27491      */
27492     triggerClass : 'x-form-arrow-trigger',
27493     /**
27494      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27495      */
27496     shadow:'sides',
27497     /**
27498      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27499      * anchor positions (defaults to 'tl-bl')
27500      */
27501     listAlign: 'tl-bl?',
27502     /**
27503      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27504      */
27505     maxHeight: 300,
27506     /**
27507      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27508      * query specified by the allQuery config option (defaults to 'query')
27509      */
27510     triggerAction: 'query',
27511     /**
27512      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27513      * (defaults to 4, does not apply if editable = false)
27514      */
27515     minChars : 4,
27516     /**
27517      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27518      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27519      */
27520     typeAhead: false,
27521     /**
27522      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27523      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27524      */
27525     queryDelay: 500,
27526     /**
27527      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27528      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27529      */
27530     pageSize: 0,
27531     /**
27532      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27533      * when editable = true (defaults to false)
27534      */
27535     selectOnFocus:false,
27536     /**
27537      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27538      */
27539     queryParam: 'query',
27540     /**
27541      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27542      * when mode = 'remote' (defaults to 'Loading...')
27543      */
27544     loadingText: 'Loading...',
27545     /**
27546      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27547      */
27548     resizable: false,
27549     /**
27550      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27551      */
27552     handleHeight : 8,
27553     /**
27554      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27555      * traditional select (defaults to true)
27556      */
27557     editable: true,
27558     /**
27559      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27560      */
27561     allQuery: '',
27562     /**
27563      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27564      */
27565     mode: 'remote',
27566     /**
27567      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27568      * listWidth has a higher value)
27569      */
27570     minListWidth : 70,
27571     /**
27572      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27573      * allow the user to set arbitrary text into the field (defaults to false)
27574      */
27575     forceSelection:false,
27576     /**
27577      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27578      * if typeAhead = true (defaults to 250)
27579      */
27580     typeAheadDelay : 250,
27581     /**
27582      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27583      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27584      */
27585     valueNotFoundText : undefined,
27586     
27587     /**
27588      * @cfg {String} defaultValue The value displayed after loading the store.
27589      */
27590     defaultValue: '',
27591     
27592     /**
27593      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27594      */
27595     blockFocus : false,
27596     
27597     /**
27598      * @cfg {Boolean} disableClear Disable showing of clear button.
27599      */
27600     disableClear : false,
27601     /**
27602      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27603      */
27604     alwaysQuery : false,
27605     
27606     //private
27607     addicon : false,
27608     editicon: false,
27609     
27610     // element that contains real text value.. (when hidden is used..)
27611      
27612     // private
27613     onRender : function(ct, position){
27614         Roo.form.Field.prototype.onRender.call(this, ct, position);
27615         
27616         if(this.store){
27617             this.store.on('beforeload', this.onBeforeLoad, this);
27618             this.store.on('load', this.onLoad, this);
27619             this.store.on('loadexception', this.onLoadException, this);
27620             this.store.load({});
27621         }
27622         
27623         
27624         
27625     },
27626
27627     // private
27628     initEvents : function(){
27629         //Roo.form.ComboBox.superclass.initEvents.call(this);
27630  
27631     },
27632
27633     onDestroy : function(){
27634        
27635         if(this.store){
27636             this.store.un('beforeload', this.onBeforeLoad, this);
27637             this.store.un('load', this.onLoad, this);
27638             this.store.un('loadexception', this.onLoadException, this);
27639         }
27640         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27641     },
27642
27643     // private
27644     fireKey : function(e){
27645         if(e.isNavKeyPress() && !this.list.isVisible()){
27646             this.fireEvent("specialkey", this, e);
27647         }
27648     },
27649
27650     // private
27651     onResize: function(w, h){
27652         
27653         return; 
27654     
27655         
27656     },
27657
27658     /**
27659      * Allow or prevent the user from directly editing the field text.  If false is passed,
27660      * the user will only be able to select from the items defined in the dropdown list.  This method
27661      * is the runtime equivalent of setting the 'editable' config option at config time.
27662      * @param {Boolean} value True to allow the user to directly edit the field text
27663      */
27664     setEditable : function(value){
27665          
27666     },
27667
27668     // private
27669     onBeforeLoad : function(){
27670         
27671         Roo.log("Select before load");
27672         return;
27673     
27674         this.innerList.update(this.loadingText ?
27675                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27676         //this.restrictHeight();
27677         this.selectedIndex = -1;
27678     },
27679
27680     // private
27681     onLoad : function(){
27682
27683     
27684         var dom = this.el.dom;
27685         dom.innerHTML = '';
27686          var od = dom.ownerDocument;
27687          
27688         if (this.emptyText) {
27689             var op = od.createElement('option');
27690             op.setAttribute('value', '');
27691             op.innerHTML = String.format('{0}', this.emptyText);
27692             dom.appendChild(op);
27693         }
27694         if(this.store.getCount() > 0){
27695            
27696             var vf = this.valueField;
27697             var df = this.displayField;
27698             this.store.data.each(function(r) {
27699                 // which colmsn to use... testing - cdoe / title..
27700                 var op = od.createElement('option');
27701                 op.setAttribute('value', r.data[vf]);
27702                 op.innerHTML = String.format('{0}', r.data[df]);
27703                 dom.appendChild(op);
27704             });
27705             if (typeof(this.defaultValue != 'undefined')) {
27706                 this.setValue(this.defaultValue);
27707             }
27708             
27709              
27710         }else{
27711             //this.onEmptyResults();
27712         }
27713         //this.el.focus();
27714     },
27715     // private
27716     onLoadException : function()
27717     {
27718         dom.innerHTML = '';
27719             
27720         Roo.log("Select on load exception");
27721         return;
27722     
27723         this.collapse();
27724         Roo.log(this.store.reader.jsonData);
27725         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27726             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27727         }
27728         
27729         
27730     },
27731     // private
27732     onTypeAhead : function(){
27733          
27734     },
27735
27736     // private
27737     onSelect : function(record, index){
27738         Roo.log('on select?');
27739         return;
27740         if(this.fireEvent('beforeselect', this, record, index) !== false){
27741             this.setFromData(index > -1 ? record.data : false);
27742             this.collapse();
27743             this.fireEvent('select', this, record, index);
27744         }
27745     },
27746
27747     /**
27748      * Returns the currently selected field value or empty string if no value is set.
27749      * @return {String} value The selected value
27750      */
27751     getValue : function(){
27752         var dom = this.el.dom;
27753         this.value = dom.options[dom.selectedIndex].value;
27754         return this.value;
27755         
27756     },
27757
27758     /**
27759      * Clears any text/value currently set in the field
27760      */
27761     clearValue : function(){
27762         this.value = '';
27763         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27764         
27765     },
27766
27767     /**
27768      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27769      * will be displayed in the field.  If the value does not match the data value of an existing item,
27770      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27771      * Otherwise the field will be blank (although the value will still be set).
27772      * @param {String} value The value to match
27773      */
27774     setValue : function(v){
27775         var d = this.el.dom;
27776         for (var i =0; i < d.options.length;i++) {
27777             if (v == d.options[i].value) {
27778                 d.selectedIndex = i;
27779                 this.value = v;
27780                 return;
27781             }
27782         }
27783         this.clearValue();
27784     },
27785     /**
27786      * @property {Object} the last set data for the element
27787      */
27788     
27789     lastData : false,
27790     /**
27791      * Sets the value of the field based on a object which is related to the record format for the store.
27792      * @param {Object} value the value to set as. or false on reset?
27793      */
27794     setFromData : function(o){
27795         Roo.log('setfrom data?');
27796          
27797         
27798         
27799     },
27800     // private
27801     reset : function(){
27802         this.clearValue();
27803     },
27804     // private
27805     findRecord : function(prop, value){
27806         
27807         return false;
27808     
27809         var record;
27810         if(this.store.getCount() > 0){
27811             this.store.each(function(r){
27812                 if(r.data[prop] == value){
27813                     record = r;
27814                     return false;
27815                 }
27816                 return true;
27817             });
27818         }
27819         return record;
27820     },
27821     
27822     getName: function()
27823     {
27824         // returns hidden if it's set..
27825         if (!this.rendered) {return ''};
27826         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27827         
27828     },
27829      
27830
27831     
27832
27833     // private
27834     onEmptyResults : function(){
27835         Roo.log('empty results');
27836         //this.collapse();
27837     },
27838
27839     /**
27840      * Returns true if the dropdown list is expanded, else false.
27841      */
27842     isExpanded : function(){
27843         return false;
27844     },
27845
27846     /**
27847      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27848      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27849      * @param {String} value The data value of the item to select
27850      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27851      * selected item if it is not currently in view (defaults to true)
27852      * @return {Boolean} True if the value matched an item in the list, else false
27853      */
27854     selectByValue : function(v, scrollIntoView){
27855         Roo.log('select By Value');
27856         return false;
27857     
27858         if(v !== undefined && v !== null){
27859             var r = this.findRecord(this.valueField || this.displayField, v);
27860             if(r){
27861                 this.select(this.store.indexOf(r), scrollIntoView);
27862                 return true;
27863             }
27864         }
27865         return false;
27866     },
27867
27868     /**
27869      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27870      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27871      * @param {Number} index The zero-based index of the list item to select
27872      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27873      * selected item if it is not currently in view (defaults to true)
27874      */
27875     select : function(index, scrollIntoView){
27876         Roo.log('select ');
27877         return  ;
27878         
27879         this.selectedIndex = index;
27880         this.view.select(index);
27881         if(scrollIntoView !== false){
27882             var el = this.view.getNode(index);
27883             if(el){
27884                 this.innerList.scrollChildIntoView(el, false);
27885             }
27886         }
27887     },
27888
27889       
27890
27891     // private
27892     validateBlur : function(){
27893         
27894         return;
27895         
27896     },
27897
27898     // private
27899     initQuery : function(){
27900         this.doQuery(this.getRawValue());
27901     },
27902
27903     // private
27904     doForce : function(){
27905         if(this.el.dom.value.length > 0){
27906             this.el.dom.value =
27907                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27908              
27909         }
27910     },
27911
27912     /**
27913      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27914      * query allowing the query action to be canceled if needed.
27915      * @param {String} query The SQL query to execute
27916      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27917      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27918      * saved in the current store (defaults to false)
27919      */
27920     doQuery : function(q, forceAll){
27921         
27922         Roo.log('doQuery?');
27923         if(q === undefined || q === null){
27924             q = '';
27925         }
27926         var qe = {
27927             query: q,
27928             forceAll: forceAll,
27929             combo: this,
27930             cancel:false
27931         };
27932         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27933             return false;
27934         }
27935         q = qe.query;
27936         forceAll = qe.forceAll;
27937         if(forceAll === true || (q.length >= this.minChars)){
27938             if(this.lastQuery != q || this.alwaysQuery){
27939                 this.lastQuery = q;
27940                 if(this.mode == 'local'){
27941                     this.selectedIndex = -1;
27942                     if(forceAll){
27943                         this.store.clearFilter();
27944                     }else{
27945                         this.store.filter(this.displayField, q);
27946                     }
27947                     this.onLoad();
27948                 }else{
27949                     this.store.baseParams[this.queryParam] = q;
27950                     this.store.load({
27951                         params: this.getParams(q)
27952                     });
27953                     this.expand();
27954                 }
27955             }else{
27956                 this.selectedIndex = -1;
27957                 this.onLoad();   
27958             }
27959         }
27960     },
27961
27962     // private
27963     getParams : function(q){
27964         var p = {};
27965         //p[this.queryParam] = q;
27966         if(this.pageSize){
27967             p.start = 0;
27968             p.limit = this.pageSize;
27969         }
27970         return p;
27971     },
27972
27973     /**
27974      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27975      */
27976     collapse : function(){
27977         
27978     },
27979
27980     // private
27981     collapseIf : function(e){
27982         
27983     },
27984
27985     /**
27986      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27987      */
27988     expand : function(){
27989         
27990     } ,
27991
27992     // private
27993      
27994
27995     /** 
27996     * @cfg {Boolean} grow 
27997     * @hide 
27998     */
27999     /** 
28000     * @cfg {Number} growMin 
28001     * @hide 
28002     */
28003     /** 
28004     * @cfg {Number} growMax 
28005     * @hide 
28006     */
28007     /**
28008      * @hide
28009      * @method autoSize
28010      */
28011     
28012     setWidth : function()
28013     {
28014         
28015     },
28016     getResizeEl : function(){
28017         return this.el;
28018     }
28019 });//<script type="text/javasscript">
28020  
28021
28022 /**
28023  * @class Roo.DDView
28024  * A DnD enabled version of Roo.View.
28025  * @param {Element/String} container The Element in which to create the View.
28026  * @param {String} tpl The template string used to create the markup for each element of the View
28027  * @param {Object} config The configuration properties. These include all the config options of
28028  * {@link Roo.View} plus some specific to this class.<br>
28029  * <p>
28030  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28031  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28032  * <p>
28033  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28034 .x-view-drag-insert-above {
28035         border-top:1px dotted #3366cc;
28036 }
28037 .x-view-drag-insert-below {
28038         border-bottom:1px dotted #3366cc;
28039 }
28040 </code></pre>
28041  * 
28042  */
28043  
28044 Roo.DDView = function(container, tpl, config) {
28045     Roo.DDView.superclass.constructor.apply(this, arguments);
28046     this.getEl().setStyle("outline", "0px none");
28047     this.getEl().unselectable();
28048     if (this.dragGroup) {
28049                 this.setDraggable(this.dragGroup.split(","));
28050     }
28051     if (this.dropGroup) {
28052                 this.setDroppable(this.dropGroup.split(","));
28053     }
28054     if (this.deletable) {
28055         this.setDeletable();
28056     }
28057     this.isDirtyFlag = false;
28058         this.addEvents({
28059                 "drop" : true
28060         });
28061 };
28062
28063 Roo.extend(Roo.DDView, Roo.View, {
28064 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28065 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28066 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28067 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28068
28069         isFormField: true,
28070
28071         reset: Roo.emptyFn,
28072         
28073         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28074
28075         validate: function() {
28076                 return true;
28077         },
28078         
28079         destroy: function() {
28080                 this.purgeListeners();
28081                 this.getEl.removeAllListeners();
28082                 this.getEl().remove();
28083                 if (this.dragZone) {
28084                         if (this.dragZone.destroy) {
28085                                 this.dragZone.destroy();
28086                         }
28087                 }
28088                 if (this.dropZone) {
28089                         if (this.dropZone.destroy) {
28090                                 this.dropZone.destroy();
28091                         }
28092                 }
28093         },
28094
28095 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28096         getName: function() {
28097                 return this.name;
28098         },
28099
28100 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28101         setValue: function(v) {
28102                 if (!this.store) {
28103                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28104                 }
28105                 var data = {};
28106                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28107                 this.store.proxy = new Roo.data.MemoryProxy(data);
28108                 this.store.load();
28109         },
28110
28111 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28112         getValue: function() {
28113                 var result = '(';
28114                 this.store.each(function(rec) {
28115                         result += rec.id + ',';
28116                 });
28117                 return result.substr(0, result.length - 1) + ')';
28118         },
28119         
28120         getIds: function() {
28121                 var i = 0, result = new Array(this.store.getCount());
28122                 this.store.each(function(rec) {
28123                         result[i++] = rec.id;
28124                 });
28125                 return result;
28126         },
28127         
28128         isDirty: function() {
28129                 return this.isDirtyFlag;
28130         },
28131
28132 /**
28133  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28134  *      whole Element becomes the target, and this causes the drop gesture to append.
28135  */
28136     getTargetFromEvent : function(e) {
28137                 var target = e.getTarget();
28138                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28139                 target = target.parentNode;
28140                 }
28141                 if (!target) {
28142                         target = this.el.dom.lastChild || this.el.dom;
28143                 }
28144                 return target;
28145     },
28146
28147 /**
28148  *      Create the drag data which consists of an object which has the property "ddel" as
28149  *      the drag proxy element. 
28150  */
28151     getDragData : function(e) {
28152         var target = this.findItemFromChild(e.getTarget());
28153                 if(target) {
28154                         this.handleSelection(e);
28155                         var selNodes = this.getSelectedNodes();
28156             var dragData = {
28157                 source: this,
28158                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28159                 nodes: selNodes,
28160                 records: []
28161                         };
28162                         var selectedIndices = this.getSelectedIndexes();
28163                         for (var i = 0; i < selectedIndices.length; i++) {
28164                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28165                         }
28166                         if (selNodes.length == 1) {
28167                                 dragData.ddel = target.cloneNode(true); // the div element
28168                         } else {
28169                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28170                                 div.className = 'multi-proxy';
28171                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28172                                         div.appendChild(selNodes[i].cloneNode(true));
28173                                 }
28174                                 dragData.ddel = div;
28175                         }
28176             //console.log(dragData)
28177             //console.log(dragData.ddel.innerHTML)
28178                         return dragData;
28179                 }
28180         //console.log('nodragData')
28181                 return false;
28182     },
28183     
28184 /**     Specify to which ddGroup items in this DDView may be dragged. */
28185     setDraggable: function(ddGroup) {
28186         if (ddGroup instanceof Array) {
28187                 Roo.each(ddGroup, this.setDraggable, this);
28188                 return;
28189         }
28190         if (this.dragZone) {
28191                 this.dragZone.addToGroup(ddGroup);
28192         } else {
28193                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28194                                 containerScroll: true,
28195                                 ddGroup: ddGroup 
28196
28197                         });
28198 //                      Draggability implies selection. DragZone's mousedown selects the element.
28199                         if (!this.multiSelect) { this.singleSelect = true; }
28200
28201 //                      Wire the DragZone's handlers up to methods in *this*
28202                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28203                 }
28204     },
28205
28206 /**     Specify from which ddGroup this DDView accepts drops. */
28207     setDroppable: function(ddGroup) {
28208         if (ddGroup instanceof Array) {
28209                 Roo.each(ddGroup, this.setDroppable, this);
28210                 return;
28211         }
28212         if (this.dropZone) {
28213                 this.dropZone.addToGroup(ddGroup);
28214         } else {
28215                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28216                                 containerScroll: true,
28217                                 ddGroup: ddGroup
28218                         });
28219
28220 //                      Wire the DropZone's handlers up to methods in *this*
28221                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28222                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28223                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28224                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28225                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28226                 }
28227     },
28228
28229 /**     Decide whether to drop above or below a View node. */
28230     getDropPoint : function(e, n, dd){
28231         if (n == this.el.dom) { return "above"; }
28232                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28233                 var c = t + (b - t) / 2;
28234                 var y = Roo.lib.Event.getPageY(e);
28235                 if(y <= c) {
28236                         return "above";
28237                 }else{
28238                         return "below";
28239                 }
28240     },
28241
28242     onNodeEnter : function(n, dd, e, data){
28243                 return false;
28244     },
28245     
28246     onNodeOver : function(n, dd, e, data){
28247                 var pt = this.getDropPoint(e, n, dd);
28248                 // set the insert point style on the target node
28249                 var dragElClass = this.dropNotAllowed;
28250                 if (pt) {
28251                         var targetElClass;
28252                         if (pt == "above"){
28253                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28254                                 targetElClass = "x-view-drag-insert-above";
28255                         } else {
28256                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28257                                 targetElClass = "x-view-drag-insert-below";
28258                         }
28259                         if (this.lastInsertClass != targetElClass){
28260                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28261                                 this.lastInsertClass = targetElClass;
28262                         }
28263                 }
28264                 return dragElClass;
28265         },
28266
28267     onNodeOut : function(n, dd, e, data){
28268                 this.removeDropIndicators(n);
28269     },
28270
28271     onNodeDrop : function(n, dd, e, data){
28272         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28273                 return false;
28274         }
28275         var pt = this.getDropPoint(e, n, dd);
28276                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28277                 if (pt == "below") { insertAt++; }
28278                 for (var i = 0; i < data.records.length; i++) {
28279                         var r = data.records[i];
28280                         var dup = this.store.getById(r.id);
28281                         if (dup && (dd != this.dragZone)) {
28282                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28283                         } else {
28284                                 if (data.copy) {
28285                                         this.store.insert(insertAt++, r.copy());
28286                                 } else {
28287                                         data.source.isDirtyFlag = true;
28288                                         r.store.remove(r);
28289                                         this.store.insert(insertAt++, r);
28290                                 }
28291                                 this.isDirtyFlag = true;
28292                         }
28293                 }
28294                 this.dragZone.cachedTarget = null;
28295                 return true;
28296     },
28297
28298     removeDropIndicators : function(n){
28299                 if(n){
28300                         Roo.fly(n).removeClass([
28301                                 "x-view-drag-insert-above",
28302                                 "x-view-drag-insert-below"]);
28303                         this.lastInsertClass = "_noclass";
28304                 }
28305     },
28306
28307 /**
28308  *      Utility method. Add a delete option to the DDView's context menu.
28309  *      @param {String} imageUrl The URL of the "delete" icon image.
28310  */
28311         setDeletable: function(imageUrl) {
28312                 if (!this.singleSelect && !this.multiSelect) {
28313                         this.singleSelect = true;
28314                 }
28315                 var c = this.getContextMenu();
28316                 this.contextMenu.on("itemclick", function(item) {
28317                         switch (item.id) {
28318                                 case "delete":
28319                                         this.remove(this.getSelectedIndexes());
28320                                         break;
28321                         }
28322                 }, this);
28323                 this.contextMenu.add({
28324                         icon: imageUrl,
28325                         id: "delete",
28326                         text: 'Delete'
28327                 });
28328         },
28329         
28330 /**     Return the context menu for this DDView. */
28331         getContextMenu: function() {
28332                 if (!this.contextMenu) {
28333 //                      Create the View's context menu
28334                         this.contextMenu = new Roo.menu.Menu({
28335                                 id: this.id + "-contextmenu"
28336                         });
28337                         this.el.on("contextmenu", this.showContextMenu, this);
28338                 }
28339                 return this.contextMenu;
28340         },
28341         
28342         disableContextMenu: function() {
28343                 if (this.contextMenu) {
28344                         this.el.un("contextmenu", this.showContextMenu, this);
28345                 }
28346         },
28347
28348         showContextMenu: function(e, item) {
28349         item = this.findItemFromChild(e.getTarget());
28350                 if (item) {
28351                         e.stopEvent();
28352                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28353                         this.contextMenu.showAt(e.getXY());
28354             }
28355     },
28356
28357 /**
28358  *      Remove {@link Roo.data.Record}s at the specified indices.
28359  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28360  */
28361     remove: function(selectedIndices) {
28362                 selectedIndices = [].concat(selectedIndices);
28363                 for (var i = 0; i < selectedIndices.length; i++) {
28364                         var rec = this.store.getAt(selectedIndices[i]);
28365                         this.store.remove(rec);
28366                 }
28367     },
28368
28369 /**
28370  *      Double click fires the event, but also, if this is draggable, and there is only one other
28371  *      related DropZone, it transfers the selected node.
28372  */
28373     onDblClick : function(e){
28374         var item = this.findItemFromChild(e.getTarget());
28375         if(item){
28376             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28377                 return false;
28378             }
28379             if (this.dragGroup) {
28380                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28381                     while (targets.indexOf(this.dropZone) > -1) {
28382                             targets.remove(this.dropZone);
28383                                 }
28384                     if (targets.length == 1) {
28385                                         this.dragZone.cachedTarget = null;
28386                         var el = Roo.get(targets[0].getEl());
28387                         var box = el.getBox(true);
28388                         targets[0].onNodeDrop(el.dom, {
28389                                 target: el.dom,
28390                                 xy: [box.x, box.y + box.height - 1]
28391                         }, null, this.getDragData(e));
28392                     }
28393                 }
28394         }
28395     },
28396     
28397     handleSelection: function(e) {
28398                 this.dragZone.cachedTarget = null;
28399         var item = this.findItemFromChild(e.getTarget());
28400         if (!item) {
28401                 this.clearSelections(true);
28402                 return;
28403         }
28404                 if (item && (this.multiSelect || this.singleSelect)){
28405                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28406                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28407                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28408                                 this.unselect(item);
28409                         } else {
28410                                 this.select(item, this.multiSelect && e.ctrlKey);
28411                                 this.lastSelection = item;
28412                         }
28413                 }
28414     },
28415
28416     onItemClick : function(item, index, e){
28417                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28418                         return false;
28419                 }
28420                 return true;
28421     },
28422
28423     unselect : function(nodeInfo, suppressEvent){
28424                 var node = this.getNode(nodeInfo);
28425                 if(node && this.isSelected(node)){
28426                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28427                                 Roo.fly(node).removeClass(this.selectedClass);
28428                                 this.selections.remove(node);
28429                                 if(!suppressEvent){
28430                                         this.fireEvent("selectionchange", this, this.selections);
28431                                 }
28432                         }
28433                 }
28434     }
28435 });
28436 /*
28437  * Based on:
28438  * Ext JS Library 1.1.1
28439  * Copyright(c) 2006-2007, Ext JS, LLC.
28440  *
28441  * Originally Released Under LGPL - original licence link has changed is not relivant.
28442  *
28443  * Fork - LGPL
28444  * <script type="text/javascript">
28445  */
28446  
28447 /**
28448  * @class Roo.LayoutManager
28449  * @extends Roo.util.Observable
28450  * Base class for layout managers.
28451  */
28452 Roo.LayoutManager = function(container, config){
28453     Roo.LayoutManager.superclass.constructor.call(this);
28454     this.el = Roo.get(container);
28455     // ie scrollbar fix
28456     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28457         document.body.scroll = "no";
28458     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28459         this.el.position('relative');
28460     }
28461     this.id = this.el.id;
28462     this.el.addClass("x-layout-container");
28463     /** false to disable window resize monitoring @type Boolean */
28464     this.monitorWindowResize = true;
28465     this.regions = {};
28466     this.addEvents({
28467         /**
28468          * @event layout
28469          * Fires when a layout is performed. 
28470          * @param {Roo.LayoutManager} this
28471          */
28472         "layout" : true,
28473         /**
28474          * @event regionresized
28475          * Fires when the user resizes a region. 
28476          * @param {Roo.LayoutRegion} region The resized region
28477          * @param {Number} newSize The new size (width for east/west, height for north/south)
28478          */
28479         "regionresized" : true,
28480         /**
28481          * @event regioncollapsed
28482          * Fires when a region is collapsed. 
28483          * @param {Roo.LayoutRegion} region The collapsed region
28484          */
28485         "regioncollapsed" : true,
28486         /**
28487          * @event regionexpanded
28488          * Fires when a region is expanded.  
28489          * @param {Roo.LayoutRegion} region The expanded region
28490          */
28491         "regionexpanded" : true
28492     });
28493     this.updating = false;
28494     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28495 };
28496
28497 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28498     /**
28499      * Returns true if this layout is currently being updated
28500      * @return {Boolean}
28501      */
28502     isUpdating : function(){
28503         return this.updating; 
28504     },
28505     
28506     /**
28507      * Suspend the LayoutManager from doing auto-layouts while
28508      * making multiple add or remove calls
28509      */
28510     beginUpdate : function(){
28511         this.updating = true;    
28512     },
28513     
28514     /**
28515      * Restore auto-layouts and optionally disable the manager from performing a layout
28516      * @param {Boolean} noLayout true to disable a layout update 
28517      */
28518     endUpdate : function(noLayout){
28519         this.updating = false;
28520         if(!noLayout){
28521             this.layout();
28522         }    
28523     },
28524     
28525     layout: function(){
28526         
28527     },
28528     
28529     onRegionResized : function(region, newSize){
28530         this.fireEvent("regionresized", region, newSize);
28531         this.layout();
28532     },
28533     
28534     onRegionCollapsed : function(region){
28535         this.fireEvent("regioncollapsed", region);
28536     },
28537     
28538     onRegionExpanded : function(region){
28539         this.fireEvent("regionexpanded", region);
28540     },
28541         
28542     /**
28543      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28544      * performs box-model adjustments.
28545      * @return {Object} The size as an object {width: (the width), height: (the height)}
28546      */
28547     getViewSize : function(){
28548         var size;
28549         if(this.el.dom != document.body){
28550             size = this.el.getSize();
28551         }else{
28552             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28553         }
28554         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28555         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28556         return size;
28557     },
28558     
28559     /**
28560      * Returns the Element this layout is bound to.
28561      * @return {Roo.Element}
28562      */
28563     getEl : function(){
28564         return this.el;
28565     },
28566     
28567     /**
28568      * Returns the specified region.
28569      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28570      * @return {Roo.LayoutRegion}
28571      */
28572     getRegion : function(target){
28573         return this.regions[target.toLowerCase()];
28574     },
28575     
28576     onWindowResize : function(){
28577         if(this.monitorWindowResize){
28578             this.layout();
28579         }
28580     }
28581 });/*
28582  * Based on:
28583  * Ext JS Library 1.1.1
28584  * Copyright(c) 2006-2007, Ext JS, LLC.
28585  *
28586  * Originally Released Under LGPL - original licence link has changed is not relivant.
28587  *
28588  * Fork - LGPL
28589  * <script type="text/javascript">
28590  */
28591 /**
28592  * @class Roo.BorderLayout
28593  * @extends Roo.LayoutManager
28594  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28595  * please see: <br><br>
28596  * <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>
28597  * <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>
28598  * Example:
28599  <pre><code>
28600  var layout = new Roo.BorderLayout(document.body, {
28601     north: {
28602         initialSize: 25,
28603         titlebar: false
28604     },
28605     west: {
28606         split:true,
28607         initialSize: 200,
28608         minSize: 175,
28609         maxSize: 400,
28610         titlebar: true,
28611         collapsible: true
28612     },
28613     east: {
28614         split:true,
28615         initialSize: 202,
28616         minSize: 175,
28617         maxSize: 400,
28618         titlebar: true,
28619         collapsible: true
28620     },
28621     south: {
28622         split:true,
28623         initialSize: 100,
28624         minSize: 100,
28625         maxSize: 200,
28626         titlebar: true,
28627         collapsible: true
28628     },
28629     center: {
28630         titlebar: true,
28631         autoScroll:true,
28632         resizeTabs: true,
28633         minTabWidth: 50,
28634         preferredTabWidth: 150
28635     }
28636 });
28637
28638 // shorthand
28639 var CP = Roo.ContentPanel;
28640
28641 layout.beginUpdate();
28642 layout.add("north", new CP("north", "North"));
28643 layout.add("south", new CP("south", {title: "South", closable: true}));
28644 layout.add("west", new CP("west", {title: "West"}));
28645 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28646 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28647 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28648 layout.getRegion("center").showPanel("center1");
28649 layout.endUpdate();
28650 </code></pre>
28651
28652 <b>The container the layout is rendered into can be either the body element or any other element.
28653 If it is not the body element, the container needs to either be an absolute positioned element,
28654 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28655 the container size if it is not the body element.</b>
28656
28657 * @constructor
28658 * Create a new BorderLayout
28659 * @param {String/HTMLElement/Element} container The container this layout is bound to
28660 * @param {Object} config Configuration options
28661  */
28662 Roo.BorderLayout = function(container, config){
28663     config = config || {};
28664     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28665     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28666     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28667         var target = this.factory.validRegions[i];
28668         if(config[target]){
28669             this.addRegion(target, config[target]);
28670         }
28671     }
28672 };
28673
28674 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28675     /**
28676      * Creates and adds a new region if it doesn't already exist.
28677      * @param {String} target The target region key (north, south, east, west or center).
28678      * @param {Object} config The regions config object
28679      * @return {BorderLayoutRegion} The new region
28680      */
28681     addRegion : function(target, config){
28682         if(!this.regions[target]){
28683             var r = this.factory.create(target, this, config);
28684             this.bindRegion(target, r);
28685         }
28686         return this.regions[target];
28687     },
28688
28689     // private (kinda)
28690     bindRegion : function(name, r){
28691         this.regions[name] = r;
28692         r.on("visibilitychange", this.layout, this);
28693         r.on("paneladded", this.layout, this);
28694         r.on("panelremoved", this.layout, this);
28695         r.on("invalidated", this.layout, this);
28696         r.on("resized", this.onRegionResized, this);
28697         r.on("collapsed", this.onRegionCollapsed, this);
28698         r.on("expanded", this.onRegionExpanded, this);
28699     },
28700
28701     /**
28702      * Performs a layout update.
28703      */
28704     layout : function(){
28705         if(this.updating) {
28706             return;
28707         }
28708         var size = this.getViewSize();
28709         var w = size.width;
28710         var h = size.height;
28711         var centerW = w;
28712         var centerH = h;
28713         var centerY = 0;
28714         var centerX = 0;
28715         //var x = 0, y = 0;
28716
28717         var rs = this.regions;
28718         var north = rs["north"];
28719         var south = rs["south"]; 
28720         var west = rs["west"];
28721         var east = rs["east"];
28722         var center = rs["center"];
28723         //if(this.hideOnLayout){ // not supported anymore
28724             //c.el.setStyle("display", "none");
28725         //}
28726         if(north && north.isVisible()){
28727             var b = north.getBox();
28728             var m = north.getMargins();
28729             b.width = w - (m.left+m.right);
28730             b.x = m.left;
28731             b.y = m.top;
28732             centerY = b.height + b.y + m.bottom;
28733             centerH -= centerY;
28734             north.updateBox(this.safeBox(b));
28735         }
28736         if(south && south.isVisible()){
28737             var b = south.getBox();
28738             var m = south.getMargins();
28739             b.width = w - (m.left+m.right);
28740             b.x = m.left;
28741             var totalHeight = (b.height + m.top + m.bottom);
28742             b.y = h - totalHeight + m.top;
28743             centerH -= totalHeight;
28744             south.updateBox(this.safeBox(b));
28745         }
28746         if(west && west.isVisible()){
28747             var b = west.getBox();
28748             var m = west.getMargins();
28749             b.height = centerH - (m.top+m.bottom);
28750             b.x = m.left;
28751             b.y = centerY + m.top;
28752             var totalWidth = (b.width + m.left + m.right);
28753             centerX += totalWidth;
28754             centerW -= totalWidth;
28755             west.updateBox(this.safeBox(b));
28756         }
28757         if(east && east.isVisible()){
28758             var b = east.getBox();
28759             var m = east.getMargins();
28760             b.height = centerH - (m.top+m.bottom);
28761             var totalWidth = (b.width + m.left + m.right);
28762             b.x = w - totalWidth + m.left;
28763             b.y = centerY + m.top;
28764             centerW -= totalWidth;
28765             east.updateBox(this.safeBox(b));
28766         }
28767         if(center){
28768             var m = center.getMargins();
28769             var centerBox = {
28770                 x: centerX + m.left,
28771                 y: centerY + m.top,
28772                 width: centerW - (m.left+m.right),
28773                 height: centerH - (m.top+m.bottom)
28774             };
28775             //if(this.hideOnLayout){
28776                 //center.el.setStyle("display", "block");
28777             //}
28778             center.updateBox(this.safeBox(centerBox));
28779         }
28780         this.el.repaint();
28781         this.fireEvent("layout", this);
28782     },
28783
28784     // private
28785     safeBox : function(box){
28786         box.width = Math.max(0, box.width);
28787         box.height = Math.max(0, box.height);
28788         return box;
28789     },
28790
28791     /**
28792      * Adds a ContentPanel (or subclass) to this layout.
28793      * @param {String} target The target region key (north, south, east, west or center).
28794      * @param {Roo.ContentPanel} panel The panel to add
28795      * @return {Roo.ContentPanel} The added panel
28796      */
28797     add : function(target, panel){
28798          
28799         target = target.toLowerCase();
28800         return this.regions[target].add(panel);
28801     },
28802
28803     /**
28804      * Remove a ContentPanel (or subclass) to this layout.
28805      * @param {String} target The target region key (north, south, east, west or center).
28806      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28807      * @return {Roo.ContentPanel} The removed panel
28808      */
28809     remove : function(target, panel){
28810         target = target.toLowerCase();
28811         return this.regions[target].remove(panel);
28812     },
28813
28814     /**
28815      * Searches all regions for a panel with the specified id
28816      * @param {String} panelId
28817      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28818      */
28819     findPanel : function(panelId){
28820         var rs = this.regions;
28821         for(var target in rs){
28822             if(typeof rs[target] != "function"){
28823                 var p = rs[target].getPanel(panelId);
28824                 if(p){
28825                     return p;
28826                 }
28827             }
28828         }
28829         return null;
28830     },
28831
28832     /**
28833      * Searches all regions for a panel with the specified id and activates (shows) it.
28834      * @param {String/ContentPanel} panelId The panels id or the panel itself
28835      * @return {Roo.ContentPanel} The shown panel or null
28836      */
28837     showPanel : function(panelId) {
28838       var rs = this.regions;
28839       for(var target in rs){
28840          var r = rs[target];
28841          if(typeof r != "function"){
28842             if(r.hasPanel(panelId)){
28843                return r.showPanel(panelId);
28844             }
28845          }
28846       }
28847       return null;
28848    },
28849
28850    /**
28851      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28852      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28853      */
28854     restoreState : function(provider){
28855         if(!provider){
28856             provider = Roo.state.Manager;
28857         }
28858         var sm = new Roo.LayoutStateManager();
28859         sm.init(this, provider);
28860     },
28861
28862     /**
28863      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28864      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28865      * a valid ContentPanel config object.  Example:
28866      * <pre><code>
28867 // Create the main layout
28868 var layout = new Roo.BorderLayout('main-ct', {
28869     west: {
28870         split:true,
28871         minSize: 175,
28872         titlebar: true
28873     },
28874     center: {
28875         title:'Components'
28876     }
28877 }, 'main-ct');
28878
28879 // Create and add multiple ContentPanels at once via configs
28880 layout.batchAdd({
28881    west: {
28882        id: 'source-files',
28883        autoCreate:true,
28884        title:'Ext Source Files',
28885        autoScroll:true,
28886        fitToFrame:true
28887    },
28888    center : {
28889        el: cview,
28890        autoScroll:true,
28891        fitToFrame:true,
28892        toolbar: tb,
28893        resizeEl:'cbody'
28894    }
28895 });
28896 </code></pre>
28897      * @param {Object} regions An object containing ContentPanel configs by region name
28898      */
28899     batchAdd : function(regions){
28900         this.beginUpdate();
28901         for(var rname in regions){
28902             var lr = this.regions[rname];
28903             if(lr){
28904                 this.addTypedPanels(lr, regions[rname]);
28905             }
28906         }
28907         this.endUpdate();
28908     },
28909
28910     // private
28911     addTypedPanels : function(lr, ps){
28912         if(typeof ps == 'string'){
28913             lr.add(new Roo.ContentPanel(ps));
28914         }
28915         else if(ps instanceof Array){
28916             for(var i =0, len = ps.length; i < len; i++){
28917                 this.addTypedPanels(lr, ps[i]);
28918             }
28919         }
28920         else if(!ps.events){ // raw config?
28921             var el = ps.el;
28922             delete ps.el; // prevent conflict
28923             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28924         }
28925         else {  // panel object assumed!
28926             lr.add(ps);
28927         }
28928     },
28929     /**
28930      * Adds a xtype elements to the layout.
28931      * <pre><code>
28932
28933 layout.addxtype({
28934        xtype : 'ContentPanel',
28935        region: 'west',
28936        items: [ .... ]
28937    }
28938 );
28939
28940 layout.addxtype({
28941         xtype : 'NestedLayoutPanel',
28942         region: 'west',
28943         layout: {
28944            center: { },
28945            west: { }   
28946         },
28947         items : [ ... list of content panels or nested layout panels.. ]
28948    }
28949 );
28950 </code></pre>
28951      * @param {Object} cfg Xtype definition of item to add.
28952      */
28953     addxtype : function(cfg)
28954     {
28955         // basically accepts a pannel...
28956         // can accept a layout region..!?!?
28957         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28958         
28959         if (!cfg.xtype.match(/Panel$/)) {
28960             return false;
28961         }
28962         var ret = false;
28963         
28964         if (typeof(cfg.region) == 'undefined') {
28965             Roo.log("Failed to add Panel, region was not set");
28966             Roo.log(cfg);
28967             return false;
28968         }
28969         var region = cfg.region;
28970         delete cfg.region;
28971         
28972           
28973         var xitems = [];
28974         if (cfg.items) {
28975             xitems = cfg.items;
28976             delete cfg.items;
28977         }
28978         var nb = false;
28979         
28980         switch(cfg.xtype) 
28981         {
28982             case 'ContentPanel':  // ContentPanel (el, cfg)
28983             case 'ScrollPanel':  // ContentPanel (el, cfg)
28984             case 'ViewPanel': 
28985                 if(cfg.autoCreate) {
28986                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28987                 } else {
28988                     var el = this.el.createChild();
28989                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28990                 }
28991                 
28992                 this.add(region, ret);
28993                 break;
28994             
28995             
28996             case 'TreePanel': // our new panel!
28997                 cfg.el = this.el.createChild();
28998                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28999                 this.add(region, ret);
29000                 break;
29001             
29002             case 'NestedLayoutPanel': 
29003                 // create a new Layout (which is  a Border Layout...
29004                 var el = this.el.createChild();
29005                 var clayout = cfg.layout;
29006                 delete cfg.layout;
29007                 clayout.items   = clayout.items  || [];
29008                 // replace this exitems with the clayout ones..
29009                 xitems = clayout.items;
29010                  
29011                 
29012                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29013                     cfg.background = false;
29014                 }
29015                 var layout = new Roo.BorderLayout(el, clayout);
29016                 
29017                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29018                 //console.log('adding nested layout panel '  + cfg.toSource());
29019                 this.add(region, ret);
29020                 nb = {}; /// find first...
29021                 break;
29022                 
29023             case 'GridPanel': 
29024             
29025                 // needs grid and region
29026                 
29027                 //var el = this.getRegion(region).el.createChild();
29028                 var el = this.el.createChild();
29029                 // create the grid first...
29030                 
29031                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29032                 delete cfg.grid;
29033                 if (region == 'center' && this.active ) {
29034                     cfg.background = false;
29035                 }
29036                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29037                 
29038                 this.add(region, ret);
29039                 if (cfg.background) {
29040                     ret.on('activate', function(gp) {
29041                         if (!gp.grid.rendered) {
29042                             gp.grid.render();
29043                         }
29044                     });
29045                 } else {
29046                     grid.render();
29047                 }
29048                 break;
29049            
29050            
29051            
29052                 
29053                 
29054                 
29055             default:
29056                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29057                     
29058                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29059                     this.add(region, ret);
29060                 } else {
29061                 
29062                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29063                     return null;
29064                 }
29065                 
29066              // GridPanel (grid, cfg)
29067             
29068         }
29069         this.beginUpdate();
29070         // add children..
29071         var region = '';
29072         var abn = {};
29073         Roo.each(xitems, function(i)  {
29074             region = nb && i.region ? i.region : false;
29075             
29076             var add = ret.addxtype(i);
29077            
29078             if (region) {
29079                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29080                 if (!i.background) {
29081                     abn[region] = nb[region] ;
29082                 }
29083             }
29084             
29085         });
29086         this.endUpdate();
29087
29088         // make the last non-background panel active..
29089         //if (nb) { Roo.log(abn); }
29090         if (nb) {
29091             
29092             for(var r in abn) {
29093                 region = this.getRegion(r);
29094                 if (region) {
29095                     // tried using nb[r], but it does not work..
29096                      
29097                     region.showPanel(abn[r]);
29098                    
29099                 }
29100             }
29101         }
29102         return ret;
29103         
29104     }
29105 });
29106
29107 /**
29108  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29109  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29110  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29111  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29112  * <pre><code>
29113 // shorthand
29114 var CP = Roo.ContentPanel;
29115
29116 var layout = Roo.BorderLayout.create({
29117     north: {
29118         initialSize: 25,
29119         titlebar: false,
29120         panels: [new CP("north", "North")]
29121     },
29122     west: {
29123         split:true,
29124         initialSize: 200,
29125         minSize: 175,
29126         maxSize: 400,
29127         titlebar: true,
29128         collapsible: true,
29129         panels: [new CP("west", {title: "West"})]
29130     },
29131     east: {
29132         split:true,
29133         initialSize: 202,
29134         minSize: 175,
29135         maxSize: 400,
29136         titlebar: true,
29137         collapsible: true,
29138         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29139     },
29140     south: {
29141         split:true,
29142         initialSize: 100,
29143         minSize: 100,
29144         maxSize: 200,
29145         titlebar: true,
29146         collapsible: true,
29147         panels: [new CP("south", {title: "South", closable: true})]
29148     },
29149     center: {
29150         titlebar: true,
29151         autoScroll:true,
29152         resizeTabs: true,
29153         minTabWidth: 50,
29154         preferredTabWidth: 150,
29155         panels: [
29156             new CP("center1", {title: "Close Me", closable: true}),
29157             new CP("center2", {title: "Center Panel", closable: false})
29158         ]
29159     }
29160 }, document.body);
29161
29162 layout.getRegion("center").showPanel("center1");
29163 </code></pre>
29164  * @param config
29165  * @param targetEl
29166  */
29167 Roo.BorderLayout.create = function(config, targetEl){
29168     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29169     layout.beginUpdate();
29170     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29171     for(var j = 0, jlen = regions.length; j < jlen; j++){
29172         var lr = regions[j];
29173         if(layout.regions[lr] && config[lr].panels){
29174             var r = layout.regions[lr];
29175             var ps = config[lr].panels;
29176             layout.addTypedPanels(r, ps);
29177         }
29178     }
29179     layout.endUpdate();
29180     return layout;
29181 };
29182
29183 // private
29184 Roo.BorderLayout.RegionFactory = {
29185     // private
29186     validRegions : ["north","south","east","west","center"],
29187
29188     // private
29189     create : function(target, mgr, config){
29190         target = target.toLowerCase();
29191         if(config.lightweight || config.basic){
29192             return new Roo.BasicLayoutRegion(mgr, config, target);
29193         }
29194         switch(target){
29195             case "north":
29196                 return new Roo.NorthLayoutRegion(mgr, config);
29197             case "south":
29198                 return new Roo.SouthLayoutRegion(mgr, config);
29199             case "east":
29200                 return new Roo.EastLayoutRegion(mgr, config);
29201             case "west":
29202                 return new Roo.WestLayoutRegion(mgr, config);
29203             case "center":
29204                 return new Roo.CenterLayoutRegion(mgr, config);
29205         }
29206         throw 'Layout region "'+target+'" not supported.';
29207     }
29208 };/*
29209  * Based on:
29210  * Ext JS Library 1.1.1
29211  * Copyright(c) 2006-2007, Ext JS, LLC.
29212  *
29213  * Originally Released Under LGPL - original licence link has changed is not relivant.
29214  *
29215  * Fork - LGPL
29216  * <script type="text/javascript">
29217  */
29218  
29219 /**
29220  * @class Roo.BasicLayoutRegion
29221  * @extends Roo.util.Observable
29222  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29223  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29224  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29225  */
29226 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29227     this.mgr = mgr;
29228     this.position  = pos;
29229     this.events = {
29230         /**
29231          * @scope Roo.BasicLayoutRegion
29232          */
29233         
29234         /**
29235          * @event beforeremove
29236          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29237          * @param {Roo.LayoutRegion} this
29238          * @param {Roo.ContentPanel} panel The panel
29239          * @param {Object} e The cancel event object
29240          */
29241         "beforeremove" : true,
29242         /**
29243          * @event invalidated
29244          * Fires when the layout for this region is changed.
29245          * @param {Roo.LayoutRegion} this
29246          */
29247         "invalidated" : true,
29248         /**
29249          * @event visibilitychange
29250          * Fires when this region is shown or hidden 
29251          * @param {Roo.LayoutRegion} this
29252          * @param {Boolean} visibility true or false
29253          */
29254         "visibilitychange" : true,
29255         /**
29256          * @event paneladded
29257          * Fires when a panel is added. 
29258          * @param {Roo.LayoutRegion} this
29259          * @param {Roo.ContentPanel} panel The panel
29260          */
29261         "paneladded" : true,
29262         /**
29263          * @event panelremoved
29264          * Fires when a panel is removed. 
29265          * @param {Roo.LayoutRegion} this
29266          * @param {Roo.ContentPanel} panel The panel
29267          */
29268         "panelremoved" : true,
29269         /**
29270          * @event beforecollapse
29271          * Fires when this region before collapse.
29272          * @param {Roo.LayoutRegion} this
29273          */
29274         "beforecollapse" : true,
29275         /**
29276          * @event collapsed
29277          * Fires when this region is collapsed.
29278          * @param {Roo.LayoutRegion} this
29279          */
29280         "collapsed" : true,
29281         /**
29282          * @event expanded
29283          * Fires when this region is expanded.
29284          * @param {Roo.LayoutRegion} this
29285          */
29286         "expanded" : true,
29287         /**
29288          * @event slideshow
29289          * Fires when this region is slid into view.
29290          * @param {Roo.LayoutRegion} this
29291          */
29292         "slideshow" : true,
29293         /**
29294          * @event slidehide
29295          * Fires when this region slides out of view. 
29296          * @param {Roo.LayoutRegion} this
29297          */
29298         "slidehide" : true,
29299         /**
29300          * @event panelactivated
29301          * Fires when a panel is activated. 
29302          * @param {Roo.LayoutRegion} this
29303          * @param {Roo.ContentPanel} panel The activated panel
29304          */
29305         "panelactivated" : true,
29306         /**
29307          * @event resized
29308          * Fires when the user resizes this region. 
29309          * @param {Roo.LayoutRegion} this
29310          * @param {Number} newSize The new size (width for east/west, height for north/south)
29311          */
29312         "resized" : true
29313     };
29314     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29315     this.panels = new Roo.util.MixedCollection();
29316     this.panels.getKey = this.getPanelId.createDelegate(this);
29317     this.box = null;
29318     this.activePanel = null;
29319     // ensure listeners are added...
29320     
29321     if (config.listeners || config.events) {
29322         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29323             listeners : config.listeners || {},
29324             events : config.events || {}
29325         });
29326     }
29327     
29328     if(skipConfig !== true){
29329         this.applyConfig(config);
29330     }
29331 };
29332
29333 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29334     getPanelId : function(p){
29335         return p.getId();
29336     },
29337     
29338     applyConfig : function(config){
29339         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29340         this.config = config;
29341         
29342     },
29343     
29344     /**
29345      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29346      * the width, for horizontal (north, south) the height.
29347      * @param {Number} newSize The new width or height
29348      */
29349     resizeTo : function(newSize){
29350         var el = this.el ? this.el :
29351                  (this.activePanel ? this.activePanel.getEl() : null);
29352         if(el){
29353             switch(this.position){
29354                 case "east":
29355                 case "west":
29356                     el.setWidth(newSize);
29357                     this.fireEvent("resized", this, newSize);
29358                 break;
29359                 case "north":
29360                 case "south":
29361                     el.setHeight(newSize);
29362                     this.fireEvent("resized", this, newSize);
29363                 break;                
29364             }
29365         }
29366     },
29367     
29368     getBox : function(){
29369         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29370     },
29371     
29372     getMargins : function(){
29373         return this.margins;
29374     },
29375     
29376     updateBox : function(box){
29377         this.box = box;
29378         var el = this.activePanel.getEl();
29379         el.dom.style.left = box.x + "px";
29380         el.dom.style.top = box.y + "px";
29381         this.activePanel.setSize(box.width, box.height);
29382     },
29383     
29384     /**
29385      * Returns the container element for this region.
29386      * @return {Roo.Element}
29387      */
29388     getEl : function(){
29389         return this.activePanel;
29390     },
29391     
29392     /**
29393      * Returns true if this region is currently visible.
29394      * @return {Boolean}
29395      */
29396     isVisible : function(){
29397         return this.activePanel ? true : false;
29398     },
29399     
29400     setActivePanel : function(panel){
29401         panel = this.getPanel(panel);
29402         if(this.activePanel && this.activePanel != panel){
29403             this.activePanel.setActiveState(false);
29404             this.activePanel.getEl().setLeftTop(-10000,-10000);
29405         }
29406         this.activePanel = panel;
29407         panel.setActiveState(true);
29408         if(this.box){
29409             panel.setSize(this.box.width, this.box.height);
29410         }
29411         this.fireEvent("panelactivated", this, panel);
29412         this.fireEvent("invalidated");
29413     },
29414     
29415     /**
29416      * Show the specified panel.
29417      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29418      * @return {Roo.ContentPanel} The shown panel or null
29419      */
29420     showPanel : function(panel){
29421         if(panel = this.getPanel(panel)){
29422             this.setActivePanel(panel);
29423         }
29424         return panel;
29425     },
29426     
29427     /**
29428      * Get the active panel for this region.
29429      * @return {Roo.ContentPanel} The active panel or null
29430      */
29431     getActivePanel : function(){
29432         return this.activePanel;
29433     },
29434     
29435     /**
29436      * Add the passed ContentPanel(s)
29437      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29438      * @return {Roo.ContentPanel} The panel added (if only one was added)
29439      */
29440     add : function(panel){
29441         if(arguments.length > 1){
29442             for(var i = 0, len = arguments.length; i < len; i++) {
29443                 this.add(arguments[i]);
29444             }
29445             return null;
29446         }
29447         if(this.hasPanel(panel)){
29448             this.showPanel(panel);
29449             return panel;
29450         }
29451         var el = panel.getEl();
29452         if(el.dom.parentNode != this.mgr.el.dom){
29453             this.mgr.el.dom.appendChild(el.dom);
29454         }
29455         if(panel.setRegion){
29456             panel.setRegion(this);
29457         }
29458         this.panels.add(panel);
29459         el.setStyle("position", "absolute");
29460         if(!panel.background){
29461             this.setActivePanel(panel);
29462             if(this.config.initialSize && this.panels.getCount()==1){
29463                 this.resizeTo(this.config.initialSize);
29464             }
29465         }
29466         this.fireEvent("paneladded", this, panel);
29467         return panel;
29468     },
29469     
29470     /**
29471      * Returns true if the panel is in this region.
29472      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29473      * @return {Boolean}
29474      */
29475     hasPanel : function(panel){
29476         if(typeof panel == "object"){ // must be panel obj
29477             panel = panel.getId();
29478         }
29479         return this.getPanel(panel) ? true : false;
29480     },
29481     
29482     /**
29483      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29484      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29485      * @param {Boolean} preservePanel Overrides the config preservePanel option
29486      * @return {Roo.ContentPanel} The panel that was removed
29487      */
29488     remove : function(panel, preservePanel){
29489         panel = this.getPanel(panel);
29490         if(!panel){
29491             return null;
29492         }
29493         var e = {};
29494         this.fireEvent("beforeremove", this, panel, e);
29495         if(e.cancel === true){
29496             return null;
29497         }
29498         var panelId = panel.getId();
29499         this.panels.removeKey(panelId);
29500         return panel;
29501     },
29502     
29503     /**
29504      * Returns the panel specified or null if it's not in this region.
29505      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29506      * @return {Roo.ContentPanel}
29507      */
29508     getPanel : function(id){
29509         if(typeof id == "object"){ // must be panel obj
29510             return id;
29511         }
29512         return this.panels.get(id);
29513     },
29514     
29515     /**
29516      * Returns this regions position (north/south/east/west/center).
29517      * @return {String} 
29518      */
29519     getPosition: function(){
29520         return this.position;    
29521     }
29522 });/*
29523  * Based on:
29524  * Ext JS Library 1.1.1
29525  * Copyright(c) 2006-2007, Ext JS, LLC.
29526  *
29527  * Originally Released Under LGPL - original licence link has changed is not relivant.
29528  *
29529  * Fork - LGPL
29530  * <script type="text/javascript">
29531  */
29532  
29533 /**
29534  * @class Roo.LayoutRegion
29535  * @extends Roo.BasicLayoutRegion
29536  * This class represents a region in a layout manager.
29537  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29538  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29539  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29540  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29541  * @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})
29542  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29543  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29544  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29545  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29546  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29547  * @cfg {String}    title           The title for the region (overrides panel titles)
29548  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29549  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29550  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29551  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29552  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29553  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29554  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29555  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29556  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29557  * @cfg {Boolean}   showPin         True to show a pin button
29558  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29559  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29560  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29561  * @cfg {Number}    width           For East/West panels
29562  * @cfg {Number}    height          For North/South panels
29563  * @cfg {Boolean}   split           To show the splitter
29564  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29565  */
29566 Roo.LayoutRegion = function(mgr, config, pos){
29567     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29568     var dh = Roo.DomHelper;
29569     /** This region's container element 
29570     * @type Roo.Element */
29571     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29572     /** This region's title element 
29573     * @type Roo.Element */
29574
29575     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29576         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29577         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29578     ]}, true);
29579     this.titleEl.enableDisplayMode();
29580     /** This region's title text element 
29581     * @type HTMLElement */
29582     this.titleTextEl = this.titleEl.dom.firstChild;
29583     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29584     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29585     this.closeBtn.enableDisplayMode();
29586     this.closeBtn.on("click", this.closeClicked, this);
29587     this.closeBtn.hide();
29588
29589     this.createBody(config);
29590     this.visible = true;
29591     this.collapsed = false;
29592
29593     if(config.hideWhenEmpty){
29594         this.hide();
29595         this.on("paneladded", this.validateVisibility, this);
29596         this.on("panelremoved", this.validateVisibility, this);
29597     }
29598     this.applyConfig(config);
29599 };
29600
29601 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29602
29603     createBody : function(){
29604         /** This region's body element 
29605         * @type Roo.Element */
29606         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29607     },
29608
29609     applyConfig : function(c){
29610         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29611             var dh = Roo.DomHelper;
29612             if(c.titlebar !== false){
29613                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29614                 this.collapseBtn.on("click", this.collapse, this);
29615                 this.collapseBtn.enableDisplayMode();
29616
29617                 if(c.showPin === true || this.showPin){
29618                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29619                     this.stickBtn.enableDisplayMode();
29620                     this.stickBtn.on("click", this.expand, this);
29621                     this.stickBtn.hide();
29622                 }
29623             }
29624             /** This region's collapsed element
29625             * @type Roo.Element */
29626             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29627                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29628             ]}, true);
29629             if(c.floatable !== false){
29630                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29631                this.collapsedEl.on("click", this.collapseClick, this);
29632             }
29633
29634             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29635                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29636                    id: "message", unselectable: "on", style:{"float":"left"}});
29637                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29638              }
29639             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29640             this.expandBtn.on("click", this.expand, this);
29641         }
29642         if(this.collapseBtn){
29643             this.collapseBtn.setVisible(c.collapsible == true);
29644         }
29645         this.cmargins = c.cmargins || this.cmargins ||
29646                          (this.position == "west" || this.position == "east" ?
29647                              {top: 0, left: 2, right:2, bottom: 0} :
29648                              {top: 2, left: 0, right:0, bottom: 2});
29649         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29650         this.bottomTabs = c.tabPosition != "top";
29651         this.autoScroll = c.autoScroll || false;
29652         if(this.autoScroll){
29653             this.bodyEl.setStyle("overflow", "auto");
29654         }else{
29655             this.bodyEl.setStyle("overflow", "hidden");
29656         }
29657         //if(c.titlebar !== false){
29658             if((!c.titlebar && !c.title) || c.titlebar === false){
29659                 this.titleEl.hide();
29660             }else{
29661                 this.titleEl.show();
29662                 if(c.title){
29663                     this.titleTextEl.innerHTML = c.title;
29664                 }
29665             }
29666         //}
29667         this.duration = c.duration || .30;
29668         this.slideDuration = c.slideDuration || .45;
29669         this.config = c;
29670         if(c.collapsed){
29671             this.collapse(true);
29672         }
29673         if(c.hidden){
29674             this.hide();
29675         }
29676     },
29677     /**
29678      * Returns true if this region is currently visible.
29679      * @return {Boolean}
29680      */
29681     isVisible : function(){
29682         return this.visible;
29683     },
29684
29685     /**
29686      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29687      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29688      */
29689     setCollapsedTitle : function(title){
29690         title = title || "&#160;";
29691         if(this.collapsedTitleTextEl){
29692             this.collapsedTitleTextEl.innerHTML = title;
29693         }
29694     },
29695
29696     getBox : function(){
29697         var b;
29698         if(!this.collapsed){
29699             b = this.el.getBox(false, true);
29700         }else{
29701             b = this.collapsedEl.getBox(false, true);
29702         }
29703         return b;
29704     },
29705
29706     getMargins : function(){
29707         return this.collapsed ? this.cmargins : this.margins;
29708     },
29709
29710     highlight : function(){
29711         this.el.addClass("x-layout-panel-dragover");
29712     },
29713
29714     unhighlight : function(){
29715         this.el.removeClass("x-layout-panel-dragover");
29716     },
29717
29718     updateBox : function(box){
29719         this.box = box;
29720         if(!this.collapsed){
29721             this.el.dom.style.left = box.x + "px";
29722             this.el.dom.style.top = box.y + "px";
29723             this.updateBody(box.width, box.height);
29724         }else{
29725             this.collapsedEl.dom.style.left = box.x + "px";
29726             this.collapsedEl.dom.style.top = box.y + "px";
29727             this.collapsedEl.setSize(box.width, box.height);
29728         }
29729         if(this.tabs){
29730             this.tabs.autoSizeTabs();
29731         }
29732     },
29733
29734     updateBody : function(w, h){
29735         if(w !== null){
29736             this.el.setWidth(w);
29737             w -= this.el.getBorderWidth("rl");
29738             if(this.config.adjustments){
29739                 w += this.config.adjustments[0];
29740             }
29741         }
29742         if(h !== null){
29743             this.el.setHeight(h);
29744             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29745             h -= this.el.getBorderWidth("tb");
29746             if(this.config.adjustments){
29747                 h += this.config.adjustments[1];
29748             }
29749             this.bodyEl.setHeight(h);
29750             if(this.tabs){
29751                 h = this.tabs.syncHeight(h);
29752             }
29753         }
29754         if(this.panelSize){
29755             w = w !== null ? w : this.panelSize.width;
29756             h = h !== null ? h : this.panelSize.height;
29757         }
29758         if(this.activePanel){
29759             var el = this.activePanel.getEl();
29760             w = w !== null ? w : el.getWidth();
29761             h = h !== null ? h : el.getHeight();
29762             this.panelSize = {width: w, height: h};
29763             this.activePanel.setSize(w, h);
29764         }
29765         if(Roo.isIE && this.tabs){
29766             this.tabs.el.repaint();
29767         }
29768     },
29769
29770     /**
29771      * Returns the container element for this region.
29772      * @return {Roo.Element}
29773      */
29774     getEl : function(){
29775         return this.el;
29776     },
29777
29778     /**
29779      * Hides this region.
29780      */
29781     hide : function(){
29782         if(!this.collapsed){
29783             this.el.dom.style.left = "-2000px";
29784             this.el.hide();
29785         }else{
29786             this.collapsedEl.dom.style.left = "-2000px";
29787             this.collapsedEl.hide();
29788         }
29789         this.visible = false;
29790         this.fireEvent("visibilitychange", this, false);
29791     },
29792
29793     /**
29794      * Shows this region if it was previously hidden.
29795      */
29796     show : function(){
29797         if(!this.collapsed){
29798             this.el.show();
29799         }else{
29800             this.collapsedEl.show();
29801         }
29802         this.visible = true;
29803         this.fireEvent("visibilitychange", this, true);
29804     },
29805
29806     closeClicked : function(){
29807         if(this.activePanel){
29808             this.remove(this.activePanel);
29809         }
29810     },
29811
29812     collapseClick : function(e){
29813         if(this.isSlid){
29814            e.stopPropagation();
29815            this.slideIn();
29816         }else{
29817            e.stopPropagation();
29818            this.slideOut();
29819         }
29820     },
29821
29822     /**
29823      * Collapses this region.
29824      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29825      */
29826     collapse : function(skipAnim, skipCheck = false){
29827         if(this.collapsed) {
29828             return;
29829         }
29830         
29831         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29832             
29833             this.collapsed = true;
29834             if(this.split){
29835                 this.split.el.hide();
29836             }
29837             if(this.config.animate && skipAnim !== true){
29838                 this.fireEvent("invalidated", this);
29839                 this.animateCollapse();
29840             }else{
29841                 this.el.setLocation(-20000,-20000);
29842                 this.el.hide();
29843                 this.collapsedEl.show();
29844                 this.fireEvent("collapsed", this);
29845                 this.fireEvent("invalidated", this);
29846             }
29847         }
29848         
29849     },
29850
29851     animateCollapse : function(){
29852         // overridden
29853     },
29854
29855     /**
29856      * Expands this region if it was previously collapsed.
29857      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29858      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29859      */
29860     expand : function(e, skipAnim){
29861         if(e) {
29862             e.stopPropagation();
29863         }
29864         if(!this.collapsed || this.el.hasActiveFx()) {
29865             return;
29866         }
29867         if(this.isSlid){
29868             this.afterSlideIn();
29869             skipAnim = true;
29870         }
29871         this.collapsed = false;
29872         if(this.config.animate && skipAnim !== true){
29873             this.animateExpand();
29874         }else{
29875             this.el.show();
29876             if(this.split){
29877                 this.split.el.show();
29878             }
29879             this.collapsedEl.setLocation(-2000,-2000);
29880             this.collapsedEl.hide();
29881             this.fireEvent("invalidated", this);
29882             this.fireEvent("expanded", this);
29883         }
29884     },
29885
29886     animateExpand : function(){
29887         // overridden
29888     },
29889
29890     initTabs : function()
29891     {
29892         this.bodyEl.setStyle("overflow", "hidden");
29893         var ts = new Roo.TabPanel(
29894                 this.bodyEl.dom,
29895                 {
29896                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29897                     disableTooltips: this.config.disableTabTips,
29898                     toolbar : this.config.toolbar
29899                 }
29900         );
29901         if(this.config.hideTabs){
29902             ts.stripWrap.setDisplayed(false);
29903         }
29904         this.tabs = ts;
29905         ts.resizeTabs = this.config.resizeTabs === true;
29906         ts.minTabWidth = this.config.minTabWidth || 40;
29907         ts.maxTabWidth = this.config.maxTabWidth || 250;
29908         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29909         ts.monitorResize = false;
29910         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29911         ts.bodyEl.addClass('x-layout-tabs-body');
29912         this.panels.each(this.initPanelAsTab, this);
29913     },
29914
29915     initPanelAsTab : function(panel){
29916         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29917                     this.config.closeOnTab && panel.isClosable());
29918         if(panel.tabTip !== undefined){
29919             ti.setTooltip(panel.tabTip);
29920         }
29921         ti.on("activate", function(){
29922               this.setActivePanel(panel);
29923         }, this);
29924         if(this.config.closeOnTab){
29925             ti.on("beforeclose", function(t, e){
29926                 e.cancel = true;
29927                 this.remove(panel);
29928             }, this);
29929         }
29930         return ti;
29931     },
29932
29933     updatePanelTitle : function(panel, title){
29934         if(this.activePanel == panel){
29935             this.updateTitle(title);
29936         }
29937         if(this.tabs){
29938             var ti = this.tabs.getTab(panel.getEl().id);
29939             ti.setText(title);
29940             if(panel.tabTip !== undefined){
29941                 ti.setTooltip(panel.tabTip);
29942             }
29943         }
29944     },
29945
29946     updateTitle : function(title){
29947         if(this.titleTextEl && !this.config.title){
29948             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29949         }
29950     },
29951
29952     setActivePanel : function(panel){
29953         panel = this.getPanel(panel);
29954         if(this.activePanel && this.activePanel != panel){
29955             this.activePanel.setActiveState(false);
29956         }
29957         this.activePanel = panel;
29958         panel.setActiveState(true);
29959         if(this.panelSize){
29960             panel.setSize(this.panelSize.width, this.panelSize.height);
29961         }
29962         if(this.closeBtn){
29963             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29964         }
29965         this.updateTitle(panel.getTitle());
29966         if(this.tabs){
29967             this.fireEvent("invalidated", this);
29968         }
29969         this.fireEvent("panelactivated", this, panel);
29970     },
29971
29972     /**
29973      * Shows the specified panel.
29974      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29975      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29976      */
29977     showPanel : function(panel)
29978     {
29979         panel = this.getPanel(panel);
29980         if(panel){
29981             if(this.tabs){
29982                 var tab = this.tabs.getTab(panel.getEl().id);
29983                 if(tab.isHidden()){
29984                     this.tabs.unhideTab(tab.id);
29985                 }
29986                 tab.activate();
29987             }else{
29988                 this.setActivePanel(panel);
29989             }
29990         }
29991         return panel;
29992     },
29993
29994     /**
29995      * Get the active panel for this region.
29996      * @return {Roo.ContentPanel} The active panel or null
29997      */
29998     getActivePanel : function(){
29999         return this.activePanel;
30000     },
30001
30002     validateVisibility : function(){
30003         if(this.panels.getCount() < 1){
30004             this.updateTitle("&#160;");
30005             this.closeBtn.hide();
30006             this.hide();
30007         }else{
30008             if(!this.isVisible()){
30009                 this.show();
30010             }
30011         }
30012     },
30013
30014     /**
30015      * Adds the passed ContentPanel(s) to this region.
30016      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30017      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30018      */
30019     add : function(panel){
30020         if(arguments.length > 1){
30021             for(var i = 0, len = arguments.length; i < len; i++) {
30022                 this.add(arguments[i]);
30023             }
30024             return null;
30025         }
30026         if(this.hasPanel(panel)){
30027             this.showPanel(panel);
30028             return panel;
30029         }
30030         panel.setRegion(this);
30031         this.panels.add(panel);
30032         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30033             this.bodyEl.dom.appendChild(panel.getEl().dom);
30034             if(panel.background !== true){
30035                 this.setActivePanel(panel);
30036             }
30037             this.fireEvent("paneladded", this, panel);
30038             return panel;
30039         }
30040         if(!this.tabs){
30041             this.initTabs();
30042         }else{
30043             this.initPanelAsTab(panel);
30044         }
30045         if(panel.background !== true){
30046             this.tabs.activate(panel.getEl().id);
30047         }
30048         this.fireEvent("paneladded", this, panel);
30049         return panel;
30050     },
30051
30052     /**
30053      * Hides the tab for the specified panel.
30054      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30055      */
30056     hidePanel : function(panel){
30057         if(this.tabs && (panel = this.getPanel(panel))){
30058             this.tabs.hideTab(panel.getEl().id);
30059         }
30060     },
30061
30062     /**
30063      * Unhides the tab for a previously hidden panel.
30064      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30065      */
30066     unhidePanel : function(panel){
30067         if(this.tabs && (panel = this.getPanel(panel))){
30068             this.tabs.unhideTab(panel.getEl().id);
30069         }
30070     },
30071
30072     clearPanels : function(){
30073         while(this.panels.getCount() > 0){
30074              this.remove(this.panels.first());
30075         }
30076     },
30077
30078     /**
30079      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30080      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30081      * @param {Boolean} preservePanel Overrides the config preservePanel option
30082      * @return {Roo.ContentPanel} The panel that was removed
30083      */
30084     remove : function(panel, preservePanel){
30085         panel = this.getPanel(panel);
30086         if(!panel){
30087             return null;
30088         }
30089         var e = {};
30090         this.fireEvent("beforeremove", this, panel, e);
30091         if(e.cancel === true){
30092             return null;
30093         }
30094         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30095         var panelId = panel.getId();
30096         this.panels.removeKey(panelId);
30097         if(preservePanel){
30098             document.body.appendChild(panel.getEl().dom);
30099         }
30100         if(this.tabs){
30101             this.tabs.removeTab(panel.getEl().id);
30102         }else if (!preservePanel){
30103             this.bodyEl.dom.removeChild(panel.getEl().dom);
30104         }
30105         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30106             var p = this.panels.first();
30107             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30108             tempEl.appendChild(p.getEl().dom);
30109             this.bodyEl.update("");
30110             this.bodyEl.dom.appendChild(p.getEl().dom);
30111             tempEl = null;
30112             this.updateTitle(p.getTitle());
30113             this.tabs = null;
30114             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30115             this.setActivePanel(p);
30116         }
30117         panel.setRegion(null);
30118         if(this.activePanel == panel){
30119             this.activePanel = null;
30120         }
30121         if(this.config.autoDestroy !== false && preservePanel !== true){
30122             try{panel.destroy();}catch(e){}
30123         }
30124         this.fireEvent("panelremoved", this, panel);
30125         return panel;
30126     },
30127
30128     /**
30129      * Returns the TabPanel component used by this region
30130      * @return {Roo.TabPanel}
30131      */
30132     getTabs : function(){
30133         return this.tabs;
30134     },
30135
30136     createTool : function(parentEl, className){
30137         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30138             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30139         btn.addClassOnOver("x-layout-tools-button-over");
30140         return btn;
30141     }
30142 });/*
30143  * Based on:
30144  * Ext JS Library 1.1.1
30145  * Copyright(c) 2006-2007, Ext JS, LLC.
30146  *
30147  * Originally Released Under LGPL - original licence link has changed is not relivant.
30148  *
30149  * Fork - LGPL
30150  * <script type="text/javascript">
30151  */
30152  
30153
30154
30155 /**
30156  * @class Roo.SplitLayoutRegion
30157  * @extends Roo.LayoutRegion
30158  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30159  */
30160 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30161     this.cursor = cursor;
30162     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30163 };
30164
30165 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30166     splitTip : "Drag to resize.",
30167     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30168     useSplitTips : false,
30169
30170     applyConfig : function(config){
30171         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30172         if(config.split){
30173             if(!this.split){
30174                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30175                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30176                 /** The SplitBar for this region 
30177                 * @type Roo.SplitBar */
30178                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30179                 this.split.on("moved", this.onSplitMove, this);
30180                 this.split.useShim = config.useShim === true;
30181                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30182                 if(this.useSplitTips){
30183                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30184                 }
30185                 if(config.collapsible){
30186                     this.split.el.on("dblclick", this.collapse,  this);
30187                 }
30188             }
30189             if(typeof config.minSize != "undefined"){
30190                 this.split.minSize = config.minSize;
30191             }
30192             if(typeof config.maxSize != "undefined"){
30193                 this.split.maxSize = config.maxSize;
30194             }
30195             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30196                 this.hideSplitter();
30197             }
30198         }
30199     },
30200
30201     getHMaxSize : function(){
30202          var cmax = this.config.maxSize || 10000;
30203          var center = this.mgr.getRegion("center");
30204          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30205     },
30206
30207     getVMaxSize : function(){
30208          var cmax = this.config.maxSize || 10000;
30209          var center = this.mgr.getRegion("center");
30210          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30211     },
30212
30213     onSplitMove : function(split, newSize){
30214         this.fireEvent("resized", this, newSize);
30215     },
30216     
30217     /** 
30218      * Returns the {@link Roo.SplitBar} for this region.
30219      * @return {Roo.SplitBar}
30220      */
30221     getSplitBar : function(){
30222         return this.split;
30223     },
30224     
30225     hide : function(){
30226         this.hideSplitter();
30227         Roo.SplitLayoutRegion.superclass.hide.call(this);
30228     },
30229
30230     hideSplitter : function(){
30231         if(this.split){
30232             this.split.el.setLocation(-2000,-2000);
30233             this.split.el.hide();
30234         }
30235     },
30236
30237     show : function(){
30238         if(this.split){
30239             this.split.el.show();
30240         }
30241         Roo.SplitLayoutRegion.superclass.show.call(this);
30242     },
30243     
30244     beforeSlide: function(){
30245         if(Roo.isGecko){// firefox overflow auto bug workaround
30246             this.bodyEl.clip();
30247             if(this.tabs) {
30248                 this.tabs.bodyEl.clip();
30249             }
30250             if(this.activePanel){
30251                 this.activePanel.getEl().clip();
30252                 
30253                 if(this.activePanel.beforeSlide){
30254                     this.activePanel.beforeSlide();
30255                 }
30256             }
30257         }
30258     },
30259     
30260     afterSlide : function(){
30261         if(Roo.isGecko){// firefox overflow auto bug workaround
30262             this.bodyEl.unclip();
30263             if(this.tabs) {
30264                 this.tabs.bodyEl.unclip();
30265             }
30266             if(this.activePanel){
30267                 this.activePanel.getEl().unclip();
30268                 if(this.activePanel.afterSlide){
30269                     this.activePanel.afterSlide();
30270                 }
30271             }
30272         }
30273     },
30274
30275     initAutoHide : function(){
30276         if(this.autoHide !== false){
30277             if(!this.autoHideHd){
30278                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30279                 this.autoHideHd = {
30280                     "mouseout": function(e){
30281                         if(!e.within(this.el, true)){
30282                             st.delay(500);
30283                         }
30284                     },
30285                     "mouseover" : function(e){
30286                         st.cancel();
30287                     },
30288                     scope : this
30289                 };
30290             }
30291             this.el.on(this.autoHideHd);
30292         }
30293     },
30294
30295     clearAutoHide : function(){
30296         if(this.autoHide !== false){
30297             this.el.un("mouseout", this.autoHideHd.mouseout);
30298             this.el.un("mouseover", this.autoHideHd.mouseover);
30299         }
30300     },
30301
30302     clearMonitor : function(){
30303         Roo.get(document).un("click", this.slideInIf, this);
30304     },
30305
30306     // these names are backwards but not changed for compat
30307     slideOut : function(){
30308         if(this.isSlid || this.el.hasActiveFx()){
30309             return;
30310         }
30311         this.isSlid = true;
30312         if(this.collapseBtn){
30313             this.collapseBtn.hide();
30314         }
30315         this.closeBtnState = this.closeBtn.getStyle('display');
30316         this.closeBtn.hide();
30317         if(this.stickBtn){
30318             this.stickBtn.show();
30319         }
30320         this.el.show();
30321         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30322         this.beforeSlide();
30323         this.el.setStyle("z-index", 10001);
30324         this.el.slideIn(this.getSlideAnchor(), {
30325             callback: function(){
30326                 this.afterSlide();
30327                 this.initAutoHide();
30328                 Roo.get(document).on("click", this.slideInIf, this);
30329                 this.fireEvent("slideshow", this);
30330             },
30331             scope: this,
30332             block: true
30333         });
30334     },
30335
30336     afterSlideIn : function(){
30337         this.clearAutoHide();
30338         this.isSlid = false;
30339         this.clearMonitor();
30340         this.el.setStyle("z-index", "");
30341         if(this.collapseBtn){
30342             this.collapseBtn.show();
30343         }
30344         this.closeBtn.setStyle('display', this.closeBtnState);
30345         if(this.stickBtn){
30346             this.stickBtn.hide();
30347         }
30348         this.fireEvent("slidehide", this);
30349     },
30350
30351     slideIn : function(cb){
30352         if(!this.isSlid || this.el.hasActiveFx()){
30353             Roo.callback(cb);
30354             return;
30355         }
30356         this.isSlid = false;
30357         this.beforeSlide();
30358         this.el.slideOut(this.getSlideAnchor(), {
30359             callback: function(){
30360                 this.el.setLeftTop(-10000, -10000);
30361                 this.afterSlide();
30362                 this.afterSlideIn();
30363                 Roo.callback(cb);
30364             },
30365             scope: this,
30366             block: true
30367         });
30368     },
30369     
30370     slideInIf : function(e){
30371         if(!e.within(this.el)){
30372             this.slideIn();
30373         }
30374     },
30375
30376     animateCollapse : function(){
30377         this.beforeSlide();
30378         this.el.setStyle("z-index", 20000);
30379         var anchor = this.getSlideAnchor();
30380         this.el.slideOut(anchor, {
30381             callback : function(){
30382                 this.el.setStyle("z-index", "");
30383                 this.collapsedEl.slideIn(anchor, {duration:.3});
30384                 this.afterSlide();
30385                 this.el.setLocation(-10000,-10000);
30386                 this.el.hide();
30387                 this.fireEvent("collapsed", this);
30388             },
30389             scope: this,
30390             block: true
30391         });
30392     },
30393
30394     animateExpand : function(){
30395         this.beforeSlide();
30396         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30397         this.el.setStyle("z-index", 20000);
30398         this.collapsedEl.hide({
30399             duration:.1
30400         });
30401         this.el.slideIn(this.getSlideAnchor(), {
30402             callback : function(){
30403                 this.el.setStyle("z-index", "");
30404                 this.afterSlide();
30405                 if(this.split){
30406                     this.split.el.show();
30407                 }
30408                 this.fireEvent("invalidated", this);
30409                 this.fireEvent("expanded", this);
30410             },
30411             scope: this,
30412             block: true
30413         });
30414     },
30415
30416     anchors : {
30417         "west" : "left",
30418         "east" : "right",
30419         "north" : "top",
30420         "south" : "bottom"
30421     },
30422
30423     sanchors : {
30424         "west" : "l",
30425         "east" : "r",
30426         "north" : "t",
30427         "south" : "b"
30428     },
30429
30430     canchors : {
30431         "west" : "tl-tr",
30432         "east" : "tr-tl",
30433         "north" : "tl-bl",
30434         "south" : "bl-tl"
30435     },
30436
30437     getAnchor : function(){
30438         return this.anchors[this.position];
30439     },
30440
30441     getCollapseAnchor : function(){
30442         return this.canchors[this.position];
30443     },
30444
30445     getSlideAnchor : function(){
30446         return this.sanchors[this.position];
30447     },
30448
30449     getAlignAdj : function(){
30450         var cm = this.cmargins;
30451         switch(this.position){
30452             case "west":
30453                 return [0, 0];
30454             break;
30455             case "east":
30456                 return [0, 0];
30457             break;
30458             case "north":
30459                 return [0, 0];
30460             break;
30461             case "south":
30462                 return [0, 0];
30463             break;
30464         }
30465     },
30466
30467     getExpandAdj : function(){
30468         var c = this.collapsedEl, cm = this.cmargins;
30469         switch(this.position){
30470             case "west":
30471                 return [-(cm.right+c.getWidth()+cm.left), 0];
30472             break;
30473             case "east":
30474                 return [cm.right+c.getWidth()+cm.left, 0];
30475             break;
30476             case "north":
30477                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30478             break;
30479             case "south":
30480                 return [0, cm.top+cm.bottom+c.getHeight()];
30481             break;
30482         }
30483     }
30484 });/*
30485  * Based on:
30486  * Ext JS Library 1.1.1
30487  * Copyright(c) 2006-2007, Ext JS, LLC.
30488  *
30489  * Originally Released Under LGPL - original licence link has changed is not relivant.
30490  *
30491  * Fork - LGPL
30492  * <script type="text/javascript">
30493  */
30494 /*
30495  * These classes are private internal classes
30496  */
30497 Roo.CenterLayoutRegion = function(mgr, config){
30498     Roo.LayoutRegion.call(this, mgr, config, "center");
30499     this.visible = true;
30500     this.minWidth = config.minWidth || 20;
30501     this.minHeight = config.minHeight || 20;
30502 };
30503
30504 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30505     hide : function(){
30506         // center panel can't be hidden
30507     },
30508     
30509     show : function(){
30510         // center panel can't be hidden
30511     },
30512     
30513     getMinWidth: function(){
30514         return this.minWidth;
30515     },
30516     
30517     getMinHeight: function(){
30518         return this.minHeight;
30519     }
30520 });
30521
30522
30523 Roo.NorthLayoutRegion = function(mgr, config){
30524     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30525     if(this.split){
30526         this.split.placement = Roo.SplitBar.TOP;
30527         this.split.orientation = Roo.SplitBar.VERTICAL;
30528         this.split.el.addClass("x-layout-split-v");
30529     }
30530     var size = config.initialSize || config.height;
30531     if(typeof size != "undefined"){
30532         this.el.setHeight(size);
30533     }
30534 };
30535 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30536     orientation: Roo.SplitBar.VERTICAL,
30537     getBox : function(){
30538         if(this.collapsed){
30539             return this.collapsedEl.getBox();
30540         }
30541         var box = this.el.getBox();
30542         if(this.split){
30543             box.height += this.split.el.getHeight();
30544         }
30545         return box;
30546     },
30547     
30548     updateBox : function(box){
30549         if(this.split && !this.collapsed){
30550             box.height -= this.split.el.getHeight();
30551             this.split.el.setLeft(box.x);
30552             this.split.el.setTop(box.y+box.height);
30553             this.split.el.setWidth(box.width);
30554         }
30555         if(this.collapsed){
30556             this.updateBody(box.width, null);
30557         }
30558         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30559     }
30560 });
30561
30562 Roo.SouthLayoutRegion = function(mgr, config){
30563     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30564     if(this.split){
30565         this.split.placement = Roo.SplitBar.BOTTOM;
30566         this.split.orientation = Roo.SplitBar.VERTICAL;
30567         this.split.el.addClass("x-layout-split-v");
30568     }
30569     var size = config.initialSize || config.height;
30570     if(typeof size != "undefined"){
30571         this.el.setHeight(size);
30572     }
30573 };
30574 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30575     orientation: Roo.SplitBar.VERTICAL,
30576     getBox : function(){
30577         if(this.collapsed){
30578             return this.collapsedEl.getBox();
30579         }
30580         var box = this.el.getBox();
30581         if(this.split){
30582             var sh = this.split.el.getHeight();
30583             box.height += sh;
30584             box.y -= sh;
30585         }
30586         return box;
30587     },
30588     
30589     updateBox : function(box){
30590         if(this.split && !this.collapsed){
30591             var sh = this.split.el.getHeight();
30592             box.height -= sh;
30593             box.y += sh;
30594             this.split.el.setLeft(box.x);
30595             this.split.el.setTop(box.y-sh);
30596             this.split.el.setWidth(box.width);
30597         }
30598         if(this.collapsed){
30599             this.updateBody(box.width, null);
30600         }
30601         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30602     }
30603 });
30604
30605 Roo.EastLayoutRegion = function(mgr, config){
30606     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30607     if(this.split){
30608         this.split.placement = Roo.SplitBar.RIGHT;
30609         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30610         this.split.el.addClass("x-layout-split-h");
30611     }
30612     var size = config.initialSize || config.width;
30613     if(typeof size != "undefined"){
30614         this.el.setWidth(size);
30615     }
30616 };
30617 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30618     orientation: Roo.SplitBar.HORIZONTAL,
30619     getBox : function(){
30620         if(this.collapsed){
30621             return this.collapsedEl.getBox();
30622         }
30623         var box = this.el.getBox();
30624         if(this.split){
30625             var sw = this.split.el.getWidth();
30626             box.width += sw;
30627             box.x -= sw;
30628         }
30629         return box;
30630     },
30631
30632     updateBox : function(box){
30633         if(this.split && !this.collapsed){
30634             var sw = this.split.el.getWidth();
30635             box.width -= sw;
30636             this.split.el.setLeft(box.x);
30637             this.split.el.setTop(box.y);
30638             this.split.el.setHeight(box.height);
30639             box.x += sw;
30640         }
30641         if(this.collapsed){
30642             this.updateBody(null, box.height);
30643         }
30644         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30645     }
30646 });
30647
30648 Roo.WestLayoutRegion = function(mgr, config){
30649     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30650     if(this.split){
30651         this.split.placement = Roo.SplitBar.LEFT;
30652         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30653         this.split.el.addClass("x-layout-split-h");
30654     }
30655     var size = config.initialSize || config.width;
30656     if(typeof size != "undefined"){
30657         this.el.setWidth(size);
30658     }
30659 };
30660 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30661     orientation: Roo.SplitBar.HORIZONTAL,
30662     getBox : function(){
30663         if(this.collapsed){
30664             return this.collapsedEl.getBox();
30665         }
30666         var box = this.el.getBox();
30667         if(this.split){
30668             box.width += this.split.el.getWidth();
30669         }
30670         return box;
30671     },
30672     
30673     updateBox : function(box){
30674         if(this.split && !this.collapsed){
30675             var sw = this.split.el.getWidth();
30676             box.width -= sw;
30677             this.split.el.setLeft(box.x+box.width);
30678             this.split.el.setTop(box.y);
30679             this.split.el.setHeight(box.height);
30680         }
30681         if(this.collapsed){
30682             this.updateBody(null, box.height);
30683         }
30684         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30685     }
30686 });
30687 /*
30688  * Based on:
30689  * Ext JS Library 1.1.1
30690  * Copyright(c) 2006-2007, Ext JS, LLC.
30691  *
30692  * Originally Released Under LGPL - original licence link has changed is not relivant.
30693  *
30694  * Fork - LGPL
30695  * <script type="text/javascript">
30696  */
30697  
30698  
30699 /*
30700  * Private internal class for reading and applying state
30701  */
30702 Roo.LayoutStateManager = function(layout){
30703      // default empty state
30704      this.state = {
30705         north: {},
30706         south: {},
30707         east: {},
30708         west: {}       
30709     };
30710 };
30711
30712 Roo.LayoutStateManager.prototype = {
30713     init : function(layout, provider){
30714         this.provider = provider;
30715         var state = provider.get(layout.id+"-layout-state");
30716         if(state){
30717             var wasUpdating = layout.isUpdating();
30718             if(!wasUpdating){
30719                 layout.beginUpdate();
30720             }
30721             for(var key in state){
30722                 if(typeof state[key] != "function"){
30723                     var rstate = state[key];
30724                     var r = layout.getRegion(key);
30725                     if(r && rstate){
30726                         if(rstate.size){
30727                             r.resizeTo(rstate.size);
30728                         }
30729                         if(rstate.collapsed == true){
30730                             r.collapse(true);
30731                         }else{
30732                             r.expand(null, true);
30733                         }
30734                     }
30735                 }
30736             }
30737             if(!wasUpdating){
30738                 layout.endUpdate();
30739             }
30740             this.state = state; 
30741         }
30742         this.layout = layout;
30743         layout.on("regionresized", this.onRegionResized, this);
30744         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30745         layout.on("regionexpanded", this.onRegionExpanded, this);
30746     },
30747     
30748     storeState : function(){
30749         this.provider.set(this.layout.id+"-layout-state", this.state);
30750     },
30751     
30752     onRegionResized : function(region, newSize){
30753         this.state[region.getPosition()].size = newSize;
30754         this.storeState();
30755     },
30756     
30757     onRegionCollapsed : function(region){
30758         this.state[region.getPosition()].collapsed = true;
30759         this.storeState();
30760     },
30761     
30762     onRegionExpanded : function(region){
30763         this.state[region.getPosition()].collapsed = false;
30764         this.storeState();
30765     }
30766 };/*
30767  * Based on:
30768  * Ext JS Library 1.1.1
30769  * Copyright(c) 2006-2007, Ext JS, LLC.
30770  *
30771  * Originally Released Under LGPL - original licence link has changed is not relivant.
30772  *
30773  * Fork - LGPL
30774  * <script type="text/javascript">
30775  */
30776 /**
30777  * @class Roo.ContentPanel
30778  * @extends Roo.util.Observable
30779  * A basic ContentPanel element.
30780  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30781  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30782  * @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
30783  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30784  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30785  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30786  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30787  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30788  * @cfg {String} title          The title for this panel
30789  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30790  * @cfg {String} url            Calls {@link #setUrl} with this value
30791  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30792  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30793  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30794  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30795
30796  * @constructor
30797  * Create a new ContentPanel.
30798  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30799  * @param {String/Object} config A string to set only the title or a config object
30800  * @param {String} content (optional) Set the HTML content for this panel
30801  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30802  */
30803 Roo.ContentPanel = function(el, config, content){
30804     
30805      
30806     /*
30807     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30808         config = el;
30809         el = Roo.id();
30810     }
30811     if (config && config.parentLayout) { 
30812         el = config.parentLayout.el.createChild(); 
30813     }
30814     */
30815     if(el.autoCreate){ // xtype is available if this is called from factory
30816         config = el;
30817         el = Roo.id();
30818     }
30819     this.el = Roo.get(el);
30820     if(!this.el && config && config.autoCreate){
30821         if(typeof config.autoCreate == "object"){
30822             if(!config.autoCreate.id){
30823                 config.autoCreate.id = config.id||el;
30824             }
30825             this.el = Roo.DomHelper.append(document.body,
30826                         config.autoCreate, true);
30827         }else{
30828             this.el = Roo.DomHelper.append(document.body,
30829                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30830         }
30831     }
30832     this.closable = false;
30833     this.loaded = false;
30834     this.active = false;
30835     if(typeof config == "string"){
30836         this.title = config;
30837     }else{
30838         Roo.apply(this, config);
30839     }
30840     
30841     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30842         this.wrapEl = this.el.wrap();
30843         this.toolbar.container = this.el.insertSibling(false, 'before');
30844         this.toolbar = new Roo.Toolbar(this.toolbar);
30845     }
30846     
30847     // xtype created footer. - not sure if will work as we normally have to render first..
30848     if (this.footer && !this.footer.el && this.footer.xtype) {
30849         if (!this.wrapEl) {
30850             this.wrapEl = this.el.wrap();
30851         }
30852     
30853         this.footer.container = this.wrapEl.createChild();
30854          
30855         this.footer = Roo.factory(this.footer, Roo);
30856         
30857     }
30858     
30859     if(this.resizeEl){
30860         this.resizeEl = Roo.get(this.resizeEl, true);
30861     }else{
30862         this.resizeEl = this.el;
30863     }
30864     // handle view.xtype
30865     
30866  
30867     
30868     
30869     this.addEvents({
30870         /**
30871          * @event activate
30872          * Fires when this panel is activated. 
30873          * @param {Roo.ContentPanel} this
30874          */
30875         "activate" : true,
30876         /**
30877          * @event deactivate
30878          * Fires when this panel is activated. 
30879          * @param {Roo.ContentPanel} this
30880          */
30881         "deactivate" : true,
30882
30883         /**
30884          * @event resize
30885          * Fires when this panel is resized if fitToFrame is true.
30886          * @param {Roo.ContentPanel} this
30887          * @param {Number} width The width after any component adjustments
30888          * @param {Number} height The height after any component adjustments
30889          */
30890         "resize" : true,
30891         
30892          /**
30893          * @event render
30894          * Fires when this tab is created
30895          * @param {Roo.ContentPanel} this
30896          */
30897         "render" : true
30898         
30899         
30900         
30901     });
30902     
30903
30904     
30905     
30906     if(this.autoScroll){
30907         this.resizeEl.setStyle("overflow", "auto");
30908     } else {
30909         // fix randome scrolling
30910         this.el.on('scroll', function() {
30911             Roo.log('fix random scolling');
30912             this.scrollTo('top',0); 
30913         });
30914     }
30915     content = content || this.content;
30916     if(content){
30917         this.setContent(content);
30918     }
30919     if(config && config.url){
30920         this.setUrl(this.url, this.params, this.loadOnce);
30921     }
30922     
30923     
30924     
30925     Roo.ContentPanel.superclass.constructor.call(this);
30926     
30927     if (this.view && typeof(this.view.xtype) != 'undefined') {
30928         this.view.el = this.el.appendChild(document.createElement("div"));
30929         this.view = Roo.factory(this.view); 
30930         this.view.render  &&  this.view.render(false, '');  
30931     }
30932     
30933     
30934     this.fireEvent('render', this);
30935 };
30936
30937 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30938     tabTip:'',
30939     setRegion : function(region){
30940         this.region = region;
30941         if(region){
30942            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30943         }else{
30944            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30945         } 
30946     },
30947     
30948     /**
30949      * Returns the toolbar for this Panel if one was configured. 
30950      * @return {Roo.Toolbar} 
30951      */
30952     getToolbar : function(){
30953         return this.toolbar;
30954     },
30955     
30956     setActiveState : function(active){
30957         this.active = active;
30958         if(!active){
30959             this.fireEvent("deactivate", this);
30960         }else{
30961             this.fireEvent("activate", this);
30962         }
30963     },
30964     /**
30965      * Updates this panel's element
30966      * @param {String} content The new content
30967      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30968     */
30969     setContent : function(content, loadScripts){
30970         this.el.update(content, loadScripts);
30971     },
30972
30973     ignoreResize : function(w, h){
30974         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30975             return true;
30976         }else{
30977             this.lastSize = {width: w, height: h};
30978             return false;
30979         }
30980     },
30981     /**
30982      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30983      * @return {Roo.UpdateManager} The UpdateManager
30984      */
30985     getUpdateManager : function(){
30986         return this.el.getUpdateManager();
30987     },
30988      /**
30989      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30990      * @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:
30991 <pre><code>
30992 panel.load({
30993     url: "your-url.php",
30994     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30995     callback: yourFunction,
30996     scope: yourObject, //(optional scope)
30997     discardUrl: false,
30998     nocache: false,
30999     text: "Loading...",
31000     timeout: 30,
31001     scripts: false
31002 });
31003 </code></pre>
31004      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31005      * 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.
31006      * @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}
31007      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31008      * @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.
31009      * @return {Roo.ContentPanel} this
31010      */
31011     load : function(){
31012         var um = this.el.getUpdateManager();
31013         um.update.apply(um, arguments);
31014         return this;
31015     },
31016
31017
31018     /**
31019      * 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.
31020      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31021      * @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)
31022      * @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)
31023      * @return {Roo.UpdateManager} The UpdateManager
31024      */
31025     setUrl : function(url, params, loadOnce){
31026         if(this.refreshDelegate){
31027             this.removeListener("activate", this.refreshDelegate);
31028         }
31029         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31030         this.on("activate", this.refreshDelegate);
31031         return this.el.getUpdateManager();
31032     },
31033     
31034     _handleRefresh : function(url, params, loadOnce){
31035         if(!loadOnce || !this.loaded){
31036             var updater = this.el.getUpdateManager();
31037             updater.update(url, params, this._setLoaded.createDelegate(this));
31038         }
31039     },
31040     
31041     _setLoaded : function(){
31042         this.loaded = true;
31043     }, 
31044     
31045     /**
31046      * Returns this panel's id
31047      * @return {String} 
31048      */
31049     getId : function(){
31050         return this.el.id;
31051     },
31052     
31053     /** 
31054      * Returns this panel's element - used by regiosn to add.
31055      * @return {Roo.Element} 
31056      */
31057     getEl : function(){
31058         return this.wrapEl || this.el;
31059     },
31060     
31061     adjustForComponents : function(width, height)
31062     {
31063         //Roo.log('adjustForComponents ');
31064         if(this.resizeEl != this.el){
31065             width -= this.el.getFrameWidth('lr');
31066             height -= this.el.getFrameWidth('tb');
31067         }
31068         if(this.toolbar){
31069             var te = this.toolbar.getEl();
31070             height -= te.getHeight();
31071             te.setWidth(width);
31072         }
31073         if(this.footer){
31074             var te = this.footer.getEl();
31075             Roo.log("footer:" + te.getHeight());
31076             
31077             height -= te.getHeight();
31078             te.setWidth(width);
31079         }
31080         
31081         
31082         if(this.adjustments){
31083             width += this.adjustments[0];
31084             height += this.adjustments[1];
31085         }
31086         return {"width": width, "height": height};
31087     },
31088     
31089     setSize : function(width, height){
31090         if(this.fitToFrame && !this.ignoreResize(width, height)){
31091             if(this.fitContainer && this.resizeEl != this.el){
31092                 this.el.setSize(width, height);
31093             }
31094             var size = this.adjustForComponents(width, height);
31095             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31096             this.fireEvent('resize', this, size.width, size.height);
31097         }
31098     },
31099     
31100     /**
31101      * Returns this panel's title
31102      * @return {String} 
31103      */
31104     getTitle : function(){
31105         return this.title;
31106     },
31107     
31108     /**
31109      * Set this panel's title
31110      * @param {String} title
31111      */
31112     setTitle : function(title){
31113         this.title = title;
31114         if(this.region){
31115             this.region.updatePanelTitle(this, title);
31116         }
31117     },
31118     
31119     /**
31120      * Returns true is this panel was configured to be closable
31121      * @return {Boolean} 
31122      */
31123     isClosable : function(){
31124         return this.closable;
31125     },
31126     
31127     beforeSlide : function(){
31128         this.el.clip();
31129         this.resizeEl.clip();
31130     },
31131     
31132     afterSlide : function(){
31133         this.el.unclip();
31134         this.resizeEl.unclip();
31135     },
31136     
31137     /**
31138      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31139      *   Will fail silently if the {@link #setUrl} method has not been called.
31140      *   This does not activate the panel, just updates its content.
31141      */
31142     refresh : function(){
31143         if(this.refreshDelegate){
31144            this.loaded = false;
31145            this.refreshDelegate();
31146         }
31147     },
31148     
31149     /**
31150      * Destroys this panel
31151      */
31152     destroy : function(){
31153         this.el.removeAllListeners();
31154         var tempEl = document.createElement("span");
31155         tempEl.appendChild(this.el.dom);
31156         tempEl.innerHTML = "";
31157         this.el.remove();
31158         this.el = null;
31159     },
31160     
31161     /**
31162      * form - if the content panel contains a form - this is a reference to it.
31163      * @type {Roo.form.Form}
31164      */
31165     form : false,
31166     /**
31167      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31168      *    This contains a reference to it.
31169      * @type {Roo.View}
31170      */
31171     view : false,
31172     
31173       /**
31174      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31175      * <pre><code>
31176
31177 layout.addxtype({
31178        xtype : 'Form',
31179        items: [ .... ]
31180    }
31181 );
31182
31183 </code></pre>
31184      * @param {Object} cfg Xtype definition of item to add.
31185      */
31186     
31187     addxtype : function(cfg) {
31188         // add form..
31189         if (cfg.xtype.match(/^Form$/)) {
31190             
31191             var el;
31192             //if (this.footer) {
31193             //    el = this.footer.container.insertSibling(false, 'before');
31194             //} else {
31195                 el = this.el.createChild();
31196             //}
31197
31198             this.form = new  Roo.form.Form(cfg);
31199             
31200             
31201             if ( this.form.allItems.length) {
31202                 this.form.render(el.dom);
31203             }
31204             return this.form;
31205         }
31206         // should only have one of theses..
31207         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31208             // views.. should not be just added - used named prop 'view''
31209             
31210             cfg.el = this.el.appendChild(document.createElement("div"));
31211             // factory?
31212             
31213             var ret = new Roo.factory(cfg);
31214              
31215              ret.render && ret.render(false, ''); // render blank..
31216             this.view = ret;
31217             return ret;
31218         }
31219         return false;
31220     }
31221 });
31222
31223 /**
31224  * @class Roo.GridPanel
31225  * @extends Roo.ContentPanel
31226  * @constructor
31227  * Create a new GridPanel.
31228  * @param {Roo.grid.Grid} grid The grid for this panel
31229  * @param {String/Object} config A string to set only the panel's title, or a config object
31230  */
31231 Roo.GridPanel = function(grid, config){
31232     
31233   
31234     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31235         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31236         
31237     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31238     
31239     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31240     
31241     if(this.toolbar){
31242         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31243     }
31244     // xtype created footer. - not sure if will work as we normally have to render first..
31245     if (this.footer && !this.footer.el && this.footer.xtype) {
31246         
31247         this.footer.container = this.grid.getView().getFooterPanel(true);
31248         this.footer.dataSource = this.grid.dataSource;
31249         this.footer = Roo.factory(this.footer, Roo);
31250         
31251     }
31252     
31253     grid.monitorWindowResize = false; // turn off autosizing
31254     grid.autoHeight = false;
31255     grid.autoWidth = false;
31256     this.grid = grid;
31257     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31258 };
31259
31260 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31261     getId : function(){
31262         return this.grid.id;
31263     },
31264     
31265     /**
31266      * Returns the grid for this panel
31267      * @return {Roo.grid.Grid} 
31268      */
31269     getGrid : function(){
31270         return this.grid;    
31271     },
31272     
31273     setSize : function(width, height){
31274         if(!this.ignoreResize(width, height)){
31275             var grid = this.grid;
31276             var size = this.adjustForComponents(width, height);
31277             grid.getGridEl().setSize(size.width, size.height);
31278             grid.autoSize();
31279         }
31280     },
31281     
31282     beforeSlide : function(){
31283         this.grid.getView().scroller.clip();
31284     },
31285     
31286     afterSlide : function(){
31287         this.grid.getView().scroller.unclip();
31288     },
31289     
31290     destroy : function(){
31291         this.grid.destroy();
31292         delete this.grid;
31293         Roo.GridPanel.superclass.destroy.call(this); 
31294     }
31295 });
31296
31297
31298 /**
31299  * @class Roo.NestedLayoutPanel
31300  * @extends Roo.ContentPanel
31301  * @constructor
31302  * Create a new NestedLayoutPanel.
31303  * 
31304  * 
31305  * @param {Roo.BorderLayout} layout The layout for this panel
31306  * @param {String/Object} config A string to set only the title or a config object
31307  */
31308 Roo.NestedLayoutPanel = function(layout, config)
31309 {
31310     // construct with only one argument..
31311     /* FIXME - implement nicer consturctors
31312     if (layout.layout) {
31313         config = layout;
31314         layout = config.layout;
31315         delete config.layout;
31316     }
31317     if (layout.xtype && !layout.getEl) {
31318         // then layout needs constructing..
31319         layout = Roo.factory(layout, Roo);
31320     }
31321     */
31322     
31323     
31324     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31325     
31326     layout.monitorWindowResize = false; // turn off autosizing
31327     this.layout = layout;
31328     this.layout.getEl().addClass("x-layout-nested-layout");
31329     
31330     
31331     
31332     
31333 };
31334
31335 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31336
31337     setSize : function(width, height){
31338         if(!this.ignoreResize(width, height)){
31339             var size = this.adjustForComponents(width, height);
31340             var el = this.layout.getEl();
31341             el.setSize(size.width, size.height);
31342             var touch = el.dom.offsetWidth;
31343             this.layout.layout();
31344             // ie requires a double layout on the first pass
31345             if(Roo.isIE && !this.initialized){
31346                 this.initialized = true;
31347                 this.layout.layout();
31348             }
31349         }
31350     },
31351     
31352     // activate all subpanels if not currently active..
31353     
31354     setActiveState : function(active){
31355         this.active = active;
31356         if(!active){
31357             this.fireEvent("deactivate", this);
31358             return;
31359         }
31360         
31361         this.fireEvent("activate", this);
31362         // not sure if this should happen before or after..
31363         if (!this.layout) {
31364             return; // should not happen..
31365         }
31366         var reg = false;
31367         for (var r in this.layout.regions) {
31368             reg = this.layout.getRegion(r);
31369             if (reg.getActivePanel()) {
31370                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31371                 reg.setActivePanel(reg.getActivePanel());
31372                 continue;
31373             }
31374             if (!reg.panels.length) {
31375                 continue;
31376             }
31377             reg.showPanel(reg.getPanel(0));
31378         }
31379         
31380         
31381         
31382         
31383     },
31384     
31385     /**
31386      * Returns the nested BorderLayout for this panel
31387      * @return {Roo.BorderLayout} 
31388      */
31389     getLayout : function(){
31390         return this.layout;
31391     },
31392     
31393      /**
31394      * Adds a xtype elements to the layout of the nested panel
31395      * <pre><code>
31396
31397 panel.addxtype({
31398        xtype : 'ContentPanel',
31399        region: 'west',
31400        items: [ .... ]
31401    }
31402 );
31403
31404 panel.addxtype({
31405         xtype : 'NestedLayoutPanel',
31406         region: 'west',
31407         layout: {
31408            center: { },
31409            west: { }   
31410         },
31411         items : [ ... list of content panels or nested layout panels.. ]
31412    }
31413 );
31414 </code></pre>
31415      * @param {Object} cfg Xtype definition of item to add.
31416      */
31417     addxtype : function(cfg) {
31418         return this.layout.addxtype(cfg);
31419     
31420     }
31421 });
31422
31423 Roo.ScrollPanel = function(el, config, content){
31424     config = config || {};
31425     config.fitToFrame = true;
31426     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31427     
31428     this.el.dom.style.overflow = "hidden";
31429     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31430     this.el.removeClass("x-layout-inactive-content");
31431     this.el.on("mousewheel", this.onWheel, this);
31432
31433     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31434     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31435     up.unselectable(); down.unselectable();
31436     up.on("click", this.scrollUp, this);
31437     down.on("click", this.scrollDown, this);
31438     up.addClassOnOver("x-scroller-btn-over");
31439     down.addClassOnOver("x-scroller-btn-over");
31440     up.addClassOnClick("x-scroller-btn-click");
31441     down.addClassOnClick("x-scroller-btn-click");
31442     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31443
31444     this.resizeEl = this.el;
31445     this.el = wrap; this.up = up; this.down = down;
31446 };
31447
31448 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31449     increment : 100,
31450     wheelIncrement : 5,
31451     scrollUp : function(){
31452         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31453     },
31454
31455     scrollDown : function(){
31456         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31457     },
31458
31459     afterScroll : function(){
31460         var el = this.resizeEl;
31461         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31462         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31463         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31464     },
31465
31466     setSize : function(){
31467         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31468         this.afterScroll();
31469     },
31470
31471     onWheel : function(e){
31472         var d = e.getWheelDelta();
31473         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31474         this.afterScroll();
31475         e.stopEvent();
31476     },
31477
31478     setContent : function(content, loadScripts){
31479         this.resizeEl.update(content, loadScripts);
31480     }
31481
31482 });
31483
31484
31485
31486
31487
31488
31489
31490
31491
31492 /**
31493  * @class Roo.TreePanel
31494  * @extends Roo.ContentPanel
31495  * @constructor
31496  * Create a new TreePanel. - defaults to fit/scoll contents.
31497  * @param {String/Object} config A string to set only the panel's title, or a config object
31498  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31499  */
31500 Roo.TreePanel = function(config){
31501     var el = config.el;
31502     var tree = config.tree;
31503     delete config.tree; 
31504     delete config.el; // hopefull!
31505     
31506     // wrapper for IE7 strict & safari scroll issue
31507     
31508     var treeEl = el.createChild();
31509     config.resizeEl = treeEl;
31510     
31511     
31512     
31513     Roo.TreePanel.superclass.constructor.call(this, el, config);
31514  
31515  
31516     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31517     //console.log(tree);
31518     this.on('activate', function()
31519     {
31520         if (this.tree.rendered) {
31521             return;
31522         }
31523         //console.log('render tree');
31524         this.tree.render();
31525     });
31526     // this should not be needed.. - it's actually the 'el' that resizes?
31527     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31528     
31529     //this.on('resize',  function (cp, w, h) {
31530     //        this.tree.innerCt.setWidth(w);
31531     //        this.tree.innerCt.setHeight(h);
31532     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31533     //});
31534
31535         
31536     
31537 };
31538
31539 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31540     fitToFrame : true,
31541     autoScroll : true
31542 });
31543
31544
31545
31546
31547
31548
31549
31550
31551
31552
31553
31554 /*
31555  * Based on:
31556  * Ext JS Library 1.1.1
31557  * Copyright(c) 2006-2007, Ext JS, LLC.
31558  *
31559  * Originally Released Under LGPL - original licence link has changed is not relivant.
31560  *
31561  * Fork - LGPL
31562  * <script type="text/javascript">
31563  */
31564  
31565
31566 /**
31567  * @class Roo.ReaderLayout
31568  * @extends Roo.BorderLayout
31569  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31570  * center region containing two nested regions (a top one for a list view and one for item preview below),
31571  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31572  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31573  * expedites the setup of the overall layout and regions for this common application style.
31574  * Example:
31575  <pre><code>
31576 var reader = new Roo.ReaderLayout();
31577 var CP = Roo.ContentPanel;  // shortcut for adding
31578
31579 reader.beginUpdate();
31580 reader.add("north", new CP("north", "North"));
31581 reader.add("west", new CP("west", {title: "West"}));
31582 reader.add("east", new CP("east", {title: "East"}));
31583
31584 reader.regions.listView.add(new CP("listView", "List"));
31585 reader.regions.preview.add(new CP("preview", "Preview"));
31586 reader.endUpdate();
31587 </code></pre>
31588 * @constructor
31589 * Create a new ReaderLayout
31590 * @param {Object} config Configuration options
31591 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31592 * document.body if omitted)
31593 */
31594 Roo.ReaderLayout = function(config, renderTo){
31595     var c = config || {size:{}};
31596     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31597         north: c.north !== false ? Roo.apply({
31598             split:false,
31599             initialSize: 32,
31600             titlebar: false
31601         }, c.north) : false,
31602         west: c.west !== false ? Roo.apply({
31603             split:true,
31604             initialSize: 200,
31605             minSize: 175,
31606             maxSize: 400,
31607             titlebar: true,
31608             collapsible: true,
31609             animate: true,
31610             margins:{left:5,right:0,bottom:5,top:5},
31611             cmargins:{left:5,right:5,bottom:5,top:5}
31612         }, c.west) : false,
31613         east: c.east !== false ? Roo.apply({
31614             split:true,
31615             initialSize: 200,
31616             minSize: 175,
31617             maxSize: 400,
31618             titlebar: true,
31619             collapsible: true,
31620             animate: true,
31621             margins:{left:0,right:5,bottom:5,top:5},
31622             cmargins:{left:5,right:5,bottom:5,top:5}
31623         }, c.east) : false,
31624         center: Roo.apply({
31625             tabPosition: 'top',
31626             autoScroll:false,
31627             closeOnTab: true,
31628             titlebar:false,
31629             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31630         }, c.center)
31631     });
31632
31633     this.el.addClass('x-reader');
31634
31635     this.beginUpdate();
31636
31637     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31638         south: c.preview !== false ? Roo.apply({
31639             split:true,
31640             initialSize: 200,
31641             minSize: 100,
31642             autoScroll:true,
31643             collapsible:true,
31644             titlebar: true,
31645             cmargins:{top:5,left:0, right:0, bottom:0}
31646         }, c.preview) : false,
31647         center: Roo.apply({
31648             autoScroll:false,
31649             titlebar:false,
31650             minHeight:200
31651         }, c.listView)
31652     });
31653     this.add('center', new Roo.NestedLayoutPanel(inner,
31654             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31655
31656     this.endUpdate();
31657
31658     this.regions.preview = inner.getRegion('south');
31659     this.regions.listView = inner.getRegion('center');
31660 };
31661
31662 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31663  * Based on:
31664  * Ext JS Library 1.1.1
31665  * Copyright(c) 2006-2007, Ext JS, LLC.
31666  *
31667  * Originally Released Under LGPL - original licence link has changed is not relivant.
31668  *
31669  * Fork - LGPL
31670  * <script type="text/javascript">
31671  */
31672  
31673 /**
31674  * @class Roo.grid.Grid
31675  * @extends Roo.util.Observable
31676  * This class represents the primary interface of a component based grid control.
31677  * <br><br>Usage:<pre><code>
31678  var grid = new Roo.grid.Grid("my-container-id", {
31679      ds: myDataStore,
31680      cm: myColModel,
31681      selModel: mySelectionModel,
31682      autoSizeColumns: true,
31683      monitorWindowResize: false,
31684      trackMouseOver: true
31685  });
31686  // set any options
31687  grid.render();
31688  * </code></pre>
31689  * <b>Common Problems:</b><br/>
31690  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31691  * element will correct this<br/>
31692  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31693  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31694  * are unpredictable.<br/>
31695  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31696  * grid to calculate dimensions/offsets.<br/>
31697   * @constructor
31698  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31699  * The container MUST have some type of size defined for the grid to fill. The container will be
31700  * automatically set to position relative if it isn't already.
31701  * @param {Object} config A config object that sets properties on this grid.
31702  */
31703 Roo.grid.Grid = function(container, config){
31704         // initialize the container
31705         this.container = Roo.get(container);
31706         this.container.update("");
31707         this.container.setStyle("overflow", "hidden");
31708     this.container.addClass('x-grid-container');
31709
31710     this.id = this.container.id;
31711
31712     Roo.apply(this, config);
31713     // check and correct shorthanded configs
31714     if(this.ds){
31715         this.dataSource = this.ds;
31716         delete this.ds;
31717     }
31718     if(this.cm){
31719         this.colModel = this.cm;
31720         delete this.cm;
31721     }
31722     if(this.sm){
31723         this.selModel = this.sm;
31724         delete this.sm;
31725     }
31726
31727     if (this.selModel) {
31728         this.selModel = Roo.factory(this.selModel, Roo.grid);
31729         this.sm = this.selModel;
31730         this.sm.xmodule = this.xmodule || false;
31731     }
31732     if (typeof(this.colModel.config) == 'undefined') {
31733         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31734         this.cm = this.colModel;
31735         this.cm.xmodule = this.xmodule || false;
31736     }
31737     if (this.dataSource) {
31738         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31739         this.ds = this.dataSource;
31740         this.ds.xmodule = this.xmodule || false;
31741          
31742     }
31743     
31744     
31745     
31746     if(this.width){
31747         this.container.setWidth(this.width);
31748     }
31749
31750     if(this.height){
31751         this.container.setHeight(this.height);
31752     }
31753     /** @private */
31754         this.addEvents({
31755         // raw events
31756         /**
31757          * @event click
31758          * The raw click event for the entire grid.
31759          * @param {Roo.EventObject} e
31760          */
31761         "click" : true,
31762         /**
31763          * @event dblclick
31764          * The raw dblclick event for the entire grid.
31765          * @param {Roo.EventObject} e
31766          */
31767         "dblclick" : true,
31768         /**
31769          * @event contextmenu
31770          * The raw contextmenu event for the entire grid.
31771          * @param {Roo.EventObject} e
31772          */
31773         "contextmenu" : true,
31774         /**
31775          * @event mousedown
31776          * The raw mousedown event for the entire grid.
31777          * @param {Roo.EventObject} e
31778          */
31779         "mousedown" : true,
31780         /**
31781          * @event mouseup
31782          * The raw mouseup event for the entire grid.
31783          * @param {Roo.EventObject} e
31784          */
31785         "mouseup" : true,
31786         /**
31787          * @event mouseover
31788          * The raw mouseover event for the entire grid.
31789          * @param {Roo.EventObject} e
31790          */
31791         "mouseover" : true,
31792         /**
31793          * @event mouseout
31794          * The raw mouseout event for the entire grid.
31795          * @param {Roo.EventObject} e
31796          */
31797         "mouseout" : true,
31798         /**
31799          * @event keypress
31800          * The raw keypress event for the entire grid.
31801          * @param {Roo.EventObject} e
31802          */
31803         "keypress" : true,
31804         /**
31805          * @event keydown
31806          * The raw keydown event for the entire grid.
31807          * @param {Roo.EventObject} e
31808          */
31809         "keydown" : true,
31810
31811         // custom events
31812
31813         /**
31814          * @event cellclick
31815          * Fires when a cell is clicked
31816          * @param {Grid} this
31817          * @param {Number} rowIndex
31818          * @param {Number} columnIndex
31819          * @param {Roo.EventObject} e
31820          */
31821         "cellclick" : true,
31822         /**
31823          * @event celldblclick
31824          * Fires when a cell is double clicked
31825          * @param {Grid} this
31826          * @param {Number} rowIndex
31827          * @param {Number} columnIndex
31828          * @param {Roo.EventObject} e
31829          */
31830         "celldblclick" : true,
31831         /**
31832          * @event rowclick
31833          * Fires when a row is clicked
31834          * @param {Grid} this
31835          * @param {Number} rowIndex
31836          * @param {Roo.EventObject} e
31837          */
31838         "rowclick" : true,
31839         /**
31840          * @event rowdblclick
31841          * Fires when a row is double clicked
31842          * @param {Grid} this
31843          * @param {Number} rowIndex
31844          * @param {Roo.EventObject} e
31845          */
31846         "rowdblclick" : true,
31847         /**
31848          * @event headerclick
31849          * Fires when a header is clicked
31850          * @param {Grid} this
31851          * @param {Number} columnIndex
31852          * @param {Roo.EventObject} e
31853          */
31854         "headerclick" : true,
31855         /**
31856          * @event headerdblclick
31857          * Fires when a header cell is double clicked
31858          * @param {Grid} this
31859          * @param {Number} columnIndex
31860          * @param {Roo.EventObject} e
31861          */
31862         "headerdblclick" : true,
31863         /**
31864          * @event rowcontextmenu
31865          * Fires when a row is right clicked
31866          * @param {Grid} this
31867          * @param {Number} rowIndex
31868          * @param {Roo.EventObject} e
31869          */
31870         "rowcontextmenu" : true,
31871         /**
31872          * @event cellcontextmenu
31873          * Fires when a cell is right clicked
31874          * @param {Grid} this
31875          * @param {Number} rowIndex
31876          * @param {Number} cellIndex
31877          * @param {Roo.EventObject} e
31878          */
31879          "cellcontextmenu" : true,
31880         /**
31881          * @event headercontextmenu
31882          * Fires when a header is right clicked
31883          * @param {Grid} this
31884          * @param {Number} columnIndex
31885          * @param {Roo.EventObject} e
31886          */
31887         "headercontextmenu" : true,
31888         /**
31889          * @event bodyscroll
31890          * Fires when the body element is scrolled
31891          * @param {Number} scrollLeft
31892          * @param {Number} scrollTop
31893          */
31894         "bodyscroll" : true,
31895         /**
31896          * @event columnresize
31897          * Fires when the user resizes a column
31898          * @param {Number} columnIndex
31899          * @param {Number} newSize
31900          */
31901         "columnresize" : true,
31902         /**
31903          * @event columnmove
31904          * Fires when the user moves a column
31905          * @param {Number} oldIndex
31906          * @param {Number} newIndex
31907          */
31908         "columnmove" : true,
31909         /**
31910          * @event startdrag
31911          * Fires when row(s) start being dragged
31912          * @param {Grid} this
31913          * @param {Roo.GridDD} dd The drag drop object
31914          * @param {event} e The raw browser event
31915          */
31916         "startdrag" : true,
31917         /**
31918          * @event enddrag
31919          * Fires when a drag operation is complete
31920          * @param {Grid} this
31921          * @param {Roo.GridDD} dd The drag drop object
31922          * @param {event} e The raw browser event
31923          */
31924         "enddrag" : true,
31925         /**
31926          * @event dragdrop
31927          * Fires when dragged row(s) are dropped on a valid DD target
31928          * @param {Grid} this
31929          * @param {Roo.GridDD} dd The drag drop object
31930          * @param {String} targetId The target drag drop object
31931          * @param {event} e The raw browser event
31932          */
31933         "dragdrop" : true,
31934         /**
31935          * @event dragover
31936          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31937          * @param {Grid} this
31938          * @param {Roo.GridDD} dd The drag drop object
31939          * @param {String} targetId The target drag drop object
31940          * @param {event} e The raw browser event
31941          */
31942         "dragover" : true,
31943         /**
31944          * @event dragenter
31945          *  Fires when the dragged row(s) first cross another DD target while being dragged
31946          * @param {Grid} this
31947          * @param {Roo.GridDD} dd The drag drop object
31948          * @param {String} targetId The target drag drop object
31949          * @param {event} e The raw browser event
31950          */
31951         "dragenter" : true,
31952         /**
31953          * @event dragout
31954          * Fires when the dragged row(s) leave another DD target while being dragged
31955          * @param {Grid} this
31956          * @param {Roo.GridDD} dd The drag drop object
31957          * @param {String} targetId The target drag drop object
31958          * @param {event} e The raw browser event
31959          */
31960         "dragout" : true,
31961         /**
31962          * @event rowclass
31963          * Fires when a row is rendered, so you can change add a style to it.
31964          * @param {GridView} gridview   The grid view
31965          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31966          */
31967         'rowclass' : true,
31968
31969         /**
31970          * @event render
31971          * Fires when the grid is rendered
31972          * @param {Grid} grid
31973          */
31974         'render' : true
31975     });
31976
31977     Roo.grid.Grid.superclass.constructor.call(this);
31978 };
31979 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31980     
31981     /**
31982      * @cfg {String} ddGroup - drag drop group.
31983      */
31984
31985     /**
31986      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31987      */
31988     minColumnWidth : 25,
31989
31990     /**
31991      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31992      * <b>on initial render.</b> It is more efficient to explicitly size the columns
31993      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31994      */
31995     autoSizeColumns : false,
31996
31997     /**
31998      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31999      */
32000     autoSizeHeaders : true,
32001
32002     /**
32003      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32004      */
32005     monitorWindowResize : true,
32006
32007     /**
32008      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32009      * rows measured to get a columns size. Default is 0 (all rows).
32010      */
32011     maxRowsToMeasure : 0,
32012
32013     /**
32014      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32015      */
32016     trackMouseOver : true,
32017
32018     /**
32019     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32020     */
32021     
32022     /**
32023     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32024     */
32025     enableDragDrop : false,
32026     
32027     /**
32028     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32029     */
32030     enableColumnMove : true,
32031     
32032     /**
32033     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32034     */
32035     enableColumnHide : true,
32036     
32037     /**
32038     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32039     */
32040     enableRowHeightSync : false,
32041     
32042     /**
32043     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32044     */
32045     stripeRows : true,
32046     
32047     /**
32048     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32049     */
32050     autoHeight : false,
32051
32052     /**
32053      * @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.
32054      */
32055     autoExpandColumn : false,
32056
32057     /**
32058     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32059     * Default is 50.
32060     */
32061     autoExpandMin : 50,
32062
32063     /**
32064     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32065     */
32066     autoExpandMax : 1000,
32067
32068     /**
32069     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32070     */
32071     view : null,
32072
32073     /**
32074     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32075     */
32076     loadMask : false,
32077     /**
32078     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32079     */
32080     dropTarget: false,
32081     
32082    
32083     
32084     // private
32085     rendered : false,
32086
32087     /**
32088     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32089     * of a fixed width. Default is false.
32090     */
32091     /**
32092     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32093     */
32094     /**
32095      * Called once after all setup has been completed and the grid is ready to be rendered.
32096      * @return {Roo.grid.Grid} this
32097      */
32098     render : function()
32099     {
32100         var c = this.container;
32101         // try to detect autoHeight/width mode
32102         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32103             this.autoHeight = true;
32104         }
32105         var view = this.getView();
32106         view.init(this);
32107
32108         c.on("click", this.onClick, this);
32109         c.on("dblclick", this.onDblClick, this);
32110         c.on("contextmenu", this.onContextMenu, this);
32111         c.on("keydown", this.onKeyDown, this);
32112         if (Roo.isTouch) {
32113             c.on("touchstart", this.onTouchStart, this);
32114         }
32115
32116         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32117
32118         this.getSelectionModel().init(this);
32119
32120         view.render();
32121
32122         if(this.loadMask){
32123             this.loadMask = new Roo.LoadMask(this.container,
32124                     Roo.apply({store:this.dataSource}, this.loadMask));
32125         }
32126         
32127         
32128         if (this.toolbar && this.toolbar.xtype) {
32129             this.toolbar.container = this.getView().getHeaderPanel(true);
32130             this.toolbar = new Roo.Toolbar(this.toolbar);
32131         }
32132         if (this.footer && this.footer.xtype) {
32133             this.footer.dataSource = this.getDataSource();
32134             this.footer.container = this.getView().getFooterPanel(true);
32135             this.footer = Roo.factory(this.footer, Roo);
32136         }
32137         if (this.dropTarget && this.dropTarget.xtype) {
32138             delete this.dropTarget.xtype;
32139             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32140         }
32141         
32142         
32143         this.rendered = true;
32144         this.fireEvent('render', this);
32145         return this;
32146     },
32147
32148         /**
32149          * Reconfigures the grid to use a different Store and Column Model.
32150          * The View will be bound to the new objects and refreshed.
32151          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32152          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32153          */
32154     reconfigure : function(dataSource, colModel){
32155         if(this.loadMask){
32156             this.loadMask.destroy();
32157             this.loadMask = new Roo.LoadMask(this.container,
32158                     Roo.apply({store:dataSource}, this.loadMask));
32159         }
32160         this.view.bind(dataSource, colModel);
32161         this.dataSource = dataSource;
32162         this.colModel = colModel;
32163         this.view.refresh(true);
32164     },
32165
32166     // private
32167     onKeyDown : function(e){
32168         this.fireEvent("keydown", e);
32169     },
32170
32171     /**
32172      * Destroy this grid.
32173      * @param {Boolean} removeEl True to remove the element
32174      */
32175     destroy : function(removeEl, keepListeners){
32176         if(this.loadMask){
32177             this.loadMask.destroy();
32178         }
32179         var c = this.container;
32180         c.removeAllListeners();
32181         this.view.destroy();
32182         this.colModel.purgeListeners();
32183         if(!keepListeners){
32184             this.purgeListeners();
32185         }
32186         c.update("");
32187         if(removeEl === true){
32188             c.remove();
32189         }
32190     },
32191
32192     // private
32193     processEvent : function(name, e){
32194         // does this fire select???
32195         //Roo.log('grid:processEvent '  + name);
32196         
32197         if (name != 'touchstart' ) {
32198             this.fireEvent(name, e);    
32199         }
32200         
32201         var t = e.getTarget();
32202         var v = this.view;
32203         var header = v.findHeaderIndex(t);
32204         if(header !== false){
32205             var ename = name == 'touchstart' ? 'click' : name;
32206              
32207             this.fireEvent("header" + ename, this, header, e);
32208         }else{
32209             var row = v.findRowIndex(t);
32210             var cell = v.findCellIndex(t);
32211             if (name == 'touchstart') {
32212                 // first touch is always a click.
32213                 // hopefull this happens after selection is updated.?
32214                 name = false;
32215                 
32216                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32217                     var cs = this.selModel.getSelectedCell();
32218                     if (row == cs[0] && cell == cs[1]){
32219                         name = 'dblclick';
32220                     }
32221                 }
32222                 if (typeof(this.selModel.getSelections) != 'undefined') {
32223                     var cs = this.selModel.getSelections();
32224                     var ds = this.dataSource;
32225                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32226                         name = 'dblclick';
32227                     }
32228                 }
32229                 if (!name) {
32230                     return;
32231                 }
32232             }
32233             
32234             
32235             if(row !== false){
32236                 this.fireEvent("row" + name, this, row, e);
32237                 if(cell !== false){
32238                     this.fireEvent("cell" + name, this, row, cell, e);
32239                 }
32240             }
32241         }
32242     },
32243
32244     // private
32245     onClick : function(e){
32246         this.processEvent("click", e);
32247     },
32248    // private
32249     onTouchStart : function(e){
32250         this.processEvent("touchstart", e);
32251     },
32252
32253     // private
32254     onContextMenu : function(e, t){
32255         this.processEvent("contextmenu", e);
32256     },
32257
32258     // private
32259     onDblClick : function(e){
32260         this.processEvent("dblclick", e);
32261     },
32262
32263     // private
32264     walkCells : function(row, col, step, fn, scope){
32265         var cm = this.colModel, clen = cm.getColumnCount();
32266         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32267         if(step < 0){
32268             if(col < 0){
32269                 row--;
32270                 first = false;
32271             }
32272             while(row >= 0){
32273                 if(!first){
32274                     col = clen-1;
32275                 }
32276                 first = false;
32277                 while(col >= 0){
32278                     if(fn.call(scope || this, row, col, cm) === true){
32279                         return [row, col];
32280                     }
32281                     col--;
32282                 }
32283                 row--;
32284             }
32285         } else {
32286             if(col >= clen){
32287                 row++;
32288                 first = false;
32289             }
32290             while(row < rlen){
32291                 if(!first){
32292                     col = 0;
32293                 }
32294                 first = false;
32295                 while(col < clen){
32296                     if(fn.call(scope || this, row, col, cm) === true){
32297                         return [row, col];
32298                     }
32299                     col++;
32300                 }
32301                 row++;
32302             }
32303         }
32304         return null;
32305     },
32306
32307     // private
32308     getSelections : function(){
32309         return this.selModel.getSelections();
32310     },
32311
32312     /**
32313      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32314      * but if manual update is required this method will initiate it.
32315      */
32316     autoSize : function(){
32317         if(this.rendered){
32318             this.view.layout();
32319             if(this.view.adjustForScroll){
32320                 this.view.adjustForScroll();
32321             }
32322         }
32323     },
32324
32325     /**
32326      * Returns the grid's underlying element.
32327      * @return {Element} The element
32328      */
32329     getGridEl : function(){
32330         return this.container;
32331     },
32332
32333     // private for compatibility, overridden by editor grid
32334     stopEditing : function(){},
32335
32336     /**
32337      * Returns the grid's SelectionModel.
32338      * @return {SelectionModel}
32339      */
32340     getSelectionModel : function(){
32341         if(!this.selModel){
32342             this.selModel = new Roo.grid.RowSelectionModel();
32343         }
32344         return this.selModel;
32345     },
32346
32347     /**
32348      * Returns the grid's DataSource.
32349      * @return {DataSource}
32350      */
32351     getDataSource : function(){
32352         return this.dataSource;
32353     },
32354
32355     /**
32356      * Returns the grid's ColumnModel.
32357      * @return {ColumnModel}
32358      */
32359     getColumnModel : function(){
32360         return this.colModel;
32361     },
32362
32363     /**
32364      * Returns the grid's GridView object.
32365      * @return {GridView}
32366      */
32367     getView : function(){
32368         if(!this.view){
32369             this.view = new Roo.grid.GridView(this.viewConfig);
32370         }
32371         return this.view;
32372     },
32373     /**
32374      * Called to get grid's drag proxy text, by default returns this.ddText.
32375      * @return {String}
32376      */
32377     getDragDropText : function(){
32378         var count = this.selModel.getCount();
32379         return String.format(this.ddText, count, count == 1 ? '' : 's');
32380     }
32381 });
32382 /**
32383  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32384  * %0 is replaced with the number of selected rows.
32385  * @type String
32386  */
32387 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32388  * Based on:
32389  * Ext JS Library 1.1.1
32390  * Copyright(c) 2006-2007, Ext JS, LLC.
32391  *
32392  * Originally Released Under LGPL - original licence link has changed is not relivant.
32393  *
32394  * Fork - LGPL
32395  * <script type="text/javascript">
32396  */
32397  
32398 Roo.grid.AbstractGridView = function(){
32399         this.grid = null;
32400         
32401         this.events = {
32402             "beforerowremoved" : true,
32403             "beforerowsinserted" : true,
32404             "beforerefresh" : true,
32405             "rowremoved" : true,
32406             "rowsinserted" : true,
32407             "rowupdated" : true,
32408             "refresh" : true
32409         };
32410     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32411 };
32412
32413 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32414     rowClass : "x-grid-row",
32415     cellClass : "x-grid-cell",
32416     tdClass : "x-grid-td",
32417     hdClass : "x-grid-hd",
32418     splitClass : "x-grid-hd-split",
32419     
32420     init: function(grid){
32421         this.grid = grid;
32422                 var cid = this.grid.getGridEl().id;
32423         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32424         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32425         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32426         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32427         },
32428         
32429     getColumnRenderers : function(){
32430         var renderers = [];
32431         var cm = this.grid.colModel;
32432         var colCount = cm.getColumnCount();
32433         for(var i = 0; i < colCount; i++){
32434             renderers[i] = cm.getRenderer(i);
32435         }
32436         return renderers;
32437     },
32438     
32439     getColumnIds : function(){
32440         var ids = [];
32441         var cm = this.grid.colModel;
32442         var colCount = cm.getColumnCount();
32443         for(var i = 0; i < colCount; i++){
32444             ids[i] = cm.getColumnId(i);
32445         }
32446         return ids;
32447     },
32448     
32449     getDataIndexes : function(){
32450         if(!this.indexMap){
32451             this.indexMap = this.buildIndexMap();
32452         }
32453         return this.indexMap.colToData;
32454     },
32455     
32456     getColumnIndexByDataIndex : function(dataIndex){
32457         if(!this.indexMap){
32458             this.indexMap = this.buildIndexMap();
32459         }
32460         return this.indexMap.dataToCol[dataIndex];
32461     },
32462     
32463     /**
32464      * Set a css style for a column dynamically. 
32465      * @param {Number} colIndex The index of the column
32466      * @param {String} name The css property name
32467      * @param {String} value The css value
32468      */
32469     setCSSStyle : function(colIndex, name, value){
32470         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32471         Roo.util.CSS.updateRule(selector, name, value);
32472     },
32473     
32474     generateRules : function(cm){
32475         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32476         Roo.util.CSS.removeStyleSheet(rulesId);
32477         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32478             var cid = cm.getColumnId(i);
32479             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32480                          this.tdSelector, cid, " {\n}\n",
32481                          this.hdSelector, cid, " {\n}\n",
32482                          this.splitSelector, cid, " {\n}\n");
32483         }
32484         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32485     }
32486 });/*
32487  * Based on:
32488  * Ext JS Library 1.1.1
32489  * Copyright(c) 2006-2007, Ext JS, LLC.
32490  *
32491  * Originally Released Under LGPL - original licence link has changed is not relivant.
32492  *
32493  * Fork - LGPL
32494  * <script type="text/javascript">
32495  */
32496
32497 // private
32498 // This is a support class used internally by the Grid components
32499 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32500     this.grid = grid;
32501     this.view = grid.getView();
32502     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32503     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32504     if(hd2){
32505         this.setHandleElId(Roo.id(hd));
32506         this.setOuterHandleElId(Roo.id(hd2));
32507     }
32508     this.scroll = false;
32509 };
32510 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32511     maxDragWidth: 120,
32512     getDragData : function(e){
32513         var t = Roo.lib.Event.getTarget(e);
32514         var h = this.view.findHeaderCell(t);
32515         if(h){
32516             return {ddel: h.firstChild, header:h};
32517         }
32518         return false;
32519     },
32520
32521     onInitDrag : function(e){
32522         this.view.headersDisabled = true;
32523         var clone = this.dragData.ddel.cloneNode(true);
32524         clone.id = Roo.id();
32525         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32526         this.proxy.update(clone);
32527         return true;
32528     },
32529
32530     afterValidDrop : function(){
32531         var v = this.view;
32532         setTimeout(function(){
32533             v.headersDisabled = false;
32534         }, 50);
32535     },
32536
32537     afterInvalidDrop : function(){
32538         var v = this.view;
32539         setTimeout(function(){
32540             v.headersDisabled = false;
32541         }, 50);
32542     }
32543 });
32544 /*
32545  * Based on:
32546  * Ext JS Library 1.1.1
32547  * Copyright(c) 2006-2007, Ext JS, LLC.
32548  *
32549  * Originally Released Under LGPL - original licence link has changed is not relivant.
32550  *
32551  * Fork - LGPL
32552  * <script type="text/javascript">
32553  */
32554 // private
32555 // This is a support class used internally by the Grid components
32556 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32557     this.grid = grid;
32558     this.view = grid.getView();
32559     // split the proxies so they don't interfere with mouse events
32560     this.proxyTop = Roo.DomHelper.append(document.body, {
32561         cls:"col-move-top", html:"&#160;"
32562     }, true);
32563     this.proxyBottom = Roo.DomHelper.append(document.body, {
32564         cls:"col-move-bottom", html:"&#160;"
32565     }, true);
32566     this.proxyTop.hide = this.proxyBottom.hide = function(){
32567         this.setLeftTop(-100,-100);
32568         this.setStyle("visibility", "hidden");
32569     };
32570     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32571     // temporarily disabled
32572     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32573     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32574 };
32575 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32576     proxyOffsets : [-4, -9],
32577     fly: Roo.Element.fly,
32578
32579     getTargetFromEvent : function(e){
32580         var t = Roo.lib.Event.getTarget(e);
32581         var cindex = this.view.findCellIndex(t);
32582         if(cindex !== false){
32583             return this.view.getHeaderCell(cindex);
32584         }
32585         return null;
32586     },
32587
32588     nextVisible : function(h){
32589         var v = this.view, cm = this.grid.colModel;
32590         h = h.nextSibling;
32591         while(h){
32592             if(!cm.isHidden(v.getCellIndex(h))){
32593                 return h;
32594             }
32595             h = h.nextSibling;
32596         }
32597         return null;
32598     },
32599
32600     prevVisible : function(h){
32601         var v = this.view, cm = this.grid.colModel;
32602         h = h.prevSibling;
32603         while(h){
32604             if(!cm.isHidden(v.getCellIndex(h))){
32605                 return h;
32606             }
32607             h = h.prevSibling;
32608         }
32609         return null;
32610     },
32611
32612     positionIndicator : function(h, n, e){
32613         var x = Roo.lib.Event.getPageX(e);
32614         var r = Roo.lib.Dom.getRegion(n.firstChild);
32615         var px, pt, py = r.top + this.proxyOffsets[1];
32616         if((r.right - x) <= (r.right-r.left)/2){
32617             px = r.right+this.view.borderWidth;
32618             pt = "after";
32619         }else{
32620             px = r.left;
32621             pt = "before";
32622         }
32623         var oldIndex = this.view.getCellIndex(h);
32624         var newIndex = this.view.getCellIndex(n);
32625
32626         if(this.grid.colModel.isFixed(newIndex)){
32627             return false;
32628         }
32629
32630         var locked = this.grid.colModel.isLocked(newIndex);
32631
32632         if(pt == "after"){
32633             newIndex++;
32634         }
32635         if(oldIndex < newIndex){
32636             newIndex--;
32637         }
32638         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32639             return false;
32640         }
32641         px +=  this.proxyOffsets[0];
32642         this.proxyTop.setLeftTop(px, py);
32643         this.proxyTop.show();
32644         if(!this.bottomOffset){
32645             this.bottomOffset = this.view.mainHd.getHeight();
32646         }
32647         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32648         this.proxyBottom.show();
32649         return pt;
32650     },
32651
32652     onNodeEnter : function(n, dd, e, data){
32653         if(data.header != n){
32654             this.positionIndicator(data.header, n, e);
32655         }
32656     },
32657
32658     onNodeOver : function(n, dd, e, data){
32659         var result = false;
32660         if(data.header != n){
32661             result = this.positionIndicator(data.header, n, e);
32662         }
32663         if(!result){
32664             this.proxyTop.hide();
32665             this.proxyBottom.hide();
32666         }
32667         return result ? this.dropAllowed : this.dropNotAllowed;
32668     },
32669
32670     onNodeOut : function(n, dd, e, data){
32671         this.proxyTop.hide();
32672         this.proxyBottom.hide();
32673     },
32674
32675     onNodeDrop : function(n, dd, e, data){
32676         var h = data.header;
32677         if(h != n){
32678             var cm = this.grid.colModel;
32679             var x = Roo.lib.Event.getPageX(e);
32680             var r = Roo.lib.Dom.getRegion(n.firstChild);
32681             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32682             var oldIndex = this.view.getCellIndex(h);
32683             var newIndex = this.view.getCellIndex(n);
32684             var locked = cm.isLocked(newIndex);
32685             if(pt == "after"){
32686                 newIndex++;
32687             }
32688             if(oldIndex < newIndex){
32689                 newIndex--;
32690             }
32691             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32692                 return false;
32693             }
32694             cm.setLocked(oldIndex, locked, true);
32695             cm.moveColumn(oldIndex, newIndex);
32696             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32697             return true;
32698         }
32699         return false;
32700     }
32701 });
32702 /*
32703  * Based on:
32704  * Ext JS Library 1.1.1
32705  * Copyright(c) 2006-2007, Ext JS, LLC.
32706  *
32707  * Originally Released Under LGPL - original licence link has changed is not relivant.
32708  *
32709  * Fork - LGPL
32710  * <script type="text/javascript">
32711  */
32712   
32713 /**
32714  * @class Roo.grid.GridView
32715  * @extends Roo.util.Observable
32716  *
32717  * @constructor
32718  * @param {Object} config
32719  */
32720 Roo.grid.GridView = function(config){
32721     Roo.grid.GridView.superclass.constructor.call(this);
32722     this.el = null;
32723
32724     Roo.apply(this, config);
32725 };
32726
32727 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32728
32729     unselectable :  'unselectable="on"',
32730     unselectableCls :  'x-unselectable',
32731     
32732     
32733     rowClass : "x-grid-row",
32734
32735     cellClass : "x-grid-col",
32736
32737     tdClass : "x-grid-td",
32738
32739     hdClass : "x-grid-hd",
32740
32741     splitClass : "x-grid-split",
32742
32743     sortClasses : ["sort-asc", "sort-desc"],
32744
32745     enableMoveAnim : false,
32746
32747     hlColor: "C3DAF9",
32748
32749     dh : Roo.DomHelper,
32750
32751     fly : Roo.Element.fly,
32752
32753     css : Roo.util.CSS,
32754
32755     borderWidth: 1,
32756
32757     splitOffset: 3,
32758
32759     scrollIncrement : 22,
32760
32761     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32762
32763     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32764
32765     bind : function(ds, cm){
32766         if(this.ds){
32767             this.ds.un("load", this.onLoad, this);
32768             this.ds.un("datachanged", this.onDataChange, this);
32769             this.ds.un("add", this.onAdd, this);
32770             this.ds.un("remove", this.onRemove, this);
32771             this.ds.un("update", this.onUpdate, this);
32772             this.ds.un("clear", this.onClear, this);
32773         }
32774         if(ds){
32775             ds.on("load", this.onLoad, this);
32776             ds.on("datachanged", this.onDataChange, this);
32777             ds.on("add", this.onAdd, this);
32778             ds.on("remove", this.onRemove, this);
32779             ds.on("update", this.onUpdate, this);
32780             ds.on("clear", this.onClear, this);
32781         }
32782         this.ds = ds;
32783
32784         if(this.cm){
32785             this.cm.un("widthchange", this.onColWidthChange, this);
32786             this.cm.un("headerchange", this.onHeaderChange, this);
32787             this.cm.un("hiddenchange", this.onHiddenChange, this);
32788             this.cm.un("columnmoved", this.onColumnMove, this);
32789             this.cm.un("columnlockchange", this.onColumnLock, this);
32790         }
32791         if(cm){
32792             this.generateRules(cm);
32793             cm.on("widthchange", this.onColWidthChange, this);
32794             cm.on("headerchange", this.onHeaderChange, this);
32795             cm.on("hiddenchange", this.onHiddenChange, this);
32796             cm.on("columnmoved", this.onColumnMove, this);
32797             cm.on("columnlockchange", this.onColumnLock, this);
32798         }
32799         this.cm = cm;
32800     },
32801
32802     init: function(grid){
32803         Roo.grid.GridView.superclass.init.call(this, grid);
32804
32805         this.bind(grid.dataSource, grid.colModel);
32806
32807         grid.on("headerclick", this.handleHeaderClick, this);
32808
32809         if(grid.trackMouseOver){
32810             grid.on("mouseover", this.onRowOver, this);
32811             grid.on("mouseout", this.onRowOut, this);
32812         }
32813         grid.cancelTextSelection = function(){};
32814         this.gridId = grid.id;
32815
32816         var tpls = this.templates || {};
32817
32818         if(!tpls.master){
32819             tpls.master = new Roo.Template(
32820                '<div class="x-grid" hidefocus="true">',
32821                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32822                   '<div class="x-grid-topbar"></div>',
32823                   '<div class="x-grid-scroller"><div></div></div>',
32824                   '<div class="x-grid-locked">',
32825                       '<div class="x-grid-header">{lockedHeader}</div>',
32826                       '<div class="x-grid-body">{lockedBody}</div>',
32827                   "</div>",
32828                   '<div class="x-grid-viewport">',
32829                       '<div class="x-grid-header">{header}</div>',
32830                       '<div class="x-grid-body">{body}</div>',
32831                   "</div>",
32832                   '<div class="x-grid-bottombar"></div>',
32833                  
32834                   '<div class="x-grid-resize-proxy">&#160;</div>',
32835                "</div>"
32836             );
32837             tpls.master.disableformats = true;
32838         }
32839
32840         if(!tpls.header){
32841             tpls.header = new Roo.Template(
32842                '<table border="0" cellspacing="0" cellpadding="0">',
32843                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32844                "</table>{splits}"
32845             );
32846             tpls.header.disableformats = true;
32847         }
32848         tpls.header.compile();
32849
32850         if(!tpls.hcell){
32851             tpls.hcell = new Roo.Template(
32852                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32853                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32854                 "</div></td>"
32855              );
32856              tpls.hcell.disableFormats = true;
32857         }
32858         tpls.hcell.compile();
32859
32860         if(!tpls.hsplit){
32861             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32862                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32863             tpls.hsplit.disableFormats = true;
32864         }
32865         tpls.hsplit.compile();
32866
32867         if(!tpls.body){
32868             tpls.body = new Roo.Template(
32869                '<table border="0" cellspacing="0" cellpadding="0">',
32870                "<tbody>{rows}</tbody>",
32871                "</table>"
32872             );
32873             tpls.body.disableFormats = true;
32874         }
32875         tpls.body.compile();
32876
32877         if(!tpls.row){
32878             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32879             tpls.row.disableFormats = true;
32880         }
32881         tpls.row.compile();
32882
32883         if(!tpls.cell){
32884             tpls.cell = new Roo.Template(
32885                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32886                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32887                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32888                 "</td>"
32889             );
32890             tpls.cell.disableFormats = true;
32891         }
32892         tpls.cell.compile();
32893
32894         this.templates = tpls;
32895     },
32896
32897     // remap these for backwards compat
32898     onColWidthChange : function(){
32899         this.updateColumns.apply(this, arguments);
32900     },
32901     onHeaderChange : function(){
32902         this.updateHeaders.apply(this, arguments);
32903     }, 
32904     onHiddenChange : function(){
32905         this.handleHiddenChange.apply(this, arguments);
32906     },
32907     onColumnMove : function(){
32908         this.handleColumnMove.apply(this, arguments);
32909     },
32910     onColumnLock : function(){
32911         this.handleLockChange.apply(this, arguments);
32912     },
32913
32914     onDataChange : function(){
32915         this.refresh();
32916         this.updateHeaderSortState();
32917     },
32918
32919     onClear : function(){
32920         this.refresh();
32921     },
32922
32923     onUpdate : function(ds, record){
32924         this.refreshRow(record);
32925     },
32926
32927     refreshRow : function(record){
32928         var ds = this.ds, index;
32929         if(typeof record == 'number'){
32930             index = record;
32931             record = ds.getAt(index);
32932         }else{
32933             index = ds.indexOf(record);
32934         }
32935         this.insertRows(ds, index, index, true);
32936         this.onRemove(ds, record, index+1, true);
32937         this.syncRowHeights(index, index);
32938         this.layout();
32939         this.fireEvent("rowupdated", this, index, record);
32940     },
32941
32942     onAdd : function(ds, records, index){
32943         this.insertRows(ds, index, index + (records.length-1));
32944     },
32945
32946     onRemove : function(ds, record, index, isUpdate){
32947         if(isUpdate !== true){
32948             this.fireEvent("beforerowremoved", this, index, record);
32949         }
32950         var bt = this.getBodyTable(), lt = this.getLockedTable();
32951         if(bt.rows[index]){
32952             bt.firstChild.removeChild(bt.rows[index]);
32953         }
32954         if(lt.rows[index]){
32955             lt.firstChild.removeChild(lt.rows[index]);
32956         }
32957         if(isUpdate !== true){
32958             this.stripeRows(index);
32959             this.syncRowHeights(index, index);
32960             this.layout();
32961             this.fireEvent("rowremoved", this, index, record);
32962         }
32963     },
32964
32965     onLoad : function(){
32966         this.scrollToTop();
32967     },
32968
32969     /**
32970      * Scrolls the grid to the top
32971      */
32972     scrollToTop : function(){
32973         if(this.scroller){
32974             this.scroller.dom.scrollTop = 0;
32975             this.syncScroll();
32976         }
32977     },
32978
32979     /**
32980      * Gets a panel in the header of the grid that can be used for toolbars etc.
32981      * After modifying the contents of this panel a call to grid.autoSize() may be
32982      * required to register any changes in size.
32983      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32984      * @return Roo.Element
32985      */
32986     getHeaderPanel : function(doShow){
32987         if(doShow){
32988             this.headerPanel.show();
32989         }
32990         return this.headerPanel;
32991     },
32992
32993     /**
32994      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32995      * After modifying the contents of this panel a call to grid.autoSize() may be
32996      * required to register any changes in size.
32997      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32998      * @return Roo.Element
32999      */
33000     getFooterPanel : function(doShow){
33001         if(doShow){
33002             this.footerPanel.show();
33003         }
33004         return this.footerPanel;
33005     },
33006
33007     initElements : function(){
33008         var E = Roo.Element;
33009         var el = this.grid.getGridEl().dom.firstChild;
33010         var cs = el.childNodes;
33011
33012         this.el = new E(el);
33013         
33014          this.focusEl = new E(el.firstChild);
33015         this.focusEl.swallowEvent("click", true);
33016         
33017         this.headerPanel = new E(cs[1]);
33018         this.headerPanel.enableDisplayMode("block");
33019
33020         this.scroller = new E(cs[2]);
33021         this.scrollSizer = new E(this.scroller.dom.firstChild);
33022
33023         this.lockedWrap = new E(cs[3]);
33024         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33025         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33026
33027         this.mainWrap = new E(cs[4]);
33028         this.mainHd = new E(this.mainWrap.dom.firstChild);
33029         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33030
33031         this.footerPanel = new E(cs[5]);
33032         this.footerPanel.enableDisplayMode("block");
33033
33034         this.resizeProxy = new E(cs[6]);
33035
33036         this.headerSelector = String.format(
33037            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33038            this.lockedHd.id, this.mainHd.id
33039         );
33040
33041         this.splitterSelector = String.format(
33042            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33043            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33044         );
33045     },
33046     idToCssName : function(s)
33047     {
33048         return s.replace(/[^a-z0-9]+/ig, '-');
33049     },
33050
33051     getHeaderCell : function(index){
33052         return Roo.DomQuery.select(this.headerSelector)[index];
33053     },
33054
33055     getHeaderCellMeasure : function(index){
33056         return this.getHeaderCell(index).firstChild;
33057     },
33058
33059     getHeaderCellText : function(index){
33060         return this.getHeaderCell(index).firstChild.firstChild;
33061     },
33062
33063     getLockedTable : function(){
33064         return this.lockedBody.dom.firstChild;
33065     },
33066
33067     getBodyTable : function(){
33068         return this.mainBody.dom.firstChild;
33069     },
33070
33071     getLockedRow : function(index){
33072         return this.getLockedTable().rows[index];
33073     },
33074
33075     getRow : function(index){
33076         return this.getBodyTable().rows[index];
33077     },
33078
33079     getRowComposite : function(index){
33080         if(!this.rowEl){
33081             this.rowEl = new Roo.CompositeElementLite();
33082         }
33083         var els = [], lrow, mrow;
33084         if(lrow = this.getLockedRow(index)){
33085             els.push(lrow);
33086         }
33087         if(mrow = this.getRow(index)){
33088             els.push(mrow);
33089         }
33090         this.rowEl.elements = els;
33091         return this.rowEl;
33092     },
33093     /**
33094      * Gets the 'td' of the cell
33095      * 
33096      * @param {Integer} rowIndex row to select
33097      * @param {Integer} colIndex column to select
33098      * 
33099      * @return {Object} 
33100      */
33101     getCell : function(rowIndex, colIndex){
33102         var locked = this.cm.getLockedCount();
33103         var source;
33104         if(colIndex < locked){
33105             source = this.lockedBody.dom.firstChild;
33106         }else{
33107             source = this.mainBody.dom.firstChild;
33108             colIndex -= locked;
33109         }
33110         return source.rows[rowIndex].childNodes[colIndex];
33111     },
33112
33113     getCellText : function(rowIndex, colIndex){
33114         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33115     },
33116
33117     getCellBox : function(cell){
33118         var b = this.fly(cell).getBox();
33119         if(Roo.isOpera){ // opera fails to report the Y
33120             b.y = cell.offsetTop + this.mainBody.getY();
33121         }
33122         return b;
33123     },
33124
33125     getCellIndex : function(cell){
33126         var id = String(cell.className).match(this.cellRE);
33127         if(id){
33128             return parseInt(id[1], 10);
33129         }
33130         return 0;
33131     },
33132
33133     findHeaderIndex : function(n){
33134         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33135         return r ? this.getCellIndex(r) : false;
33136     },
33137
33138     findHeaderCell : function(n){
33139         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33140         return r ? r : false;
33141     },
33142
33143     findRowIndex : function(n){
33144         if(!n){
33145             return false;
33146         }
33147         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33148         return r ? r.rowIndex : false;
33149     },
33150
33151     findCellIndex : function(node){
33152         var stop = this.el.dom;
33153         while(node && node != stop){
33154             if(this.findRE.test(node.className)){
33155                 return this.getCellIndex(node);
33156             }
33157             node = node.parentNode;
33158         }
33159         return false;
33160     },
33161
33162     getColumnId : function(index){
33163         return this.cm.getColumnId(index);
33164     },
33165
33166     getSplitters : function()
33167     {
33168         if(this.splitterSelector){
33169            return Roo.DomQuery.select(this.splitterSelector);
33170         }else{
33171             return null;
33172       }
33173     },
33174
33175     getSplitter : function(index){
33176         return this.getSplitters()[index];
33177     },
33178
33179     onRowOver : function(e, t){
33180         var row;
33181         if((row = this.findRowIndex(t)) !== false){
33182             this.getRowComposite(row).addClass("x-grid-row-over");
33183         }
33184     },
33185
33186     onRowOut : function(e, t){
33187         var row;
33188         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33189             this.getRowComposite(row).removeClass("x-grid-row-over");
33190         }
33191     },
33192
33193     renderHeaders : function(){
33194         var cm = this.cm;
33195         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33196         var cb = [], lb = [], sb = [], lsb = [], p = {};
33197         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33198             p.cellId = "x-grid-hd-0-" + i;
33199             p.splitId = "x-grid-csplit-0-" + i;
33200             p.id = cm.getColumnId(i);
33201             p.value = cm.getColumnHeader(i) || "";
33202             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33203             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33204             if(!cm.isLocked(i)){
33205                 cb[cb.length] = ct.apply(p);
33206                 sb[sb.length] = st.apply(p);
33207             }else{
33208                 lb[lb.length] = ct.apply(p);
33209                 lsb[lsb.length] = st.apply(p);
33210             }
33211         }
33212         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33213                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33214     },
33215
33216     updateHeaders : function(){
33217         var html = this.renderHeaders();
33218         this.lockedHd.update(html[0]);
33219         this.mainHd.update(html[1]);
33220     },
33221
33222     /**
33223      * Focuses the specified row.
33224      * @param {Number} row The row index
33225      */
33226     focusRow : function(row)
33227     {
33228         //Roo.log('GridView.focusRow');
33229         var x = this.scroller.dom.scrollLeft;
33230         this.focusCell(row, 0, false);
33231         this.scroller.dom.scrollLeft = x;
33232     },
33233
33234     /**
33235      * Focuses the specified cell.
33236      * @param {Number} row The row index
33237      * @param {Number} col The column index
33238      * @param {Boolean} hscroll false to disable horizontal scrolling
33239      */
33240     focusCell : function(row, col, hscroll)
33241     {
33242         //Roo.log('GridView.focusCell');
33243         var el = this.ensureVisible(row, col, hscroll);
33244         this.focusEl.alignTo(el, "tl-tl");
33245         if(Roo.isGecko){
33246             this.focusEl.focus();
33247         }else{
33248             this.focusEl.focus.defer(1, this.focusEl);
33249         }
33250     },
33251
33252     /**
33253      * Scrolls the specified cell into view
33254      * @param {Number} row The row index
33255      * @param {Number} col The column index
33256      * @param {Boolean} hscroll false to disable horizontal scrolling
33257      */
33258     ensureVisible : function(row, col, hscroll)
33259     {
33260         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33261         //return null; //disable for testing.
33262         if(typeof row != "number"){
33263             row = row.rowIndex;
33264         }
33265         if(row < 0 && row >= this.ds.getCount()){
33266             return  null;
33267         }
33268         col = (col !== undefined ? col : 0);
33269         var cm = this.grid.colModel;
33270         while(cm.isHidden(col)){
33271             col++;
33272         }
33273
33274         var el = this.getCell(row, col);
33275         if(!el){
33276             return null;
33277         }
33278         var c = this.scroller.dom;
33279
33280         var ctop = parseInt(el.offsetTop, 10);
33281         var cleft = parseInt(el.offsetLeft, 10);
33282         var cbot = ctop + el.offsetHeight;
33283         var cright = cleft + el.offsetWidth;
33284         
33285         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33286         var stop = parseInt(c.scrollTop, 10);
33287         var sleft = parseInt(c.scrollLeft, 10);
33288         var sbot = stop + ch;
33289         var sright = sleft + c.clientWidth;
33290         /*
33291         Roo.log('GridView.ensureVisible:' +
33292                 ' ctop:' + ctop +
33293                 ' c.clientHeight:' + c.clientHeight +
33294                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33295                 ' stop:' + stop +
33296                 ' cbot:' + cbot +
33297                 ' sbot:' + sbot +
33298                 ' ch:' + ch  
33299                 );
33300         */
33301         if(ctop < stop){
33302              c.scrollTop = ctop;
33303             //Roo.log("set scrolltop to ctop DISABLE?");
33304         }else if(cbot > sbot){
33305             //Roo.log("set scrolltop to cbot-ch");
33306             c.scrollTop = cbot-ch;
33307         }
33308         
33309         if(hscroll !== false){
33310             if(cleft < sleft){
33311                 c.scrollLeft = cleft;
33312             }else if(cright > sright){
33313                 c.scrollLeft = cright-c.clientWidth;
33314             }
33315         }
33316          
33317         return el;
33318     },
33319
33320     updateColumns : function(){
33321         this.grid.stopEditing();
33322         var cm = this.grid.colModel, colIds = this.getColumnIds();
33323         //var totalWidth = cm.getTotalWidth();
33324         var pos = 0;
33325         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33326             //if(cm.isHidden(i)) continue;
33327             var w = cm.getColumnWidth(i);
33328             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33329             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33330         }
33331         this.updateSplitters();
33332     },
33333
33334     generateRules : function(cm){
33335         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33336         Roo.util.CSS.removeStyleSheet(rulesId);
33337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33338             var cid = cm.getColumnId(i);
33339             var align = '';
33340             if(cm.config[i].align){
33341                 align = 'text-align:'+cm.config[i].align+';';
33342             }
33343             var hidden = '';
33344             if(cm.isHidden(i)){
33345                 hidden = 'display:none;';
33346             }
33347             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33348             ruleBuf.push(
33349                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33350                     this.hdSelector, cid, " {\n", align, width, "}\n",
33351                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33352                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33353         }
33354         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33355     },
33356
33357     updateSplitters : function(){
33358         var cm = this.cm, s = this.getSplitters();
33359         if(s){ // splitters not created yet
33360             var pos = 0, locked = true;
33361             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33362                 if(cm.isHidden(i)) {
33363                     continue;
33364                 }
33365                 var w = cm.getColumnWidth(i); // make sure it's a number
33366                 if(!cm.isLocked(i) && locked){
33367                     pos = 0;
33368                     locked = false;
33369                 }
33370                 pos += w;
33371                 s[i].style.left = (pos-this.splitOffset) + "px";
33372             }
33373         }
33374     },
33375
33376     handleHiddenChange : function(colModel, colIndex, hidden){
33377         if(hidden){
33378             this.hideColumn(colIndex);
33379         }else{
33380             this.unhideColumn(colIndex);
33381         }
33382     },
33383
33384     hideColumn : function(colIndex){
33385         var cid = this.getColumnId(colIndex);
33386         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33387         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33388         if(Roo.isSafari){
33389             this.updateHeaders();
33390         }
33391         this.updateSplitters();
33392         this.layout();
33393     },
33394
33395     unhideColumn : function(colIndex){
33396         var cid = this.getColumnId(colIndex);
33397         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33398         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33399
33400         if(Roo.isSafari){
33401             this.updateHeaders();
33402         }
33403         this.updateSplitters();
33404         this.layout();
33405     },
33406
33407     insertRows : function(dm, firstRow, lastRow, isUpdate){
33408         if(firstRow == 0 && lastRow == dm.getCount()-1){
33409             this.refresh();
33410         }else{
33411             if(!isUpdate){
33412                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33413             }
33414             var s = this.getScrollState();
33415             var markup = this.renderRows(firstRow, lastRow);
33416             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33417             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33418             this.restoreScroll(s);
33419             if(!isUpdate){
33420                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33421                 this.syncRowHeights(firstRow, lastRow);
33422                 this.stripeRows(firstRow);
33423                 this.layout();
33424             }
33425         }
33426     },
33427
33428     bufferRows : function(markup, target, index){
33429         var before = null, trows = target.rows, tbody = target.tBodies[0];
33430         if(index < trows.length){
33431             before = trows[index];
33432         }
33433         var b = document.createElement("div");
33434         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33435         var rows = b.firstChild.rows;
33436         for(var i = 0, len = rows.length; i < len; i++){
33437             if(before){
33438                 tbody.insertBefore(rows[0], before);
33439             }else{
33440                 tbody.appendChild(rows[0]);
33441             }
33442         }
33443         b.innerHTML = "";
33444         b = null;
33445     },
33446
33447     deleteRows : function(dm, firstRow, lastRow){
33448         if(dm.getRowCount()<1){
33449             this.fireEvent("beforerefresh", this);
33450             this.mainBody.update("");
33451             this.lockedBody.update("");
33452             this.fireEvent("refresh", this);
33453         }else{
33454             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33455             var bt = this.getBodyTable();
33456             var tbody = bt.firstChild;
33457             var rows = bt.rows;
33458             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33459                 tbody.removeChild(rows[firstRow]);
33460             }
33461             this.stripeRows(firstRow);
33462             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33463         }
33464     },
33465
33466     updateRows : function(dataSource, firstRow, lastRow){
33467         var s = this.getScrollState();
33468         this.refresh();
33469         this.restoreScroll(s);
33470     },
33471
33472     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33473         if(!noRefresh){
33474            this.refresh();
33475         }
33476         this.updateHeaderSortState();
33477     },
33478
33479     getScrollState : function(){
33480         
33481         var sb = this.scroller.dom;
33482         return {left: sb.scrollLeft, top: sb.scrollTop};
33483     },
33484
33485     stripeRows : function(startRow){
33486         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33487             return;
33488         }
33489         startRow = startRow || 0;
33490         var rows = this.getBodyTable().rows;
33491         var lrows = this.getLockedTable().rows;
33492         var cls = ' x-grid-row-alt ';
33493         for(var i = startRow, len = rows.length; i < len; i++){
33494             var row = rows[i], lrow = lrows[i];
33495             var isAlt = ((i+1) % 2 == 0);
33496             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33497             if(isAlt == hasAlt){
33498                 continue;
33499             }
33500             if(isAlt){
33501                 row.className += " x-grid-row-alt";
33502             }else{
33503                 row.className = row.className.replace("x-grid-row-alt", "");
33504             }
33505             if(lrow){
33506                 lrow.className = row.className;
33507             }
33508         }
33509     },
33510
33511     restoreScroll : function(state){
33512         //Roo.log('GridView.restoreScroll');
33513         var sb = this.scroller.dom;
33514         sb.scrollLeft = state.left;
33515         sb.scrollTop = state.top;
33516         this.syncScroll();
33517     },
33518
33519     syncScroll : function(){
33520         //Roo.log('GridView.syncScroll');
33521         var sb = this.scroller.dom;
33522         var sh = this.mainHd.dom;
33523         var bs = this.mainBody.dom;
33524         var lv = this.lockedBody.dom;
33525         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33526         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33527     },
33528
33529     handleScroll : function(e){
33530         this.syncScroll();
33531         var sb = this.scroller.dom;
33532         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33533         e.stopEvent();
33534     },
33535
33536     handleWheel : function(e){
33537         var d = e.getWheelDelta();
33538         this.scroller.dom.scrollTop -= d*22;
33539         // set this here to prevent jumpy scrolling on large tables
33540         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33541         e.stopEvent();
33542     },
33543
33544     renderRows : function(startRow, endRow){
33545         // pull in all the crap needed to render rows
33546         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33547         var colCount = cm.getColumnCount();
33548
33549         if(ds.getCount() < 1){
33550             return ["", ""];
33551         }
33552
33553         // build a map for all the columns
33554         var cs = [];
33555         for(var i = 0; i < colCount; i++){
33556             var name = cm.getDataIndex(i);
33557             cs[i] = {
33558                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33559                 renderer : cm.getRenderer(i),
33560                 id : cm.getColumnId(i),
33561                 locked : cm.isLocked(i),
33562                 has_editor : cm.isCellEditable(i)
33563             };
33564         }
33565
33566         startRow = startRow || 0;
33567         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33568
33569         // records to render
33570         var rs = ds.getRange(startRow, endRow);
33571
33572         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33573     },
33574
33575     // As much as I hate to duplicate code, this was branched because FireFox really hates
33576     // [].join("") on strings. The performance difference was substantial enough to
33577     // branch this function
33578     doRender : Roo.isGecko ?
33579             function(cs, rs, ds, startRow, colCount, stripe){
33580                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33581                 // buffers
33582                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33583                 
33584                 var hasListener = this.grid.hasListener('rowclass');
33585                 var rowcfg = {};
33586                 for(var j = 0, len = rs.length; j < len; j++){
33587                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33588                     for(var i = 0; i < colCount; i++){
33589                         c = cs[i];
33590                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33591                         p.id = c.id;
33592                         p.css = p.attr = "";
33593                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33594                         if(p.value == undefined || p.value === "") {
33595                             p.value = "&#160;";
33596                         }
33597                         if(c.has_editor){
33598                             p.css += ' x-grid-editable-cell';
33599                         }
33600                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33601                             p.css +=  ' x-grid-dirty-cell';
33602                         }
33603                         var markup = ct.apply(p);
33604                         if(!c.locked){
33605                             cb+= markup;
33606                         }else{
33607                             lcb+= markup;
33608                         }
33609                     }
33610                     var alt = [];
33611                     if(stripe && ((rowIndex+1) % 2 == 0)){
33612                         alt.push("x-grid-row-alt")
33613                     }
33614                     if(r.dirty){
33615                         alt.push(  " x-grid-dirty-row");
33616                     }
33617                     rp.cells = lcb;
33618                     if(this.getRowClass){
33619                         alt.push(this.getRowClass(r, rowIndex));
33620                     }
33621                     if (hasListener) {
33622                         rowcfg = {
33623                              
33624                             record: r,
33625                             rowIndex : rowIndex,
33626                             rowClass : ''
33627                         };
33628                         this.grid.fireEvent('rowclass', this, rowcfg);
33629                         alt.push(rowcfg.rowClass);
33630                     }
33631                     rp.alt = alt.join(" ");
33632                     lbuf+= rt.apply(rp);
33633                     rp.cells = cb;
33634                     buf+=  rt.apply(rp);
33635                 }
33636                 return [lbuf, buf];
33637             } :
33638             function(cs, rs, ds, startRow, colCount, stripe){
33639                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33640                 // buffers
33641                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33642                 var hasListener = this.grid.hasListener('rowclass');
33643  
33644                 var rowcfg = {};
33645                 for(var j = 0, len = rs.length; j < len; j++){
33646                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33647                     for(var i = 0; i < colCount; i++){
33648                         c = cs[i];
33649                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33650                         p.id = c.id;
33651                         p.css = p.attr = "";
33652                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33653                         if(p.value == undefined || p.value === "") {
33654                             p.value = "&#160;";
33655                         }
33656                         //Roo.log(c);
33657                          if(c.has_editor){
33658                             p.css += ' x-grid-editable-cell';
33659                         }
33660                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33661                             p.css += ' x-grid-dirty-cell' 
33662                         }
33663                         
33664                         var markup = ct.apply(p);
33665                         if(!c.locked){
33666                             cb[cb.length] = markup;
33667                         }else{
33668                             lcb[lcb.length] = markup;
33669                         }
33670                     }
33671                     var alt = [];
33672                     if(stripe && ((rowIndex+1) % 2 == 0)){
33673                         alt.push( "x-grid-row-alt");
33674                     }
33675                     if(r.dirty){
33676                         alt.push(" x-grid-dirty-row");
33677                     }
33678                     rp.cells = lcb;
33679                     if(this.getRowClass){
33680                         alt.push( this.getRowClass(r, rowIndex));
33681                     }
33682                     if (hasListener) {
33683                         rowcfg = {
33684                              
33685                             record: r,
33686                             rowIndex : rowIndex,
33687                             rowClass : ''
33688                         };
33689                         this.grid.fireEvent('rowclass', this, rowcfg);
33690                         alt.push(rowcfg.rowClass);
33691                     }
33692                     
33693                     rp.alt = alt.join(" ");
33694                     rp.cells = lcb.join("");
33695                     lbuf[lbuf.length] = rt.apply(rp);
33696                     rp.cells = cb.join("");
33697                     buf[buf.length] =  rt.apply(rp);
33698                 }
33699                 return [lbuf.join(""), buf.join("")];
33700             },
33701
33702     renderBody : function(){
33703         var markup = this.renderRows();
33704         var bt = this.templates.body;
33705         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33706     },
33707
33708     /**
33709      * Refreshes the grid
33710      * @param {Boolean} headersToo
33711      */
33712     refresh : function(headersToo){
33713         this.fireEvent("beforerefresh", this);
33714         this.grid.stopEditing();
33715         var result = this.renderBody();
33716         this.lockedBody.update(result[0]);
33717         this.mainBody.update(result[1]);
33718         if(headersToo === true){
33719             this.updateHeaders();
33720             this.updateColumns();
33721             this.updateSplitters();
33722             this.updateHeaderSortState();
33723         }
33724         this.syncRowHeights();
33725         this.layout();
33726         this.fireEvent("refresh", this);
33727     },
33728
33729     handleColumnMove : function(cm, oldIndex, newIndex){
33730         this.indexMap = null;
33731         var s = this.getScrollState();
33732         this.refresh(true);
33733         this.restoreScroll(s);
33734         this.afterMove(newIndex);
33735     },
33736
33737     afterMove : function(colIndex){
33738         if(this.enableMoveAnim && Roo.enableFx){
33739             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33740         }
33741         // if multisort - fix sortOrder, and reload..
33742         if (this.grid.dataSource.multiSort) {
33743             // the we can call sort again..
33744             var dm = this.grid.dataSource;
33745             var cm = this.grid.colModel;
33746             var so = [];
33747             for(var i = 0; i < cm.config.length; i++ ) {
33748                 
33749                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33750                     continue; // dont' bother, it's not in sort list or being set.
33751                 }
33752                 
33753                 so.push(cm.config[i].dataIndex);
33754             };
33755             dm.sortOrder = so;
33756             dm.load(dm.lastOptions);
33757             
33758             
33759         }
33760         
33761     },
33762
33763     updateCell : function(dm, rowIndex, dataIndex){
33764         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33765         if(typeof colIndex == "undefined"){ // not present in grid
33766             return;
33767         }
33768         var cm = this.grid.colModel;
33769         var cell = this.getCell(rowIndex, colIndex);
33770         var cellText = this.getCellText(rowIndex, colIndex);
33771
33772         var p = {
33773             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33774             id : cm.getColumnId(colIndex),
33775             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33776         };
33777         var renderer = cm.getRenderer(colIndex);
33778         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33779         if(typeof val == "undefined" || val === "") {
33780             val = "&#160;";
33781         }
33782         cellText.innerHTML = val;
33783         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33784         this.syncRowHeights(rowIndex, rowIndex);
33785     },
33786
33787     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33788         var maxWidth = 0;
33789         if(this.grid.autoSizeHeaders){
33790             var h = this.getHeaderCellMeasure(colIndex);
33791             maxWidth = Math.max(maxWidth, h.scrollWidth);
33792         }
33793         var tb, index;
33794         if(this.cm.isLocked(colIndex)){
33795             tb = this.getLockedTable();
33796             index = colIndex;
33797         }else{
33798             tb = this.getBodyTable();
33799             index = colIndex - this.cm.getLockedCount();
33800         }
33801         if(tb && tb.rows){
33802             var rows = tb.rows;
33803             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33804             for(var i = 0; i < stopIndex; i++){
33805                 var cell = rows[i].childNodes[index].firstChild;
33806                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33807             }
33808         }
33809         return maxWidth + /*margin for error in IE*/ 5;
33810     },
33811     /**
33812      * Autofit a column to its content.
33813      * @param {Number} colIndex
33814      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33815      */
33816      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33817          if(this.cm.isHidden(colIndex)){
33818              return; // can't calc a hidden column
33819          }
33820         if(forceMinSize){
33821             var cid = this.cm.getColumnId(colIndex);
33822             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33823            if(this.grid.autoSizeHeaders){
33824                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33825            }
33826         }
33827         var newWidth = this.calcColumnWidth(colIndex);
33828         this.cm.setColumnWidth(colIndex,
33829             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33830         if(!suppressEvent){
33831             this.grid.fireEvent("columnresize", colIndex, newWidth);
33832         }
33833     },
33834
33835     /**
33836      * Autofits all columns to their content and then expands to fit any extra space in the grid
33837      */
33838      autoSizeColumns : function(){
33839         var cm = this.grid.colModel;
33840         var colCount = cm.getColumnCount();
33841         for(var i = 0; i < colCount; i++){
33842             this.autoSizeColumn(i, true, true);
33843         }
33844         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33845             this.fitColumns();
33846         }else{
33847             this.updateColumns();
33848             this.layout();
33849         }
33850     },
33851
33852     /**
33853      * Autofits all columns to the grid's width proportionate with their current size
33854      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33855      */
33856     fitColumns : function(reserveScrollSpace){
33857         var cm = this.grid.colModel;
33858         var colCount = cm.getColumnCount();
33859         var cols = [];
33860         var width = 0;
33861         var i, w;
33862         for (i = 0; i < colCount; i++){
33863             if(!cm.isHidden(i) && !cm.isFixed(i)){
33864                 w = cm.getColumnWidth(i);
33865                 cols.push(i);
33866                 cols.push(w);
33867                 width += w;
33868             }
33869         }
33870         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33871         if(reserveScrollSpace){
33872             avail -= 17;
33873         }
33874         var frac = (avail - cm.getTotalWidth())/width;
33875         while (cols.length){
33876             w = cols.pop();
33877             i = cols.pop();
33878             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33879         }
33880         this.updateColumns();
33881         this.layout();
33882     },
33883
33884     onRowSelect : function(rowIndex){
33885         var row = this.getRowComposite(rowIndex);
33886         row.addClass("x-grid-row-selected");
33887     },
33888
33889     onRowDeselect : function(rowIndex){
33890         var row = this.getRowComposite(rowIndex);
33891         row.removeClass("x-grid-row-selected");
33892     },
33893
33894     onCellSelect : function(row, col){
33895         var cell = this.getCell(row, col);
33896         if(cell){
33897             Roo.fly(cell).addClass("x-grid-cell-selected");
33898         }
33899     },
33900
33901     onCellDeselect : function(row, col){
33902         var cell = this.getCell(row, col);
33903         if(cell){
33904             Roo.fly(cell).removeClass("x-grid-cell-selected");
33905         }
33906     },
33907
33908     updateHeaderSortState : function(){
33909         
33910         // sort state can be single { field: xxx, direction : yyy}
33911         // or   { xxx=>ASC , yyy : DESC ..... }
33912         
33913         var mstate = {};
33914         if (!this.ds.multiSort) { 
33915             var state = this.ds.getSortState();
33916             if(!state){
33917                 return;
33918             }
33919             mstate[state.field] = state.direction;
33920             // FIXME... - this is not used here.. but might be elsewhere..
33921             this.sortState = state;
33922             
33923         } else {
33924             mstate = this.ds.sortToggle;
33925         }
33926         //remove existing sort classes..
33927         
33928         var sc = this.sortClasses;
33929         var hds = this.el.select(this.headerSelector).removeClass(sc);
33930         
33931         for(var f in mstate) {
33932         
33933             var sortColumn = this.cm.findColumnIndex(f);
33934             
33935             if(sortColumn != -1){
33936                 var sortDir = mstate[f];        
33937                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33938             }
33939         }
33940         
33941          
33942         
33943     },
33944
33945
33946     handleHeaderClick : function(g, index,e){
33947         
33948         Roo.log("header click");
33949         
33950         if (Roo.isTouch) {
33951             // touch events on header are handled by context
33952             this.handleHdCtx(g,index,e);
33953             return;
33954         }
33955         
33956         
33957         if(this.headersDisabled){
33958             return;
33959         }
33960         var dm = g.dataSource, cm = g.colModel;
33961         if(!cm.isSortable(index)){
33962             return;
33963         }
33964         g.stopEditing();
33965         
33966         if (dm.multiSort) {
33967             // update the sortOrder
33968             var so = [];
33969             for(var i = 0; i < cm.config.length; i++ ) {
33970                 
33971                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33972                     continue; // dont' bother, it's not in sort list or being set.
33973                 }
33974                 
33975                 so.push(cm.config[i].dataIndex);
33976             };
33977             dm.sortOrder = so;
33978         }
33979         
33980         
33981         dm.sort(cm.getDataIndex(index));
33982     },
33983
33984
33985     destroy : function(){
33986         if(this.colMenu){
33987             this.colMenu.removeAll();
33988             Roo.menu.MenuMgr.unregister(this.colMenu);
33989             this.colMenu.getEl().remove();
33990             delete this.colMenu;
33991         }
33992         if(this.hmenu){
33993             this.hmenu.removeAll();
33994             Roo.menu.MenuMgr.unregister(this.hmenu);
33995             this.hmenu.getEl().remove();
33996             delete this.hmenu;
33997         }
33998         if(this.grid.enableColumnMove){
33999             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34000             if(dds){
34001                 for(var dd in dds){
34002                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34003                         var elid = dds[dd].dragElId;
34004                         dds[dd].unreg();
34005                         Roo.get(elid).remove();
34006                     } else if(dds[dd].config.isTarget){
34007                         dds[dd].proxyTop.remove();
34008                         dds[dd].proxyBottom.remove();
34009                         dds[dd].unreg();
34010                     }
34011                     if(Roo.dd.DDM.locationCache[dd]){
34012                         delete Roo.dd.DDM.locationCache[dd];
34013                     }
34014                 }
34015                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34016             }
34017         }
34018         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34019         this.bind(null, null);
34020         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34021     },
34022
34023     handleLockChange : function(){
34024         this.refresh(true);
34025     },
34026
34027     onDenyColumnLock : function(){
34028
34029     },
34030
34031     onDenyColumnHide : function(){
34032
34033     },
34034
34035     handleHdMenuClick : function(item){
34036         var index = this.hdCtxIndex;
34037         var cm = this.cm, ds = this.ds;
34038         switch(item.id){
34039             case "asc":
34040                 ds.sort(cm.getDataIndex(index), "ASC");
34041                 break;
34042             case "desc":
34043                 ds.sort(cm.getDataIndex(index), "DESC");
34044                 break;
34045             case "lock":
34046                 var lc = cm.getLockedCount();
34047                 if(cm.getColumnCount(true) <= lc+1){
34048                     this.onDenyColumnLock();
34049                     return;
34050                 }
34051                 if(lc != index){
34052                     cm.setLocked(index, true, true);
34053                     cm.moveColumn(index, lc);
34054                     this.grid.fireEvent("columnmove", index, lc);
34055                 }else{
34056                     cm.setLocked(index, true);
34057                 }
34058             break;
34059             case "unlock":
34060                 var lc = cm.getLockedCount();
34061                 if((lc-1) != index){
34062                     cm.setLocked(index, false, true);
34063                     cm.moveColumn(index, lc-1);
34064                     this.grid.fireEvent("columnmove", index, lc-1);
34065                 }else{
34066                     cm.setLocked(index, false);
34067                 }
34068             break;
34069             case 'wider': // used to expand cols on touch..
34070             case 'narrow':
34071                 var cw = cm.getColumnWidth(index);
34072                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34073                 cw = Math.max(0, cw);
34074                 cw = Math.min(cw,4000);
34075                 cm.setColumnWidth(index, cw);
34076                 break;
34077                 
34078             default:
34079                 index = cm.getIndexById(item.id.substr(4));
34080                 if(index != -1){
34081                     if(item.checked && cm.getColumnCount(true) <= 1){
34082                         this.onDenyColumnHide();
34083                         return false;
34084                     }
34085                     cm.setHidden(index, item.checked);
34086                 }
34087         }
34088         return true;
34089     },
34090
34091     beforeColMenuShow : function(){
34092         var cm = this.cm,  colCount = cm.getColumnCount();
34093         this.colMenu.removeAll();
34094         for(var i = 0; i < colCount; i++){
34095             this.colMenu.add(new Roo.menu.CheckItem({
34096                 id: "col-"+cm.getColumnId(i),
34097                 text: cm.getColumnHeader(i),
34098                 checked: !cm.isHidden(i),
34099                 hideOnClick:false
34100             }));
34101         }
34102     },
34103
34104     handleHdCtx : function(g, index, e){
34105         e.stopEvent();
34106         var hd = this.getHeaderCell(index);
34107         this.hdCtxIndex = index;
34108         var ms = this.hmenu.items, cm = this.cm;
34109         ms.get("asc").setDisabled(!cm.isSortable(index));
34110         ms.get("desc").setDisabled(!cm.isSortable(index));
34111         if(this.grid.enableColLock !== false){
34112             ms.get("lock").setDisabled(cm.isLocked(index));
34113             ms.get("unlock").setDisabled(!cm.isLocked(index));
34114         }
34115         this.hmenu.show(hd, "tl-bl");
34116     },
34117
34118     handleHdOver : function(e){
34119         var hd = this.findHeaderCell(e.getTarget());
34120         if(hd && !this.headersDisabled){
34121             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34122                this.fly(hd).addClass("x-grid-hd-over");
34123             }
34124         }
34125     },
34126
34127     handleHdOut : function(e){
34128         var hd = this.findHeaderCell(e.getTarget());
34129         if(hd){
34130             this.fly(hd).removeClass("x-grid-hd-over");
34131         }
34132     },
34133
34134     handleSplitDblClick : function(e, t){
34135         var i = this.getCellIndex(t);
34136         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34137             this.autoSizeColumn(i, true);
34138             this.layout();
34139         }
34140     },
34141
34142     render : function(){
34143
34144         var cm = this.cm;
34145         var colCount = cm.getColumnCount();
34146
34147         if(this.grid.monitorWindowResize === true){
34148             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34149         }
34150         var header = this.renderHeaders();
34151         var body = this.templates.body.apply({rows:""});
34152         var html = this.templates.master.apply({
34153             lockedBody: body,
34154             body: body,
34155             lockedHeader: header[0],
34156             header: header[1]
34157         });
34158
34159         //this.updateColumns();
34160
34161         this.grid.getGridEl().dom.innerHTML = html;
34162
34163         this.initElements();
34164         
34165         // a kludge to fix the random scolling effect in webkit
34166         this.el.on("scroll", function() {
34167             this.el.dom.scrollTop=0; // hopefully not recursive..
34168         },this);
34169
34170         this.scroller.on("scroll", this.handleScroll, this);
34171         this.lockedBody.on("mousewheel", this.handleWheel, this);
34172         this.mainBody.on("mousewheel", this.handleWheel, this);
34173
34174         this.mainHd.on("mouseover", this.handleHdOver, this);
34175         this.mainHd.on("mouseout", this.handleHdOut, this);
34176         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34177                 {delegate: "."+this.splitClass});
34178
34179         this.lockedHd.on("mouseover", this.handleHdOver, this);
34180         this.lockedHd.on("mouseout", this.handleHdOut, this);
34181         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34182                 {delegate: "."+this.splitClass});
34183
34184         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34185             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34186         }
34187
34188         this.updateSplitters();
34189
34190         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34191             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34192             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34193         }
34194
34195         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34196             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34197             this.hmenu.add(
34198                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34199                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34200             );
34201             if(this.grid.enableColLock !== false){
34202                 this.hmenu.add('-',
34203                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34204                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34205                 );
34206             }
34207             if (Roo.isTouch) {
34208                  this.hmenu.add('-',
34209                     {id:"wider", text: this.columnsWiderText},
34210                     {id:"narrow", text: this.columnsNarrowText }
34211                 );
34212                 
34213                  
34214             }
34215             
34216             if(this.grid.enableColumnHide !== false){
34217
34218                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34219                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34220                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34221
34222                 this.hmenu.add('-',
34223                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34224                 );
34225             }
34226             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34227
34228             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34229         }
34230
34231         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34232             this.dd = new Roo.grid.GridDragZone(this.grid, {
34233                 ddGroup : this.grid.ddGroup || 'GridDD'
34234             });
34235             
34236         }
34237
34238         /*
34239         for(var i = 0; i < colCount; i++){
34240             if(cm.isHidden(i)){
34241                 this.hideColumn(i);
34242             }
34243             if(cm.config[i].align){
34244                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34245                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34246             }
34247         }*/
34248         
34249         this.updateHeaderSortState();
34250
34251         this.beforeInitialResize();
34252         this.layout(true);
34253
34254         // two part rendering gives faster view to the user
34255         this.renderPhase2.defer(1, this);
34256     },
34257
34258     renderPhase2 : function(){
34259         // render the rows now
34260         this.refresh();
34261         if(this.grid.autoSizeColumns){
34262             this.autoSizeColumns();
34263         }
34264     },
34265
34266     beforeInitialResize : function(){
34267
34268     },
34269
34270     onColumnSplitterMoved : function(i, w){
34271         this.userResized = true;
34272         var cm = this.grid.colModel;
34273         cm.setColumnWidth(i, w, true);
34274         var cid = cm.getColumnId(i);
34275         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34276         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34277         this.updateSplitters();
34278         this.layout();
34279         this.grid.fireEvent("columnresize", i, w);
34280     },
34281
34282     syncRowHeights : function(startIndex, endIndex){
34283         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34284             startIndex = startIndex || 0;
34285             var mrows = this.getBodyTable().rows;
34286             var lrows = this.getLockedTable().rows;
34287             var len = mrows.length-1;
34288             endIndex = Math.min(endIndex || len, len);
34289             for(var i = startIndex; i <= endIndex; i++){
34290                 var m = mrows[i], l = lrows[i];
34291                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34292                 m.style.height = l.style.height = h + "px";
34293             }
34294         }
34295     },
34296
34297     layout : function(initialRender, is2ndPass){
34298         var g = this.grid;
34299         var auto = g.autoHeight;
34300         var scrollOffset = 16;
34301         var c = g.getGridEl(), cm = this.cm,
34302                 expandCol = g.autoExpandColumn,
34303                 gv = this;
34304         //c.beginMeasure();
34305
34306         if(!c.dom.offsetWidth){ // display:none?
34307             if(initialRender){
34308                 this.lockedWrap.show();
34309                 this.mainWrap.show();
34310             }
34311             return;
34312         }
34313
34314         var hasLock = this.cm.isLocked(0);
34315
34316         var tbh = this.headerPanel.getHeight();
34317         var bbh = this.footerPanel.getHeight();
34318
34319         if(auto){
34320             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34321             var newHeight = ch + c.getBorderWidth("tb");
34322             if(g.maxHeight){
34323                 newHeight = Math.min(g.maxHeight, newHeight);
34324             }
34325             c.setHeight(newHeight);
34326         }
34327
34328         if(g.autoWidth){
34329             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34330         }
34331
34332         var s = this.scroller;
34333
34334         var csize = c.getSize(true);
34335
34336         this.el.setSize(csize.width, csize.height);
34337
34338         this.headerPanel.setWidth(csize.width);
34339         this.footerPanel.setWidth(csize.width);
34340
34341         var hdHeight = this.mainHd.getHeight();
34342         var vw = csize.width;
34343         var vh = csize.height - (tbh + bbh);
34344
34345         s.setSize(vw, vh);
34346
34347         var bt = this.getBodyTable();
34348         
34349         if(cm.getLockedCount() == cm.config.length){
34350             bt = this.getLockedTable();
34351         }
34352         
34353         var ltWidth = hasLock ?
34354                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34355
34356         var scrollHeight = bt.offsetHeight;
34357         var scrollWidth = ltWidth + bt.offsetWidth;
34358         var vscroll = false, hscroll = false;
34359
34360         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34361
34362         var lw = this.lockedWrap, mw = this.mainWrap;
34363         var lb = this.lockedBody, mb = this.mainBody;
34364
34365         setTimeout(function(){
34366             var t = s.dom.offsetTop;
34367             var w = s.dom.clientWidth,
34368                 h = s.dom.clientHeight;
34369
34370             lw.setTop(t);
34371             lw.setSize(ltWidth, h);
34372
34373             mw.setLeftTop(ltWidth, t);
34374             mw.setSize(w-ltWidth, h);
34375
34376             lb.setHeight(h-hdHeight);
34377             mb.setHeight(h-hdHeight);
34378
34379             if(is2ndPass !== true && !gv.userResized && expandCol){
34380                 // high speed resize without full column calculation
34381                 
34382                 var ci = cm.getIndexById(expandCol);
34383                 if (ci < 0) {
34384                     ci = cm.findColumnIndex(expandCol);
34385                 }
34386                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34387                 var expandId = cm.getColumnId(ci);
34388                 var  tw = cm.getTotalWidth(false);
34389                 var currentWidth = cm.getColumnWidth(ci);
34390                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34391                 if(currentWidth != cw){
34392                     cm.setColumnWidth(ci, cw, true);
34393                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34394                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34395                     gv.updateSplitters();
34396                     gv.layout(false, true);
34397                 }
34398             }
34399
34400             if(initialRender){
34401                 lw.show();
34402                 mw.show();
34403             }
34404             //c.endMeasure();
34405         }, 10);
34406     },
34407
34408     onWindowResize : function(){
34409         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34410             return;
34411         }
34412         this.layout();
34413     },
34414
34415     appendFooter : function(parentEl){
34416         return null;
34417     },
34418
34419     sortAscText : "Sort Ascending",
34420     sortDescText : "Sort Descending",
34421     lockText : "Lock Column",
34422     unlockText : "Unlock Column",
34423     columnsText : "Columns",
34424  
34425     columnsWiderText : "Wider",
34426     columnsNarrowText : "Thinner"
34427 });
34428
34429
34430 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34431     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34432     this.proxy.el.addClass('x-grid3-col-dd');
34433 };
34434
34435 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34436     handleMouseDown : function(e){
34437
34438     },
34439
34440     callHandleMouseDown : function(e){
34441         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34442     }
34443 });
34444 /*
34445  * Based on:
34446  * Ext JS Library 1.1.1
34447  * Copyright(c) 2006-2007, Ext JS, LLC.
34448  *
34449  * Originally Released Under LGPL - original licence link has changed is not relivant.
34450  *
34451  * Fork - LGPL
34452  * <script type="text/javascript">
34453  */
34454  
34455 // private
34456 // This is a support class used internally by the Grid components
34457 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34458     this.grid = grid;
34459     this.view = grid.getView();
34460     this.proxy = this.view.resizeProxy;
34461     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34462         "gridSplitters" + this.grid.getGridEl().id, {
34463         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34464     });
34465     this.setHandleElId(Roo.id(hd));
34466     this.setOuterHandleElId(Roo.id(hd2));
34467     this.scroll = false;
34468 };
34469 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34470     fly: Roo.Element.fly,
34471
34472     b4StartDrag : function(x, y){
34473         this.view.headersDisabled = true;
34474         this.proxy.setHeight(this.view.mainWrap.getHeight());
34475         var w = this.cm.getColumnWidth(this.cellIndex);
34476         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34477         this.resetConstraints();
34478         this.setXConstraint(minw, 1000);
34479         this.setYConstraint(0, 0);
34480         this.minX = x - minw;
34481         this.maxX = x + 1000;
34482         this.startPos = x;
34483         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34484     },
34485
34486
34487     handleMouseDown : function(e){
34488         ev = Roo.EventObject.setEvent(e);
34489         var t = this.fly(ev.getTarget());
34490         if(t.hasClass("x-grid-split")){
34491             this.cellIndex = this.view.getCellIndex(t.dom);
34492             this.split = t.dom;
34493             this.cm = this.grid.colModel;
34494             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34495                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34496             }
34497         }
34498     },
34499
34500     endDrag : function(e){
34501         this.view.headersDisabled = false;
34502         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34503         var diff = endX - this.startPos;
34504         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34505     },
34506
34507     autoOffset : function(){
34508         this.setDelta(0,0);
34509     }
34510 });/*
34511  * Based on:
34512  * Ext JS Library 1.1.1
34513  * Copyright(c) 2006-2007, Ext JS, LLC.
34514  *
34515  * Originally Released Under LGPL - original licence link has changed is not relivant.
34516  *
34517  * Fork - LGPL
34518  * <script type="text/javascript">
34519  */
34520  
34521 // private
34522 // This is a support class used internally by the Grid components
34523 Roo.grid.GridDragZone = function(grid, config){
34524     this.view = grid.getView();
34525     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34526     if(this.view.lockedBody){
34527         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34528         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34529     }
34530     this.scroll = false;
34531     this.grid = grid;
34532     this.ddel = document.createElement('div');
34533     this.ddel.className = 'x-grid-dd-wrap';
34534 };
34535
34536 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34537     ddGroup : "GridDD",
34538
34539     getDragData : function(e){
34540         var t = Roo.lib.Event.getTarget(e);
34541         var rowIndex = this.view.findRowIndex(t);
34542         var sm = this.grid.selModel;
34543             
34544         //Roo.log(rowIndex);
34545         
34546         if (sm.getSelectedCell) {
34547             // cell selection..
34548             if (!sm.getSelectedCell()) {
34549                 return false;
34550             }
34551             if (rowIndex != sm.getSelectedCell()[0]) {
34552                 return false;
34553             }
34554         
34555         }
34556         
34557         if(rowIndex !== false){
34558             
34559             // if editorgrid.. 
34560             
34561             
34562             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34563                
34564             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34565               //  
34566             //}
34567             if (e.hasModifier()){
34568                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34569             }
34570             
34571             Roo.log("getDragData");
34572             
34573             return {
34574                 grid: this.grid,
34575                 ddel: this.ddel,
34576                 rowIndex: rowIndex,
34577                 selections:sm.getSelections ? sm.getSelections() : (
34578                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34579                 )
34580             };
34581         }
34582         return false;
34583     },
34584
34585     onInitDrag : function(e){
34586         var data = this.dragData;
34587         this.ddel.innerHTML = this.grid.getDragDropText();
34588         this.proxy.update(this.ddel);
34589         // fire start drag?
34590     },
34591
34592     afterRepair : function(){
34593         this.dragging = false;
34594     },
34595
34596     getRepairXY : function(e, data){
34597         return false;
34598     },
34599
34600     onEndDrag : function(data, e){
34601         // fire end drag?
34602     },
34603
34604     onValidDrop : function(dd, e, id){
34605         // fire drag drop?
34606         this.hideProxy();
34607     },
34608
34609     beforeInvalidDrop : function(e, id){
34610
34611     }
34612 });/*
34613  * Based on:
34614  * Ext JS Library 1.1.1
34615  * Copyright(c) 2006-2007, Ext JS, LLC.
34616  *
34617  * Originally Released Under LGPL - original licence link has changed is not relivant.
34618  *
34619  * Fork - LGPL
34620  * <script type="text/javascript">
34621  */
34622  
34623
34624 /**
34625  * @class Roo.grid.ColumnModel
34626  * @extends Roo.util.Observable
34627  * This is the default implementation of a ColumnModel used by the Grid. It defines
34628  * the columns in the grid.
34629  * <br>Usage:<br>
34630  <pre><code>
34631  var colModel = new Roo.grid.ColumnModel([
34632         {header: "Ticker", width: 60, sortable: true, locked: true},
34633         {header: "Company Name", width: 150, sortable: true},
34634         {header: "Market Cap.", width: 100, sortable: true},
34635         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34636         {header: "Employees", width: 100, sortable: true, resizable: false}
34637  ]);
34638  </code></pre>
34639  * <p>
34640  
34641  * The config options listed for this class are options which may appear in each
34642  * individual column definition.
34643  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34644  * @constructor
34645  * @param {Object} config An Array of column config objects. See this class's
34646  * config objects for details.
34647 */
34648 Roo.grid.ColumnModel = function(config){
34649         /**
34650      * The config passed into the constructor
34651      */
34652     this.config = config;
34653     this.lookup = {};
34654
34655     // if no id, create one
34656     // if the column does not have a dataIndex mapping,
34657     // map it to the order it is in the config
34658     for(var i = 0, len = config.length; i < len; i++){
34659         var c = config[i];
34660         if(typeof c.dataIndex == "undefined"){
34661             c.dataIndex = i;
34662         }
34663         if(typeof c.renderer == "string"){
34664             c.renderer = Roo.util.Format[c.renderer];
34665         }
34666         if(typeof c.id == "undefined"){
34667             c.id = Roo.id();
34668         }
34669         if(c.editor && c.editor.xtype){
34670             c.editor  = Roo.factory(c.editor, Roo.grid);
34671         }
34672         if(c.editor && c.editor.isFormField){
34673             c.editor = new Roo.grid.GridEditor(c.editor);
34674         }
34675         this.lookup[c.id] = c;
34676     }
34677
34678     /**
34679      * The width of columns which have no width specified (defaults to 100)
34680      * @type Number
34681      */
34682     this.defaultWidth = 100;
34683
34684     /**
34685      * Default sortable of columns which have no sortable specified (defaults to false)
34686      * @type Boolean
34687      */
34688     this.defaultSortable = false;
34689
34690     this.addEvents({
34691         /**
34692              * @event widthchange
34693              * Fires when the width of a column changes.
34694              * @param {ColumnModel} this
34695              * @param {Number} columnIndex The column index
34696              * @param {Number} newWidth The new width
34697              */
34698             "widthchange": true,
34699         /**
34700              * @event headerchange
34701              * Fires when the text of a header changes.
34702              * @param {ColumnModel} this
34703              * @param {Number} columnIndex The column index
34704              * @param {Number} newText The new header text
34705              */
34706             "headerchange": true,
34707         /**
34708              * @event hiddenchange
34709              * Fires when a column is hidden or "unhidden".
34710              * @param {ColumnModel} this
34711              * @param {Number} columnIndex The column index
34712              * @param {Boolean} hidden true if hidden, false otherwise
34713              */
34714             "hiddenchange": true,
34715             /**
34716          * @event columnmoved
34717          * Fires when a column is moved.
34718          * @param {ColumnModel} this
34719          * @param {Number} oldIndex
34720          * @param {Number} newIndex
34721          */
34722         "columnmoved" : true,
34723         /**
34724          * @event columlockchange
34725          * Fires when a column's locked state is changed
34726          * @param {ColumnModel} this
34727          * @param {Number} colIndex
34728          * @param {Boolean} locked true if locked
34729          */
34730         "columnlockchange" : true
34731     });
34732     Roo.grid.ColumnModel.superclass.constructor.call(this);
34733 };
34734 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34735     /**
34736      * @cfg {String} header The header text to display in the Grid view.
34737      */
34738     /**
34739      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34740      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34741      * specified, the column's index is used as an index into the Record's data Array.
34742      */
34743     /**
34744      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34745      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34746      */
34747     /**
34748      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34749      * Defaults to the value of the {@link #defaultSortable} property.
34750      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34751      */
34752     /**
34753      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34754      */
34755     /**
34756      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34757      */
34758     /**
34759      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34760      */
34761     /**
34762      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34763      */
34764     /**
34765      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34766      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34767      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34768      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34769      */
34770        /**
34771      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34772      */
34773     /**
34774      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34775      */
34776     /**
34777      * @cfg {String} cursor (Optional)
34778      */
34779     /**
34780      * @cfg {String} tooltip (Optional)
34781      */
34782     /**
34783      * @cfg {Number} xs (Optional)
34784      */
34785     /**
34786      * @cfg {Number} sm (Optional)
34787      */
34788     /**
34789      * @cfg {Number} md (Optional)
34790      */
34791     /**
34792      * @cfg {Number} lg (Optional)
34793      */
34794     /**
34795      * Returns the id of the column at the specified index.
34796      * @param {Number} index The column index
34797      * @return {String} the id
34798      */
34799     getColumnId : function(index){
34800         return this.config[index].id;
34801     },
34802
34803     /**
34804      * Returns the column for a specified id.
34805      * @param {String} id The column id
34806      * @return {Object} the column
34807      */
34808     getColumnById : function(id){
34809         return this.lookup[id];
34810     },
34811
34812     
34813     /**
34814      * Returns the column for a specified dataIndex.
34815      * @param {String} dataIndex The column dataIndex
34816      * @return {Object|Boolean} the column or false if not found
34817      */
34818     getColumnByDataIndex: function(dataIndex){
34819         var index = this.findColumnIndex(dataIndex);
34820         return index > -1 ? this.config[index] : false;
34821     },
34822     
34823     /**
34824      * Returns the index for a specified column id.
34825      * @param {String} id The column id
34826      * @return {Number} the index, or -1 if not found
34827      */
34828     getIndexById : function(id){
34829         for(var i = 0, len = this.config.length; i < len; i++){
34830             if(this.config[i].id == id){
34831                 return i;
34832             }
34833         }
34834         return -1;
34835     },
34836     
34837     /**
34838      * Returns the index for a specified column dataIndex.
34839      * @param {String} dataIndex The column dataIndex
34840      * @return {Number} the index, or -1 if not found
34841      */
34842     
34843     findColumnIndex : function(dataIndex){
34844         for(var i = 0, len = this.config.length; i < len; i++){
34845             if(this.config[i].dataIndex == dataIndex){
34846                 return i;
34847             }
34848         }
34849         return -1;
34850     },
34851     
34852     
34853     moveColumn : function(oldIndex, newIndex){
34854         var c = this.config[oldIndex];
34855         this.config.splice(oldIndex, 1);
34856         this.config.splice(newIndex, 0, c);
34857         this.dataMap = null;
34858         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34859     },
34860
34861     isLocked : function(colIndex){
34862         return this.config[colIndex].locked === true;
34863     },
34864
34865     setLocked : function(colIndex, value, suppressEvent){
34866         if(this.isLocked(colIndex) == value){
34867             return;
34868         }
34869         this.config[colIndex].locked = value;
34870         if(!suppressEvent){
34871             this.fireEvent("columnlockchange", this, colIndex, value);
34872         }
34873     },
34874
34875     getTotalLockedWidth : function(){
34876         var totalWidth = 0;
34877         for(var i = 0; i < this.config.length; i++){
34878             if(this.isLocked(i) && !this.isHidden(i)){
34879                 this.totalWidth += this.getColumnWidth(i);
34880             }
34881         }
34882         return totalWidth;
34883     },
34884
34885     getLockedCount : function(){
34886         for(var i = 0, len = this.config.length; i < len; i++){
34887             if(!this.isLocked(i)){
34888                 return i;
34889             }
34890         }
34891         
34892         return this.config.length;
34893     },
34894
34895     /**
34896      * Returns the number of columns.
34897      * @return {Number}
34898      */
34899     getColumnCount : function(visibleOnly){
34900         if(visibleOnly === true){
34901             var c = 0;
34902             for(var i = 0, len = this.config.length; i < len; i++){
34903                 if(!this.isHidden(i)){
34904                     c++;
34905                 }
34906             }
34907             return c;
34908         }
34909         return this.config.length;
34910     },
34911
34912     /**
34913      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34914      * @param {Function} fn
34915      * @param {Object} scope (optional)
34916      * @return {Array} result
34917      */
34918     getColumnsBy : function(fn, scope){
34919         var r = [];
34920         for(var i = 0, len = this.config.length; i < len; i++){
34921             var c = this.config[i];
34922             if(fn.call(scope||this, c, i) === true){
34923                 r[r.length] = c;
34924             }
34925         }
34926         return r;
34927     },
34928
34929     /**
34930      * Returns true if the specified column is sortable.
34931      * @param {Number} col The column index
34932      * @return {Boolean}
34933      */
34934     isSortable : function(col){
34935         if(typeof this.config[col].sortable == "undefined"){
34936             return this.defaultSortable;
34937         }
34938         return this.config[col].sortable;
34939     },
34940
34941     /**
34942      * Returns the rendering (formatting) function defined for the column.
34943      * @param {Number} col The column index.
34944      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34945      */
34946     getRenderer : function(col){
34947         if(!this.config[col].renderer){
34948             return Roo.grid.ColumnModel.defaultRenderer;
34949         }
34950         return this.config[col].renderer;
34951     },
34952
34953     /**
34954      * Sets the rendering (formatting) function for a column.
34955      * @param {Number} col The column index
34956      * @param {Function} fn The function to use to process the cell's raw data
34957      * to return HTML markup for the grid view. The render function is called with
34958      * the following parameters:<ul>
34959      * <li>Data value.</li>
34960      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34961      * <li>css A CSS style string to apply to the table cell.</li>
34962      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34963      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34964      * <li>Row index</li>
34965      * <li>Column index</li>
34966      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34967      */
34968     setRenderer : function(col, fn){
34969         this.config[col].renderer = fn;
34970     },
34971
34972     /**
34973      * Returns the width for the specified column.
34974      * @param {Number} col The column index
34975      * @return {Number}
34976      */
34977     getColumnWidth : function(col){
34978         return this.config[col].width * 1 || this.defaultWidth;
34979     },
34980
34981     /**
34982      * Sets the width for a column.
34983      * @param {Number} col The column index
34984      * @param {Number} width The new width
34985      */
34986     setColumnWidth : function(col, width, suppressEvent){
34987         this.config[col].width = width;
34988         this.totalWidth = null;
34989         if(!suppressEvent){
34990              this.fireEvent("widthchange", this, col, width);
34991         }
34992     },
34993
34994     /**
34995      * Returns the total width of all columns.
34996      * @param {Boolean} includeHidden True to include hidden column widths
34997      * @return {Number}
34998      */
34999     getTotalWidth : function(includeHidden){
35000         if(!this.totalWidth){
35001             this.totalWidth = 0;
35002             for(var i = 0, len = this.config.length; i < len; i++){
35003                 if(includeHidden || !this.isHidden(i)){
35004                     this.totalWidth += this.getColumnWidth(i);
35005                 }
35006             }
35007         }
35008         return this.totalWidth;
35009     },
35010
35011     /**
35012      * Returns the header for the specified column.
35013      * @param {Number} col The column index
35014      * @return {String}
35015      */
35016     getColumnHeader : function(col){
35017         return this.config[col].header;
35018     },
35019
35020     /**
35021      * Sets the header for a column.
35022      * @param {Number} col The column index
35023      * @param {String} header The new header
35024      */
35025     setColumnHeader : function(col, header){
35026         this.config[col].header = header;
35027         this.fireEvent("headerchange", this, col, header);
35028     },
35029
35030     /**
35031      * Returns the tooltip for the specified column.
35032      * @param {Number} col The column index
35033      * @return {String}
35034      */
35035     getColumnTooltip : function(col){
35036             return this.config[col].tooltip;
35037     },
35038     /**
35039      * Sets the tooltip for a column.
35040      * @param {Number} col The column index
35041      * @param {String} tooltip The new tooltip
35042      */
35043     setColumnTooltip : function(col, tooltip){
35044             this.config[col].tooltip = tooltip;
35045     },
35046
35047     /**
35048      * Returns the dataIndex for the specified column.
35049      * @param {Number} col The column index
35050      * @return {Number}
35051      */
35052     getDataIndex : function(col){
35053         return this.config[col].dataIndex;
35054     },
35055
35056     /**
35057      * Sets the dataIndex for a column.
35058      * @param {Number} col The column index
35059      * @param {Number} dataIndex The new dataIndex
35060      */
35061     setDataIndex : function(col, dataIndex){
35062         this.config[col].dataIndex = dataIndex;
35063     },
35064
35065     
35066     
35067     /**
35068      * Returns true if the cell is editable.
35069      * @param {Number} colIndex The column index
35070      * @param {Number} rowIndex The row index - this is nto actually used..?
35071      * @return {Boolean}
35072      */
35073     isCellEditable : function(colIndex, rowIndex){
35074         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35075     },
35076
35077     /**
35078      * Returns the editor defined for the cell/column.
35079      * return false or null to disable editing.
35080      * @param {Number} colIndex The column index
35081      * @param {Number} rowIndex The row index
35082      * @return {Object}
35083      */
35084     getCellEditor : function(colIndex, rowIndex){
35085         return this.config[colIndex].editor;
35086     },
35087
35088     /**
35089      * Sets if a column is editable.
35090      * @param {Number} col The column index
35091      * @param {Boolean} editable True if the column is editable
35092      */
35093     setEditable : function(col, editable){
35094         this.config[col].editable = editable;
35095     },
35096
35097
35098     /**
35099      * Returns true if the column is hidden.
35100      * @param {Number} colIndex The column index
35101      * @return {Boolean}
35102      */
35103     isHidden : function(colIndex){
35104         return this.config[colIndex].hidden;
35105     },
35106
35107
35108     /**
35109      * Returns true if the column width cannot be changed
35110      */
35111     isFixed : function(colIndex){
35112         return this.config[colIndex].fixed;
35113     },
35114
35115     /**
35116      * Returns true if the column can be resized
35117      * @return {Boolean}
35118      */
35119     isResizable : function(colIndex){
35120         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35121     },
35122     /**
35123      * Sets if a column is hidden.
35124      * @param {Number} colIndex The column index
35125      * @param {Boolean} hidden True if the column is hidden
35126      */
35127     setHidden : function(colIndex, hidden){
35128         this.config[colIndex].hidden = hidden;
35129         this.totalWidth = null;
35130         this.fireEvent("hiddenchange", this, colIndex, hidden);
35131     },
35132
35133     /**
35134      * Sets the editor for a column.
35135      * @param {Number} col The column index
35136      * @param {Object} editor The editor object
35137      */
35138     setEditor : function(col, editor){
35139         this.config[col].editor = editor;
35140     }
35141 });
35142
35143 Roo.grid.ColumnModel.defaultRenderer = function(value)
35144 {
35145     if(typeof value == "object") {
35146         return value;
35147     }
35148         if(typeof value == "string" && value.length < 1){
35149             return "&#160;";
35150         }
35151     
35152         return String.format("{0}", value);
35153 };
35154
35155 // Alias for backwards compatibility
35156 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35157 /*
35158  * Based on:
35159  * Ext JS Library 1.1.1
35160  * Copyright(c) 2006-2007, Ext JS, LLC.
35161  *
35162  * Originally Released Under LGPL - original licence link has changed is not relivant.
35163  *
35164  * Fork - LGPL
35165  * <script type="text/javascript">
35166  */
35167
35168 /**
35169  * @class Roo.grid.AbstractSelectionModel
35170  * @extends Roo.util.Observable
35171  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35172  * implemented by descendant classes.  This class should not be directly instantiated.
35173  * @constructor
35174  */
35175 Roo.grid.AbstractSelectionModel = function(){
35176     this.locked = false;
35177     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35178 };
35179
35180 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35181     /** @ignore Called by the grid automatically. Do not call directly. */
35182     init : function(grid){
35183         this.grid = grid;
35184         this.initEvents();
35185     },
35186
35187     /**
35188      * Locks the selections.
35189      */
35190     lock : function(){
35191         this.locked = true;
35192     },
35193
35194     /**
35195      * Unlocks the selections.
35196      */
35197     unlock : function(){
35198         this.locked = false;
35199     },
35200
35201     /**
35202      * Returns true if the selections are locked.
35203      * @return {Boolean}
35204      */
35205     isLocked : function(){
35206         return this.locked;
35207     }
35208 });/*
35209  * Based on:
35210  * Ext JS Library 1.1.1
35211  * Copyright(c) 2006-2007, Ext JS, LLC.
35212  *
35213  * Originally Released Under LGPL - original licence link has changed is not relivant.
35214  *
35215  * Fork - LGPL
35216  * <script type="text/javascript">
35217  */
35218 /**
35219  * @extends Roo.grid.AbstractSelectionModel
35220  * @class Roo.grid.RowSelectionModel
35221  * The default SelectionModel used by {@link Roo.grid.Grid}.
35222  * It supports multiple selections and keyboard selection/navigation. 
35223  * @constructor
35224  * @param {Object} config
35225  */
35226 Roo.grid.RowSelectionModel = function(config){
35227     Roo.apply(this, config);
35228     this.selections = new Roo.util.MixedCollection(false, function(o){
35229         return o.id;
35230     });
35231
35232     this.last = false;
35233     this.lastActive = false;
35234
35235     this.addEvents({
35236         /**
35237              * @event selectionchange
35238              * Fires when the selection changes
35239              * @param {SelectionModel} this
35240              */
35241             "selectionchange" : true,
35242         /**
35243              * @event afterselectionchange
35244              * Fires after the selection changes (eg. by key press or clicking)
35245              * @param {SelectionModel} this
35246              */
35247             "afterselectionchange" : true,
35248         /**
35249              * @event beforerowselect
35250              * Fires when a row is selected being selected, return false to cancel.
35251              * @param {SelectionModel} this
35252              * @param {Number} rowIndex The selected index
35253              * @param {Boolean} keepExisting False if other selections will be cleared
35254              */
35255             "beforerowselect" : true,
35256         /**
35257              * @event rowselect
35258              * Fires when a row is selected.
35259              * @param {SelectionModel} this
35260              * @param {Number} rowIndex The selected index
35261              * @param {Roo.data.Record} r The record
35262              */
35263             "rowselect" : true,
35264         /**
35265              * @event rowdeselect
35266              * Fires when a row is deselected.
35267              * @param {SelectionModel} this
35268              * @param {Number} rowIndex The selected index
35269              */
35270         "rowdeselect" : true
35271     });
35272     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35273     this.locked = false;
35274 };
35275
35276 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35277     /**
35278      * @cfg {Boolean} singleSelect
35279      * True to allow selection of only one row at a time (defaults to false)
35280      */
35281     singleSelect : false,
35282
35283     // private
35284     initEvents : function(){
35285
35286         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35287             this.grid.on("mousedown", this.handleMouseDown, this);
35288         }else{ // allow click to work like normal
35289             this.grid.on("rowclick", this.handleDragableRowClick, this);
35290         }
35291
35292         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35293             "up" : function(e){
35294                 if(!e.shiftKey){
35295                     this.selectPrevious(e.shiftKey);
35296                 }else if(this.last !== false && this.lastActive !== false){
35297                     var last = this.last;
35298                     this.selectRange(this.last,  this.lastActive-1);
35299                     this.grid.getView().focusRow(this.lastActive);
35300                     if(last !== false){
35301                         this.last = last;
35302                     }
35303                 }else{
35304                     this.selectFirstRow();
35305                 }
35306                 this.fireEvent("afterselectionchange", this);
35307             },
35308             "down" : function(e){
35309                 if(!e.shiftKey){
35310                     this.selectNext(e.shiftKey);
35311                 }else if(this.last !== false && this.lastActive !== false){
35312                     var last = this.last;
35313                     this.selectRange(this.last,  this.lastActive+1);
35314                     this.grid.getView().focusRow(this.lastActive);
35315                     if(last !== false){
35316                         this.last = last;
35317                     }
35318                 }else{
35319                     this.selectFirstRow();
35320                 }
35321                 this.fireEvent("afterselectionchange", this);
35322             },
35323             scope: this
35324         });
35325
35326         var view = this.grid.view;
35327         view.on("refresh", this.onRefresh, this);
35328         view.on("rowupdated", this.onRowUpdated, this);
35329         view.on("rowremoved", this.onRemove, this);
35330     },
35331
35332     // private
35333     onRefresh : function(){
35334         var ds = this.grid.dataSource, i, v = this.grid.view;
35335         var s = this.selections;
35336         s.each(function(r){
35337             if((i = ds.indexOfId(r.id)) != -1){
35338                 v.onRowSelect(i);
35339                 s.add(ds.getAt(i)); // updating the selection relate data
35340             }else{
35341                 s.remove(r);
35342             }
35343         });
35344     },
35345
35346     // private
35347     onRemove : function(v, index, r){
35348         this.selections.remove(r);
35349     },
35350
35351     // private
35352     onRowUpdated : function(v, index, r){
35353         if(this.isSelected(r)){
35354             v.onRowSelect(index);
35355         }
35356     },
35357
35358     /**
35359      * Select records.
35360      * @param {Array} records The records to select
35361      * @param {Boolean} keepExisting (optional) True to keep existing selections
35362      */
35363     selectRecords : function(records, keepExisting){
35364         if(!keepExisting){
35365             this.clearSelections();
35366         }
35367         var ds = this.grid.dataSource;
35368         for(var i = 0, len = records.length; i < len; i++){
35369             this.selectRow(ds.indexOf(records[i]), true);
35370         }
35371     },
35372
35373     /**
35374      * Gets the number of selected rows.
35375      * @return {Number}
35376      */
35377     getCount : function(){
35378         return this.selections.length;
35379     },
35380
35381     /**
35382      * Selects the first row in the grid.
35383      */
35384     selectFirstRow : function(){
35385         this.selectRow(0);
35386     },
35387
35388     /**
35389      * Select the last row.
35390      * @param {Boolean} keepExisting (optional) True to keep existing selections
35391      */
35392     selectLastRow : function(keepExisting){
35393         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35394     },
35395
35396     /**
35397      * Selects the row immediately following the last selected row.
35398      * @param {Boolean} keepExisting (optional) True to keep existing selections
35399      */
35400     selectNext : function(keepExisting){
35401         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35402             this.selectRow(this.last+1, keepExisting);
35403             this.grid.getView().focusRow(this.last);
35404         }
35405     },
35406
35407     /**
35408      * Selects the row that precedes the last selected row.
35409      * @param {Boolean} keepExisting (optional) True to keep existing selections
35410      */
35411     selectPrevious : function(keepExisting){
35412         if(this.last){
35413             this.selectRow(this.last-1, keepExisting);
35414             this.grid.getView().focusRow(this.last);
35415         }
35416     },
35417
35418     /**
35419      * Returns the selected records
35420      * @return {Array} Array of selected records
35421      */
35422     getSelections : function(){
35423         return [].concat(this.selections.items);
35424     },
35425
35426     /**
35427      * Returns the first selected record.
35428      * @return {Record}
35429      */
35430     getSelected : function(){
35431         return this.selections.itemAt(0);
35432     },
35433
35434
35435     /**
35436      * Clears all selections.
35437      */
35438     clearSelections : function(fast){
35439         if(this.locked) {
35440             return;
35441         }
35442         if(fast !== true){
35443             var ds = this.grid.dataSource;
35444             var s = this.selections;
35445             s.each(function(r){
35446                 this.deselectRow(ds.indexOfId(r.id));
35447             }, this);
35448             s.clear();
35449         }else{
35450             this.selections.clear();
35451         }
35452         this.last = false;
35453     },
35454
35455
35456     /**
35457      * Selects all rows.
35458      */
35459     selectAll : function(){
35460         if(this.locked) {
35461             return;
35462         }
35463         this.selections.clear();
35464         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35465             this.selectRow(i, true);
35466         }
35467     },
35468
35469     /**
35470      * Returns True if there is a selection.
35471      * @return {Boolean}
35472      */
35473     hasSelection : function(){
35474         return this.selections.length > 0;
35475     },
35476
35477     /**
35478      * Returns True if the specified row is selected.
35479      * @param {Number/Record} record The record or index of the record to check
35480      * @return {Boolean}
35481      */
35482     isSelected : function(index){
35483         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35484         return (r && this.selections.key(r.id) ? true : false);
35485     },
35486
35487     /**
35488      * Returns True if the specified record id is selected.
35489      * @param {String} id The id of record to check
35490      * @return {Boolean}
35491      */
35492     isIdSelected : function(id){
35493         return (this.selections.key(id) ? true : false);
35494     },
35495
35496     // private
35497     handleMouseDown : function(e, t){
35498         var view = this.grid.getView(), rowIndex;
35499         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35500             return;
35501         };
35502         if(e.shiftKey && this.last !== false){
35503             var last = this.last;
35504             this.selectRange(last, rowIndex, e.ctrlKey);
35505             this.last = last; // reset the last
35506             view.focusRow(rowIndex);
35507         }else{
35508             var isSelected = this.isSelected(rowIndex);
35509             if(e.button !== 0 && isSelected){
35510                 view.focusRow(rowIndex);
35511             }else if(e.ctrlKey && isSelected){
35512                 this.deselectRow(rowIndex);
35513             }else if(!isSelected){
35514                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35515                 view.focusRow(rowIndex);
35516             }
35517         }
35518         this.fireEvent("afterselectionchange", this);
35519     },
35520     // private
35521     handleDragableRowClick :  function(grid, rowIndex, e) 
35522     {
35523         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35524             this.selectRow(rowIndex, false);
35525             grid.view.focusRow(rowIndex);
35526              this.fireEvent("afterselectionchange", this);
35527         }
35528     },
35529     
35530     /**
35531      * Selects multiple rows.
35532      * @param {Array} rows Array of the indexes of the row to select
35533      * @param {Boolean} keepExisting (optional) True to keep existing selections
35534      */
35535     selectRows : function(rows, keepExisting){
35536         if(!keepExisting){
35537             this.clearSelections();
35538         }
35539         for(var i = 0, len = rows.length; i < len; i++){
35540             this.selectRow(rows[i], true);
35541         }
35542     },
35543
35544     /**
35545      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35546      * @param {Number} startRow The index of the first row in the range
35547      * @param {Number} endRow The index of the last row in the range
35548      * @param {Boolean} keepExisting (optional) True to retain existing selections
35549      */
35550     selectRange : function(startRow, endRow, keepExisting){
35551         if(this.locked) {
35552             return;
35553         }
35554         if(!keepExisting){
35555             this.clearSelections();
35556         }
35557         if(startRow <= endRow){
35558             for(var i = startRow; i <= endRow; i++){
35559                 this.selectRow(i, true);
35560             }
35561         }else{
35562             for(var i = startRow; i >= endRow; i--){
35563                 this.selectRow(i, true);
35564             }
35565         }
35566     },
35567
35568     /**
35569      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35570      * @param {Number} startRow The index of the first row in the range
35571      * @param {Number} endRow The index of the last row in the range
35572      */
35573     deselectRange : function(startRow, endRow, preventViewNotify){
35574         if(this.locked) {
35575             return;
35576         }
35577         for(var i = startRow; i <= endRow; i++){
35578             this.deselectRow(i, preventViewNotify);
35579         }
35580     },
35581
35582     /**
35583      * Selects a row.
35584      * @param {Number} row The index of the row to select
35585      * @param {Boolean} keepExisting (optional) True to keep existing selections
35586      */
35587     selectRow : function(index, keepExisting, preventViewNotify){
35588         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35589             return;
35590         }
35591         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35592             if(!keepExisting || this.singleSelect){
35593                 this.clearSelections();
35594             }
35595             var r = this.grid.dataSource.getAt(index);
35596             this.selections.add(r);
35597             this.last = this.lastActive = index;
35598             if(!preventViewNotify){
35599                 this.grid.getView().onRowSelect(index);
35600             }
35601             this.fireEvent("rowselect", this, index, r);
35602             this.fireEvent("selectionchange", this);
35603         }
35604     },
35605
35606     /**
35607      * Deselects a row.
35608      * @param {Number} row The index of the row to deselect
35609      */
35610     deselectRow : function(index, preventViewNotify){
35611         if(this.locked) {
35612             return;
35613         }
35614         if(this.last == index){
35615             this.last = false;
35616         }
35617         if(this.lastActive == index){
35618             this.lastActive = false;
35619         }
35620         var r = this.grid.dataSource.getAt(index);
35621         this.selections.remove(r);
35622         if(!preventViewNotify){
35623             this.grid.getView().onRowDeselect(index);
35624         }
35625         this.fireEvent("rowdeselect", this, index);
35626         this.fireEvent("selectionchange", this);
35627     },
35628
35629     // private
35630     restoreLast : function(){
35631         if(this._last){
35632             this.last = this._last;
35633         }
35634     },
35635
35636     // private
35637     acceptsNav : function(row, col, cm){
35638         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35639     },
35640
35641     // private
35642     onEditorKey : function(field, e){
35643         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35644         if(k == e.TAB){
35645             e.stopEvent();
35646             ed.completeEdit();
35647             if(e.shiftKey){
35648                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35649             }else{
35650                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35651             }
35652         }else if(k == e.ENTER && !e.ctrlKey){
35653             e.stopEvent();
35654             ed.completeEdit();
35655             if(e.shiftKey){
35656                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35657             }else{
35658                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35659             }
35660         }else if(k == e.ESC){
35661             ed.cancelEdit();
35662         }
35663         if(newCell){
35664             g.startEditing(newCell[0], newCell[1]);
35665         }
35666     }
35667 });/*
35668  * Based on:
35669  * Ext JS Library 1.1.1
35670  * Copyright(c) 2006-2007, Ext JS, LLC.
35671  *
35672  * Originally Released Under LGPL - original licence link has changed is not relivant.
35673  *
35674  * Fork - LGPL
35675  * <script type="text/javascript">
35676  */
35677 /**
35678  * @class Roo.grid.CellSelectionModel
35679  * @extends Roo.grid.AbstractSelectionModel
35680  * This class provides the basic implementation for cell selection in a grid.
35681  * @constructor
35682  * @param {Object} config The object containing the configuration of this model.
35683  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35684  */
35685 Roo.grid.CellSelectionModel = function(config){
35686     Roo.apply(this, config);
35687
35688     this.selection = null;
35689
35690     this.addEvents({
35691         /**
35692              * @event beforerowselect
35693              * Fires before a cell is selected.
35694              * @param {SelectionModel} this
35695              * @param {Number} rowIndex The selected row index
35696              * @param {Number} colIndex The selected cell index
35697              */
35698             "beforecellselect" : true,
35699         /**
35700              * @event cellselect
35701              * Fires when a cell is selected.
35702              * @param {SelectionModel} this
35703              * @param {Number} rowIndex The selected row index
35704              * @param {Number} colIndex The selected cell index
35705              */
35706             "cellselect" : true,
35707         /**
35708              * @event selectionchange
35709              * Fires when the active selection changes.
35710              * @param {SelectionModel} this
35711              * @param {Object} selection null for no selection or an object (o) with two properties
35712                 <ul>
35713                 <li>o.record: the record object for the row the selection is in</li>
35714                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35715                 </ul>
35716              */
35717             "selectionchange" : true,
35718         /**
35719              * @event tabend
35720              * Fires when the tab (or enter) was pressed on the last editable cell
35721              * You can use this to trigger add new row.
35722              * @param {SelectionModel} this
35723              */
35724             "tabend" : true,
35725          /**
35726              * @event beforeeditnext
35727              * Fires before the next editable sell is made active
35728              * You can use this to skip to another cell or fire the tabend
35729              *    if you set cell to false
35730              * @param {Object} eventdata object : { cell : [ row, col ] } 
35731              */
35732             "beforeeditnext" : true
35733     });
35734     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35735 };
35736
35737 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35738     
35739     enter_is_tab: false,
35740
35741     /** @ignore */
35742     initEvents : function(){
35743         this.grid.on("mousedown", this.handleMouseDown, this);
35744         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35745         var view = this.grid.view;
35746         view.on("refresh", this.onViewChange, this);
35747         view.on("rowupdated", this.onRowUpdated, this);
35748         view.on("beforerowremoved", this.clearSelections, this);
35749         view.on("beforerowsinserted", this.clearSelections, this);
35750         if(this.grid.isEditor){
35751             this.grid.on("beforeedit", this.beforeEdit,  this);
35752         }
35753     },
35754
35755         //private
35756     beforeEdit : function(e){
35757         this.select(e.row, e.column, false, true, e.record);
35758     },
35759
35760         //private
35761     onRowUpdated : function(v, index, r){
35762         if(this.selection && this.selection.record == r){
35763             v.onCellSelect(index, this.selection.cell[1]);
35764         }
35765     },
35766
35767         //private
35768     onViewChange : function(){
35769         this.clearSelections(true);
35770     },
35771
35772         /**
35773          * Returns the currently selected cell,.
35774          * @return {Array} The selected cell (row, column) or null if none selected.
35775          */
35776     getSelectedCell : function(){
35777         return this.selection ? this.selection.cell : null;
35778     },
35779
35780     /**
35781      * Clears all selections.
35782      * @param {Boolean} true to prevent the gridview from being notified about the change.
35783      */
35784     clearSelections : function(preventNotify){
35785         var s = this.selection;
35786         if(s){
35787             if(preventNotify !== true){
35788                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35789             }
35790             this.selection = null;
35791             this.fireEvent("selectionchange", this, null);
35792         }
35793     },
35794
35795     /**
35796      * Returns true if there is a selection.
35797      * @return {Boolean}
35798      */
35799     hasSelection : function(){
35800         return this.selection ? true : false;
35801     },
35802
35803     /** @ignore */
35804     handleMouseDown : function(e, t){
35805         var v = this.grid.getView();
35806         if(this.isLocked()){
35807             return;
35808         };
35809         var row = v.findRowIndex(t);
35810         var cell = v.findCellIndex(t);
35811         if(row !== false && cell !== false){
35812             this.select(row, cell);
35813         }
35814     },
35815
35816     /**
35817      * Selects a cell.
35818      * @param {Number} rowIndex
35819      * @param {Number} collIndex
35820      */
35821     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35822         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35823             this.clearSelections();
35824             r = r || this.grid.dataSource.getAt(rowIndex);
35825             this.selection = {
35826                 record : r,
35827                 cell : [rowIndex, colIndex]
35828             };
35829             if(!preventViewNotify){
35830                 var v = this.grid.getView();
35831                 v.onCellSelect(rowIndex, colIndex);
35832                 if(preventFocus !== true){
35833                     v.focusCell(rowIndex, colIndex);
35834                 }
35835             }
35836             this.fireEvent("cellselect", this, rowIndex, colIndex);
35837             this.fireEvent("selectionchange", this, this.selection);
35838         }
35839     },
35840
35841         //private
35842     isSelectable : function(rowIndex, colIndex, cm){
35843         return !cm.isHidden(colIndex);
35844     },
35845
35846     /** @ignore */
35847     handleKeyDown : function(e){
35848         //Roo.log('Cell Sel Model handleKeyDown');
35849         if(!e.isNavKeyPress()){
35850             return;
35851         }
35852         var g = this.grid, s = this.selection;
35853         if(!s){
35854             e.stopEvent();
35855             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35856             if(cell){
35857                 this.select(cell[0], cell[1]);
35858             }
35859             return;
35860         }
35861         var sm = this;
35862         var walk = function(row, col, step){
35863             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35864         };
35865         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35866         var newCell;
35867
35868       
35869
35870         switch(k){
35871             case e.TAB:
35872                 // handled by onEditorKey
35873                 if (g.isEditor && g.editing) {
35874                     return;
35875                 }
35876                 if(e.shiftKey) {
35877                     newCell = walk(r, c-1, -1);
35878                 } else {
35879                     newCell = walk(r, c+1, 1);
35880                 }
35881                 break;
35882             
35883             case e.DOWN:
35884                newCell = walk(r+1, c, 1);
35885                 break;
35886             
35887             case e.UP:
35888                 newCell = walk(r-1, c, -1);
35889                 break;
35890             
35891             case e.RIGHT:
35892                 newCell = walk(r, c+1, 1);
35893                 break;
35894             
35895             case e.LEFT:
35896                 newCell = walk(r, c-1, -1);
35897                 break;
35898             
35899             case e.ENTER:
35900                 
35901                 if(g.isEditor && !g.editing){
35902                    g.startEditing(r, c);
35903                    e.stopEvent();
35904                    return;
35905                 }
35906                 
35907                 
35908              break;
35909         };
35910         if(newCell){
35911             this.select(newCell[0], newCell[1]);
35912             e.stopEvent();
35913             
35914         }
35915     },
35916
35917     acceptsNav : function(row, col, cm){
35918         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35919     },
35920     /**
35921      * Selects a cell.
35922      * @param {Number} field (not used) - as it's normally used as a listener
35923      * @param {Number} e - event - fake it by using
35924      *
35925      * var e = Roo.EventObjectImpl.prototype;
35926      * e.keyCode = e.TAB
35927      *
35928      * 
35929      */
35930     onEditorKey : function(field, e){
35931         
35932         var k = e.getKey(),
35933             newCell,
35934             g = this.grid,
35935             ed = g.activeEditor,
35936             forward = false;
35937         ///Roo.log('onEditorKey' + k);
35938         
35939         
35940         if (this.enter_is_tab && k == e.ENTER) {
35941             k = e.TAB;
35942         }
35943         
35944         if(k == e.TAB){
35945             if(e.shiftKey){
35946                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35947             }else{
35948                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35949                 forward = true;
35950             }
35951             
35952             e.stopEvent();
35953             
35954         } else if(k == e.ENTER &&  !e.ctrlKey){
35955             ed.completeEdit();
35956             e.stopEvent();
35957             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35958         
35959                 } else if(k == e.ESC){
35960             ed.cancelEdit();
35961         }
35962                 
35963         if (newCell) {
35964             var ecall = { cell : newCell, forward : forward };
35965             this.fireEvent('beforeeditnext', ecall );
35966             newCell = ecall.cell;
35967                         forward = ecall.forward;
35968         }
35969                 
35970         if(newCell){
35971             //Roo.log('next cell after edit');
35972             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35973         } else if (forward) {
35974             // tabbed past last
35975             this.fireEvent.defer(100, this, ['tabend',this]);
35976         }
35977     }
35978 });/*
35979  * Based on:
35980  * Ext JS Library 1.1.1
35981  * Copyright(c) 2006-2007, Ext JS, LLC.
35982  *
35983  * Originally Released Under LGPL - original licence link has changed is not relivant.
35984  *
35985  * Fork - LGPL
35986  * <script type="text/javascript">
35987  */
35988  
35989 /**
35990  * @class Roo.grid.EditorGrid
35991  * @extends Roo.grid.Grid
35992  * Class for creating and editable grid.
35993  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35994  * The container MUST have some type of size defined for the grid to fill. The container will be 
35995  * automatically set to position relative if it isn't already.
35996  * @param {Object} dataSource The data model to bind to
35997  * @param {Object} colModel The column model with info about this grid's columns
35998  */
35999 Roo.grid.EditorGrid = function(container, config){
36000     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36001     this.getGridEl().addClass("xedit-grid");
36002
36003     if(!this.selModel){
36004         this.selModel = new Roo.grid.CellSelectionModel();
36005     }
36006
36007     this.activeEditor = null;
36008
36009         this.addEvents({
36010             /**
36011              * @event beforeedit
36012              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36013              * <ul style="padding:5px;padding-left:16px;">
36014              * <li>grid - This grid</li>
36015              * <li>record - The record being edited</li>
36016              * <li>field - The field name being edited</li>
36017              * <li>value - The value for the field being edited.</li>
36018              * <li>row - The grid row index</li>
36019              * <li>column - The grid column index</li>
36020              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36021              * </ul>
36022              * @param {Object} e An edit event (see above for description)
36023              */
36024             "beforeedit" : true,
36025             /**
36026              * @event afteredit
36027              * Fires after a cell is edited. <br />
36028              * <ul style="padding:5px;padding-left:16px;">
36029              * <li>grid - This grid</li>
36030              * <li>record - The record being edited</li>
36031              * <li>field - The field name being edited</li>
36032              * <li>value - The value being set</li>
36033              * <li>originalValue - The original value for the field, before the edit.</li>
36034              * <li>row - The grid row index</li>
36035              * <li>column - The grid column index</li>
36036              * </ul>
36037              * @param {Object} e An edit event (see above for description)
36038              */
36039             "afteredit" : true,
36040             /**
36041              * @event validateedit
36042              * Fires after a cell is edited, but before the value is set in the record. 
36043          * You can use this to modify the value being set in the field, Return false
36044              * to cancel the change. The edit event object has the following properties <br />
36045              * <ul style="padding:5px;padding-left:16px;">
36046          * <li>editor - This editor</li>
36047              * <li>grid - This grid</li>
36048              * <li>record - The record being edited</li>
36049              * <li>field - The field name being edited</li>
36050              * <li>value - The value being set</li>
36051              * <li>originalValue - The original value for the field, before the edit.</li>
36052              * <li>row - The grid row index</li>
36053              * <li>column - The grid column index</li>
36054              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36055              * </ul>
36056              * @param {Object} e An edit event (see above for description)
36057              */
36058             "validateedit" : true
36059         });
36060     this.on("bodyscroll", this.stopEditing,  this);
36061     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36062 };
36063
36064 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36065     /**
36066      * @cfg {Number} clicksToEdit
36067      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36068      */
36069     clicksToEdit: 2,
36070
36071     // private
36072     isEditor : true,
36073     // private
36074     trackMouseOver: false, // causes very odd FF errors
36075
36076     onCellDblClick : function(g, row, col){
36077         this.startEditing(row, col);
36078     },
36079
36080     onEditComplete : function(ed, value, startValue){
36081         this.editing = false;
36082         this.activeEditor = null;
36083         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36084         var r = ed.record;
36085         var field = this.colModel.getDataIndex(ed.col);
36086         var e = {
36087             grid: this,
36088             record: r,
36089             field: field,
36090             originalValue: startValue,
36091             value: value,
36092             row: ed.row,
36093             column: ed.col,
36094             cancel:false,
36095             editor: ed
36096         };
36097         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36098         cell.show();
36099           
36100         if(String(value) !== String(startValue)){
36101             
36102             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36103                 r.set(field, e.value);
36104                 // if we are dealing with a combo box..
36105                 // then we also set the 'name' colum to be the displayField
36106                 if (ed.field.displayField && ed.field.name) {
36107                     r.set(ed.field.name, ed.field.el.dom.value);
36108                 }
36109                 
36110                 delete e.cancel; //?? why!!!
36111                 this.fireEvent("afteredit", e);
36112             }
36113         } else {
36114             this.fireEvent("afteredit", e); // always fire it!
36115         }
36116         this.view.focusCell(ed.row, ed.col);
36117     },
36118
36119     /**
36120      * Starts editing the specified for the specified row/column
36121      * @param {Number} rowIndex
36122      * @param {Number} colIndex
36123      */
36124     startEditing : function(row, col){
36125         this.stopEditing();
36126         if(this.colModel.isCellEditable(col, row)){
36127             this.view.ensureVisible(row, col, true);
36128           
36129             var r = this.dataSource.getAt(row);
36130             var field = this.colModel.getDataIndex(col);
36131             var cell = Roo.get(this.view.getCell(row,col));
36132             var e = {
36133                 grid: this,
36134                 record: r,
36135                 field: field,
36136                 value: r.data[field],
36137                 row: row,
36138                 column: col,
36139                 cancel:false 
36140             };
36141             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36142                 this.editing = true;
36143                 var ed = this.colModel.getCellEditor(col, row);
36144                 
36145                 if (!ed) {
36146                     return;
36147                 }
36148                 if(!ed.rendered){
36149                     ed.render(ed.parentEl || document.body);
36150                 }
36151                 ed.field.reset();
36152                
36153                 cell.hide();
36154                 
36155                 (function(){ // complex but required for focus issues in safari, ie and opera
36156                     ed.row = row;
36157                     ed.col = col;
36158                     ed.record = r;
36159                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36160                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36161                     this.activeEditor = ed;
36162                     var v = r.data[field];
36163                     ed.startEdit(this.view.getCell(row, col), v);
36164                     // combo's with 'displayField and name set
36165                     if (ed.field.displayField && ed.field.name) {
36166                         ed.field.el.dom.value = r.data[ed.field.name];
36167                     }
36168                     
36169                     
36170                 }).defer(50, this);
36171             }
36172         }
36173     },
36174         
36175     /**
36176      * Stops any active editing
36177      */
36178     stopEditing : function(){
36179         if(this.activeEditor){
36180             this.activeEditor.completeEdit();
36181         }
36182         this.activeEditor = null;
36183     },
36184         
36185          /**
36186      * Called to get grid's drag proxy text, by default returns this.ddText.
36187      * @return {String}
36188      */
36189     getDragDropText : function(){
36190         var count = this.selModel.getSelectedCell() ? 1 : 0;
36191         return String.format(this.ddText, count, count == 1 ? '' : 's');
36192     }
36193         
36194 });/*
36195  * Based on:
36196  * Ext JS Library 1.1.1
36197  * Copyright(c) 2006-2007, Ext JS, LLC.
36198  *
36199  * Originally Released Under LGPL - original licence link has changed is not relivant.
36200  *
36201  * Fork - LGPL
36202  * <script type="text/javascript">
36203  */
36204
36205 // private - not really -- you end up using it !
36206 // This is a support class used internally by the Grid components
36207
36208 /**
36209  * @class Roo.grid.GridEditor
36210  * @extends Roo.Editor
36211  * Class for creating and editable grid elements.
36212  * @param {Object} config any settings (must include field)
36213  */
36214 Roo.grid.GridEditor = function(field, config){
36215     if (!config && field.field) {
36216         config = field;
36217         field = Roo.factory(config.field, Roo.form);
36218     }
36219     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36220     field.monitorTab = false;
36221 };
36222
36223 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36224     
36225     /**
36226      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36227      */
36228     
36229     alignment: "tl-tl",
36230     autoSize: "width",
36231     hideEl : false,
36232     cls: "x-small-editor x-grid-editor",
36233     shim:false,
36234     shadow:"frame"
36235 });/*
36236  * Based on:
36237  * Ext JS Library 1.1.1
36238  * Copyright(c) 2006-2007, Ext JS, LLC.
36239  *
36240  * Originally Released Under LGPL - original licence link has changed is not relivant.
36241  *
36242  * Fork - LGPL
36243  * <script type="text/javascript">
36244  */
36245   
36246
36247   
36248 Roo.grid.PropertyRecord = Roo.data.Record.create([
36249     {name:'name',type:'string'},  'value'
36250 ]);
36251
36252
36253 Roo.grid.PropertyStore = function(grid, source){
36254     this.grid = grid;
36255     this.store = new Roo.data.Store({
36256         recordType : Roo.grid.PropertyRecord
36257     });
36258     this.store.on('update', this.onUpdate,  this);
36259     if(source){
36260         this.setSource(source);
36261     }
36262     Roo.grid.PropertyStore.superclass.constructor.call(this);
36263 };
36264
36265
36266
36267 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36268     setSource : function(o){
36269         this.source = o;
36270         this.store.removeAll();
36271         var data = [];
36272         for(var k in o){
36273             if(this.isEditableValue(o[k])){
36274                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36275             }
36276         }
36277         this.store.loadRecords({records: data}, {}, true);
36278     },
36279
36280     onUpdate : function(ds, record, type){
36281         if(type == Roo.data.Record.EDIT){
36282             var v = record.data['value'];
36283             var oldValue = record.modified['value'];
36284             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36285                 this.source[record.id] = v;
36286                 record.commit();
36287                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36288             }else{
36289                 record.reject();
36290             }
36291         }
36292     },
36293
36294     getProperty : function(row){
36295        return this.store.getAt(row);
36296     },
36297
36298     isEditableValue: function(val){
36299         if(val && val instanceof Date){
36300             return true;
36301         }else if(typeof val == 'object' || typeof val == 'function'){
36302             return false;
36303         }
36304         return true;
36305     },
36306
36307     setValue : function(prop, value){
36308         this.source[prop] = value;
36309         this.store.getById(prop).set('value', value);
36310     },
36311
36312     getSource : function(){
36313         return this.source;
36314     }
36315 });
36316
36317 Roo.grid.PropertyColumnModel = function(grid, store){
36318     this.grid = grid;
36319     var g = Roo.grid;
36320     g.PropertyColumnModel.superclass.constructor.call(this, [
36321         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36322         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36323     ]);
36324     this.store = store;
36325     this.bselect = Roo.DomHelper.append(document.body, {
36326         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36327             {tag: 'option', value: 'true', html: 'true'},
36328             {tag: 'option', value: 'false', html: 'false'}
36329         ]
36330     });
36331     Roo.id(this.bselect);
36332     var f = Roo.form;
36333     this.editors = {
36334         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36335         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36336         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36337         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36338         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36339     };
36340     this.renderCellDelegate = this.renderCell.createDelegate(this);
36341     this.renderPropDelegate = this.renderProp.createDelegate(this);
36342 };
36343
36344 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36345     
36346     
36347     nameText : 'Name',
36348     valueText : 'Value',
36349     
36350     dateFormat : 'm/j/Y',
36351     
36352     
36353     renderDate : function(dateVal){
36354         return dateVal.dateFormat(this.dateFormat);
36355     },
36356
36357     renderBool : function(bVal){
36358         return bVal ? 'true' : 'false';
36359     },
36360
36361     isCellEditable : function(colIndex, rowIndex){
36362         return colIndex == 1;
36363     },
36364
36365     getRenderer : function(col){
36366         return col == 1 ?
36367             this.renderCellDelegate : this.renderPropDelegate;
36368     },
36369
36370     renderProp : function(v){
36371         return this.getPropertyName(v);
36372     },
36373
36374     renderCell : function(val){
36375         var rv = val;
36376         if(val instanceof Date){
36377             rv = this.renderDate(val);
36378         }else if(typeof val == 'boolean'){
36379             rv = this.renderBool(val);
36380         }
36381         return Roo.util.Format.htmlEncode(rv);
36382     },
36383
36384     getPropertyName : function(name){
36385         var pn = this.grid.propertyNames;
36386         return pn && pn[name] ? pn[name] : name;
36387     },
36388
36389     getCellEditor : function(colIndex, rowIndex){
36390         var p = this.store.getProperty(rowIndex);
36391         var n = p.data['name'], val = p.data['value'];
36392         
36393         if(typeof(this.grid.customEditors[n]) == 'string'){
36394             return this.editors[this.grid.customEditors[n]];
36395         }
36396         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36397             return this.grid.customEditors[n];
36398         }
36399         if(val instanceof Date){
36400             return this.editors['date'];
36401         }else if(typeof val == 'number'){
36402             return this.editors['number'];
36403         }else if(typeof val == 'boolean'){
36404             return this.editors['boolean'];
36405         }else{
36406             return this.editors['string'];
36407         }
36408     }
36409 });
36410
36411 /**
36412  * @class Roo.grid.PropertyGrid
36413  * @extends Roo.grid.EditorGrid
36414  * This class represents the  interface of a component based property grid control.
36415  * <br><br>Usage:<pre><code>
36416  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36417       
36418  });
36419  // set any options
36420  grid.render();
36421  * </code></pre>
36422   
36423  * @constructor
36424  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36425  * The container MUST have some type of size defined for the grid to fill. The container will be
36426  * automatically set to position relative if it isn't already.
36427  * @param {Object} config A config object that sets properties on this grid.
36428  */
36429 Roo.grid.PropertyGrid = function(container, config){
36430     config = config || {};
36431     var store = new Roo.grid.PropertyStore(this);
36432     this.store = store;
36433     var cm = new Roo.grid.PropertyColumnModel(this, store);
36434     store.store.sort('name', 'ASC');
36435     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36436         ds: store.store,
36437         cm: cm,
36438         enableColLock:false,
36439         enableColumnMove:false,
36440         stripeRows:false,
36441         trackMouseOver: false,
36442         clicksToEdit:1
36443     }, config));
36444     this.getGridEl().addClass('x-props-grid');
36445     this.lastEditRow = null;
36446     this.on('columnresize', this.onColumnResize, this);
36447     this.addEvents({
36448          /**
36449              * @event beforepropertychange
36450              * Fires before a property changes (return false to stop?)
36451              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36452              * @param {String} id Record Id
36453              * @param {String} newval New Value
36454          * @param {String} oldval Old Value
36455              */
36456         "beforepropertychange": true,
36457         /**
36458              * @event propertychange
36459              * Fires after a property changes
36460              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36461              * @param {String} id Record Id
36462              * @param {String} newval New Value
36463          * @param {String} oldval Old Value
36464              */
36465         "propertychange": true
36466     });
36467     this.customEditors = this.customEditors || {};
36468 };
36469 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36470     
36471      /**
36472      * @cfg {Object} customEditors map of colnames=> custom editors.
36473      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36474      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36475      * false disables editing of the field.
36476          */
36477     
36478       /**
36479      * @cfg {Object} propertyNames map of property Names to their displayed value
36480          */
36481     
36482     render : function(){
36483         Roo.grid.PropertyGrid.superclass.render.call(this);
36484         this.autoSize.defer(100, this);
36485     },
36486
36487     autoSize : function(){
36488         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36489         if(this.view){
36490             this.view.fitColumns();
36491         }
36492     },
36493
36494     onColumnResize : function(){
36495         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36496         this.autoSize();
36497     },
36498     /**
36499      * Sets the data for the Grid
36500      * accepts a Key => Value object of all the elements avaiable.
36501      * @param {Object} data  to appear in grid.
36502      */
36503     setSource : function(source){
36504         this.store.setSource(source);
36505         //this.autoSize();
36506     },
36507     /**
36508      * Gets all the data from the grid.
36509      * @return {Object} data  data stored in grid
36510      */
36511     getSource : function(){
36512         return this.store.getSource();
36513     }
36514 });/*
36515   
36516  * Licence LGPL
36517  
36518  */
36519  
36520 /**
36521  * @class Roo.grid.Calendar
36522  * @extends Roo.util.Grid
36523  * This class extends the Grid to provide a calendar widget
36524  * <br><br>Usage:<pre><code>
36525  var grid = new Roo.grid.Calendar("my-container-id", {
36526      ds: myDataStore,
36527      cm: myColModel,
36528      selModel: mySelectionModel,
36529      autoSizeColumns: true,
36530      monitorWindowResize: false,
36531      trackMouseOver: true
36532      eventstore : real data store..
36533  });
36534  // set any options
36535  grid.render();
36536   
36537   * @constructor
36538  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36539  * The container MUST have some type of size defined for the grid to fill. The container will be
36540  * automatically set to position relative if it isn't already.
36541  * @param {Object} config A config object that sets properties on this grid.
36542  */
36543 Roo.grid.Calendar = function(container, config){
36544         // initialize the container
36545         this.container = Roo.get(container);
36546         this.container.update("");
36547         this.container.setStyle("overflow", "hidden");
36548     this.container.addClass('x-grid-container');
36549
36550     this.id = this.container.id;
36551
36552     Roo.apply(this, config);
36553     // check and correct shorthanded configs
36554     
36555     var rows = [];
36556     var d =1;
36557     for (var r = 0;r < 6;r++) {
36558         
36559         rows[r]=[];
36560         for (var c =0;c < 7;c++) {
36561             rows[r][c]= '';
36562         }
36563     }
36564     if (this.eventStore) {
36565         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36566         this.eventStore.on('load',this.onLoad, this);
36567         this.eventStore.on('beforeload',this.clearEvents, this);
36568          
36569     }
36570     
36571     this.dataSource = new Roo.data.Store({
36572             proxy: new Roo.data.MemoryProxy(rows),
36573             reader: new Roo.data.ArrayReader({}, [
36574                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36575     });
36576
36577     this.dataSource.load();
36578     this.ds = this.dataSource;
36579     this.ds.xmodule = this.xmodule || false;
36580     
36581     
36582     var cellRender = function(v,x,r)
36583     {
36584         return String.format(
36585             '<div class="fc-day  fc-widget-content"><div>' +
36586                 '<div class="fc-event-container"></div>' +
36587                 '<div class="fc-day-number">{0}</div>'+
36588                 
36589                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36590             '</div></div>', v);
36591     
36592     }
36593     
36594     
36595     this.colModel = new Roo.grid.ColumnModel( [
36596         {
36597             xtype: 'ColumnModel',
36598             xns: Roo.grid,
36599             dataIndex : 'weekday0',
36600             header : 'Sunday',
36601             renderer : cellRender
36602         },
36603         {
36604             xtype: 'ColumnModel',
36605             xns: Roo.grid,
36606             dataIndex : 'weekday1',
36607             header : 'Monday',
36608             renderer : cellRender
36609         },
36610         {
36611             xtype: 'ColumnModel',
36612             xns: Roo.grid,
36613             dataIndex : 'weekday2',
36614             header : 'Tuesday',
36615             renderer : cellRender
36616         },
36617         {
36618             xtype: 'ColumnModel',
36619             xns: Roo.grid,
36620             dataIndex : 'weekday3',
36621             header : 'Wednesday',
36622             renderer : cellRender
36623         },
36624         {
36625             xtype: 'ColumnModel',
36626             xns: Roo.grid,
36627             dataIndex : 'weekday4',
36628             header : 'Thursday',
36629             renderer : cellRender
36630         },
36631         {
36632             xtype: 'ColumnModel',
36633             xns: Roo.grid,
36634             dataIndex : 'weekday5',
36635             header : 'Friday',
36636             renderer : cellRender
36637         },
36638         {
36639             xtype: 'ColumnModel',
36640             xns: Roo.grid,
36641             dataIndex : 'weekday6',
36642             header : 'Saturday',
36643             renderer : cellRender
36644         }
36645     ]);
36646     this.cm = this.colModel;
36647     this.cm.xmodule = this.xmodule || false;
36648  
36649         
36650           
36651     //this.selModel = new Roo.grid.CellSelectionModel();
36652     //this.sm = this.selModel;
36653     //this.selModel.init(this);
36654     
36655     
36656     if(this.width){
36657         this.container.setWidth(this.width);
36658     }
36659
36660     if(this.height){
36661         this.container.setHeight(this.height);
36662     }
36663     /** @private */
36664         this.addEvents({
36665         // raw events
36666         /**
36667          * @event click
36668          * The raw click event for the entire grid.
36669          * @param {Roo.EventObject} e
36670          */
36671         "click" : true,
36672         /**
36673          * @event dblclick
36674          * The raw dblclick event for the entire grid.
36675          * @param {Roo.EventObject} e
36676          */
36677         "dblclick" : true,
36678         /**
36679          * @event contextmenu
36680          * The raw contextmenu event for the entire grid.
36681          * @param {Roo.EventObject} e
36682          */
36683         "contextmenu" : true,
36684         /**
36685          * @event mousedown
36686          * The raw mousedown event for the entire grid.
36687          * @param {Roo.EventObject} e
36688          */
36689         "mousedown" : true,
36690         /**
36691          * @event mouseup
36692          * The raw mouseup event for the entire grid.
36693          * @param {Roo.EventObject} e
36694          */
36695         "mouseup" : true,
36696         /**
36697          * @event mouseover
36698          * The raw mouseover event for the entire grid.
36699          * @param {Roo.EventObject} e
36700          */
36701         "mouseover" : true,
36702         /**
36703          * @event mouseout
36704          * The raw mouseout event for the entire grid.
36705          * @param {Roo.EventObject} e
36706          */
36707         "mouseout" : true,
36708         /**
36709          * @event keypress
36710          * The raw keypress event for the entire grid.
36711          * @param {Roo.EventObject} e
36712          */
36713         "keypress" : true,
36714         /**
36715          * @event keydown
36716          * The raw keydown event for the entire grid.
36717          * @param {Roo.EventObject} e
36718          */
36719         "keydown" : true,
36720
36721         // custom events
36722
36723         /**
36724          * @event cellclick
36725          * Fires when a cell is clicked
36726          * @param {Grid} this
36727          * @param {Number} rowIndex
36728          * @param {Number} columnIndex
36729          * @param {Roo.EventObject} e
36730          */
36731         "cellclick" : true,
36732         /**
36733          * @event celldblclick
36734          * Fires when a cell is double clicked
36735          * @param {Grid} this
36736          * @param {Number} rowIndex
36737          * @param {Number} columnIndex
36738          * @param {Roo.EventObject} e
36739          */
36740         "celldblclick" : true,
36741         /**
36742          * @event rowclick
36743          * Fires when a row is clicked
36744          * @param {Grid} this
36745          * @param {Number} rowIndex
36746          * @param {Roo.EventObject} e
36747          */
36748         "rowclick" : true,
36749         /**
36750          * @event rowdblclick
36751          * Fires when a row is double clicked
36752          * @param {Grid} this
36753          * @param {Number} rowIndex
36754          * @param {Roo.EventObject} e
36755          */
36756         "rowdblclick" : true,
36757         /**
36758          * @event headerclick
36759          * Fires when a header is clicked
36760          * @param {Grid} this
36761          * @param {Number} columnIndex
36762          * @param {Roo.EventObject} e
36763          */
36764         "headerclick" : true,
36765         /**
36766          * @event headerdblclick
36767          * Fires when a header cell is double clicked
36768          * @param {Grid} this
36769          * @param {Number} columnIndex
36770          * @param {Roo.EventObject} e
36771          */
36772         "headerdblclick" : true,
36773         /**
36774          * @event rowcontextmenu
36775          * Fires when a row is right clicked
36776          * @param {Grid} this
36777          * @param {Number} rowIndex
36778          * @param {Roo.EventObject} e
36779          */
36780         "rowcontextmenu" : true,
36781         /**
36782          * @event cellcontextmenu
36783          * Fires when a cell is right clicked
36784          * @param {Grid} this
36785          * @param {Number} rowIndex
36786          * @param {Number} cellIndex
36787          * @param {Roo.EventObject} e
36788          */
36789          "cellcontextmenu" : true,
36790         /**
36791          * @event headercontextmenu
36792          * Fires when a header is right clicked
36793          * @param {Grid} this
36794          * @param {Number} columnIndex
36795          * @param {Roo.EventObject} e
36796          */
36797         "headercontextmenu" : true,
36798         /**
36799          * @event bodyscroll
36800          * Fires when the body element is scrolled
36801          * @param {Number} scrollLeft
36802          * @param {Number} scrollTop
36803          */
36804         "bodyscroll" : true,
36805         /**
36806          * @event columnresize
36807          * Fires when the user resizes a column
36808          * @param {Number} columnIndex
36809          * @param {Number} newSize
36810          */
36811         "columnresize" : true,
36812         /**
36813          * @event columnmove
36814          * Fires when the user moves a column
36815          * @param {Number} oldIndex
36816          * @param {Number} newIndex
36817          */
36818         "columnmove" : true,
36819         /**
36820          * @event startdrag
36821          * Fires when row(s) start being dragged
36822          * @param {Grid} this
36823          * @param {Roo.GridDD} dd The drag drop object
36824          * @param {event} e The raw browser event
36825          */
36826         "startdrag" : true,
36827         /**
36828          * @event enddrag
36829          * Fires when a drag operation is complete
36830          * @param {Grid} this
36831          * @param {Roo.GridDD} dd The drag drop object
36832          * @param {event} e The raw browser event
36833          */
36834         "enddrag" : true,
36835         /**
36836          * @event dragdrop
36837          * Fires when dragged row(s) are dropped on a valid DD target
36838          * @param {Grid} this
36839          * @param {Roo.GridDD} dd The drag drop object
36840          * @param {String} targetId The target drag drop object
36841          * @param {event} e The raw browser event
36842          */
36843         "dragdrop" : true,
36844         /**
36845          * @event dragover
36846          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36847          * @param {Grid} this
36848          * @param {Roo.GridDD} dd The drag drop object
36849          * @param {String} targetId The target drag drop object
36850          * @param {event} e The raw browser event
36851          */
36852         "dragover" : true,
36853         /**
36854          * @event dragenter
36855          *  Fires when the dragged row(s) first cross another DD target while being dragged
36856          * @param {Grid} this
36857          * @param {Roo.GridDD} dd The drag drop object
36858          * @param {String} targetId The target drag drop object
36859          * @param {event} e The raw browser event
36860          */
36861         "dragenter" : true,
36862         /**
36863          * @event dragout
36864          * Fires when the dragged row(s) leave another DD target while being dragged
36865          * @param {Grid} this
36866          * @param {Roo.GridDD} dd The drag drop object
36867          * @param {String} targetId The target drag drop object
36868          * @param {event} e The raw browser event
36869          */
36870         "dragout" : true,
36871         /**
36872          * @event rowclass
36873          * Fires when a row is rendered, so you can change add a style to it.
36874          * @param {GridView} gridview   The grid view
36875          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36876          */
36877         'rowclass' : true,
36878
36879         /**
36880          * @event render
36881          * Fires when the grid is rendered
36882          * @param {Grid} grid
36883          */
36884         'render' : true,
36885             /**
36886              * @event select
36887              * Fires when a date is selected
36888              * @param {DatePicker} this
36889              * @param {Date} date The selected date
36890              */
36891         'select': true,
36892         /**
36893              * @event monthchange
36894              * Fires when the displayed month changes 
36895              * @param {DatePicker} this
36896              * @param {Date} date The selected month
36897              */
36898         'monthchange': true,
36899         /**
36900              * @event evententer
36901              * Fires when mouse over an event
36902              * @param {Calendar} this
36903              * @param {event} Event
36904              */
36905         'evententer': true,
36906         /**
36907              * @event eventleave
36908              * Fires when the mouse leaves an
36909              * @param {Calendar} this
36910              * @param {event}
36911              */
36912         'eventleave': true,
36913         /**
36914              * @event eventclick
36915              * Fires when the mouse click an
36916              * @param {Calendar} this
36917              * @param {event}
36918              */
36919         'eventclick': true,
36920         /**
36921              * @event eventrender
36922              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36923              * @param {Calendar} this
36924              * @param {data} data to be modified
36925              */
36926         'eventrender': true
36927         
36928     });
36929
36930     Roo.grid.Grid.superclass.constructor.call(this);
36931     this.on('render', function() {
36932         this.view.el.addClass('x-grid-cal'); 
36933         
36934         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36935
36936     },this);
36937     
36938     if (!Roo.grid.Calendar.style) {
36939         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36940             
36941             
36942             '.x-grid-cal .x-grid-col' :  {
36943                 height: 'auto !important',
36944                 'vertical-align': 'top'
36945             },
36946             '.x-grid-cal  .fc-event-hori' : {
36947                 height: '14px'
36948             }
36949              
36950             
36951         }, Roo.id());
36952     }
36953
36954     
36955     
36956 };
36957 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36958     /**
36959      * @cfg {Store} eventStore The store that loads events.
36960      */
36961     eventStore : 25,
36962
36963      
36964     activeDate : false,
36965     startDay : 0,
36966     autoWidth : true,
36967     monitorWindowResize : false,
36968
36969     
36970     resizeColumns : function() {
36971         var col = (this.view.el.getWidth() / 7) - 3;
36972         // loop through cols, and setWidth
36973         for(var i =0 ; i < 7 ; i++){
36974             this.cm.setColumnWidth(i, col);
36975         }
36976     },
36977      setDate :function(date) {
36978         
36979         Roo.log('setDate?');
36980         
36981         this.resizeColumns();
36982         var vd = this.activeDate;
36983         this.activeDate = date;
36984 //        if(vd && this.el){
36985 //            var t = date.getTime();
36986 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36987 //                Roo.log('using add remove');
36988 //                
36989 //                this.fireEvent('monthchange', this, date);
36990 //                
36991 //                this.cells.removeClass("fc-state-highlight");
36992 //                this.cells.each(function(c){
36993 //                   if(c.dateValue == t){
36994 //                       c.addClass("fc-state-highlight");
36995 //                       setTimeout(function(){
36996 //                            try{c.dom.firstChild.focus();}catch(e){}
36997 //                       }, 50);
36998 //                       return false;
36999 //                   }
37000 //                   return true;
37001 //                });
37002 //                return;
37003 //            }
37004 //        }
37005         
37006         var days = date.getDaysInMonth();
37007         
37008         var firstOfMonth = date.getFirstDateOfMonth();
37009         var startingPos = firstOfMonth.getDay()-this.startDay;
37010         
37011         if(startingPos < this.startDay){
37012             startingPos += 7;
37013         }
37014         
37015         var pm = date.add(Date.MONTH, -1);
37016         var prevStart = pm.getDaysInMonth()-startingPos;
37017 //        
37018         
37019         
37020         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37021         
37022         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37023         //this.cells.addClassOnOver('fc-state-hover');
37024         
37025         var cells = this.cells.elements;
37026         var textEls = this.textNodes;
37027         
37028         //Roo.each(cells, function(cell){
37029         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37030         //});
37031         
37032         days += startingPos;
37033
37034         // convert everything to numbers so it's fast
37035         var day = 86400000;
37036         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37037         //Roo.log(d);
37038         //Roo.log(pm);
37039         //Roo.log(prevStart);
37040         
37041         var today = new Date().clearTime().getTime();
37042         var sel = date.clearTime().getTime();
37043         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37044         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37045         var ddMatch = this.disabledDatesRE;
37046         var ddText = this.disabledDatesText;
37047         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37048         var ddaysText = this.disabledDaysText;
37049         var format = this.format;
37050         
37051         var setCellClass = function(cal, cell){
37052             
37053             //Roo.log('set Cell Class');
37054             cell.title = "";
37055             var t = d.getTime();
37056             
37057             //Roo.log(d);
37058             
37059             
37060             cell.dateValue = t;
37061             if(t == today){
37062                 cell.className += " fc-today";
37063                 cell.className += " fc-state-highlight";
37064                 cell.title = cal.todayText;
37065             }
37066             if(t == sel){
37067                 // disable highlight in other month..
37068                 cell.className += " fc-state-highlight";
37069                 
37070             }
37071             // disabling
37072             if(t < min) {
37073                 //cell.className = " fc-state-disabled";
37074                 cell.title = cal.minText;
37075                 return;
37076             }
37077             if(t > max) {
37078                 //cell.className = " fc-state-disabled";
37079                 cell.title = cal.maxText;
37080                 return;
37081             }
37082             if(ddays){
37083                 if(ddays.indexOf(d.getDay()) != -1){
37084                     // cell.title = ddaysText;
37085                    // cell.className = " fc-state-disabled";
37086                 }
37087             }
37088             if(ddMatch && format){
37089                 var fvalue = d.dateFormat(format);
37090                 if(ddMatch.test(fvalue)){
37091                     cell.title = ddText.replace("%0", fvalue);
37092                    cell.className = " fc-state-disabled";
37093                 }
37094             }
37095             
37096             if (!cell.initialClassName) {
37097                 cell.initialClassName = cell.dom.className;
37098             }
37099             
37100             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37101         };
37102
37103         var i = 0;
37104         
37105         for(; i < startingPos; i++) {
37106             cells[i].dayName =  (++prevStart);
37107             Roo.log(textEls[i]);
37108             d.setDate(d.getDate()+1);
37109             
37110             //cells[i].className = "fc-past fc-other-month";
37111             setCellClass(this, cells[i]);
37112         }
37113         
37114         var intDay = 0;
37115         
37116         for(; i < days; i++){
37117             intDay = i - startingPos + 1;
37118             cells[i].dayName =  (intDay);
37119             d.setDate(d.getDate()+1);
37120             
37121             cells[i].className = ''; // "x-date-active";
37122             setCellClass(this, cells[i]);
37123         }
37124         var extraDays = 0;
37125         
37126         for(; i < 42; i++) {
37127             //textEls[i].innerHTML = (++extraDays);
37128             
37129             d.setDate(d.getDate()+1);
37130             cells[i].dayName = (++extraDays);
37131             cells[i].className = "fc-future fc-other-month";
37132             setCellClass(this, cells[i]);
37133         }
37134         
37135         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37136         
37137         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37138         
37139         // this will cause all the cells to mis
37140         var rows= [];
37141         var i =0;
37142         for (var r = 0;r < 6;r++) {
37143             for (var c =0;c < 7;c++) {
37144                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37145             }    
37146         }
37147         
37148         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37149         for(i=0;i<cells.length;i++) {
37150             
37151             this.cells.elements[i].dayName = cells[i].dayName ;
37152             this.cells.elements[i].className = cells[i].className;
37153             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37154             this.cells.elements[i].title = cells[i].title ;
37155             this.cells.elements[i].dateValue = cells[i].dateValue ;
37156         }
37157         
37158         
37159         
37160         
37161         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37162         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37163         
37164         ////if(totalRows != 6){
37165             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37166            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37167        // }
37168         
37169         this.fireEvent('monthchange', this, date);
37170         
37171         
37172     },
37173  /**
37174      * Returns the grid's SelectionModel.
37175      * @return {SelectionModel}
37176      */
37177     getSelectionModel : function(){
37178         if(!this.selModel){
37179             this.selModel = new Roo.grid.CellSelectionModel();
37180         }
37181         return this.selModel;
37182     },
37183
37184     load: function() {
37185         this.eventStore.load()
37186         
37187         
37188         
37189     },
37190     
37191     findCell : function(dt) {
37192         dt = dt.clearTime().getTime();
37193         var ret = false;
37194         this.cells.each(function(c){
37195             //Roo.log("check " +c.dateValue + '?=' + dt);
37196             if(c.dateValue == dt){
37197                 ret = c;
37198                 return false;
37199             }
37200             return true;
37201         });
37202         
37203         return ret;
37204     },
37205     
37206     findCells : function(rec) {
37207         var s = rec.data.start_dt.clone().clearTime().getTime();
37208        // Roo.log(s);
37209         var e= rec.data.end_dt.clone().clearTime().getTime();
37210        // Roo.log(e);
37211         var ret = [];
37212         this.cells.each(function(c){
37213              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37214             
37215             if(c.dateValue > e){
37216                 return ;
37217             }
37218             if(c.dateValue < s){
37219                 return ;
37220             }
37221             ret.push(c);
37222         });
37223         
37224         return ret;    
37225     },
37226     
37227     findBestRow: function(cells)
37228     {
37229         var ret = 0;
37230         
37231         for (var i =0 ; i < cells.length;i++) {
37232             ret  = Math.max(cells[i].rows || 0,ret);
37233         }
37234         return ret;
37235         
37236     },
37237     
37238     
37239     addItem : function(rec)
37240     {
37241         // look for vertical location slot in
37242         var cells = this.findCells(rec);
37243         
37244         rec.row = this.findBestRow(cells);
37245         
37246         // work out the location.
37247         
37248         var crow = false;
37249         var rows = [];
37250         for(var i =0; i < cells.length; i++) {
37251             if (!crow) {
37252                 crow = {
37253                     start : cells[i],
37254                     end :  cells[i]
37255                 };
37256                 continue;
37257             }
37258             if (crow.start.getY() == cells[i].getY()) {
37259                 // on same row.
37260                 crow.end = cells[i];
37261                 continue;
37262             }
37263             // different row.
37264             rows.push(crow);
37265             crow = {
37266                 start: cells[i],
37267                 end : cells[i]
37268             };
37269             
37270         }
37271         
37272         rows.push(crow);
37273         rec.els = [];
37274         rec.rows = rows;
37275         rec.cells = cells;
37276         for (var i = 0; i < cells.length;i++) {
37277             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37278             
37279         }
37280         
37281         
37282     },
37283     
37284     clearEvents: function() {
37285         
37286         if (!this.eventStore.getCount()) {
37287             return;
37288         }
37289         // reset number of rows in cells.
37290         Roo.each(this.cells.elements, function(c){
37291             c.rows = 0;
37292         });
37293         
37294         this.eventStore.each(function(e) {
37295             this.clearEvent(e);
37296         },this);
37297         
37298     },
37299     
37300     clearEvent : function(ev)
37301     {
37302         if (ev.els) {
37303             Roo.each(ev.els, function(el) {
37304                 el.un('mouseenter' ,this.onEventEnter, this);
37305                 el.un('mouseleave' ,this.onEventLeave, this);
37306                 el.remove();
37307             },this);
37308             ev.els = [];
37309         }
37310     },
37311     
37312     
37313     renderEvent : function(ev,ctr) {
37314         if (!ctr) {
37315              ctr = this.view.el.select('.fc-event-container',true).first();
37316         }
37317         
37318          
37319         this.clearEvent(ev);
37320             //code
37321        
37322         
37323         
37324         ev.els = [];
37325         var cells = ev.cells;
37326         var rows = ev.rows;
37327         this.fireEvent('eventrender', this, ev);
37328         
37329         for(var i =0; i < rows.length; i++) {
37330             
37331             cls = '';
37332             if (i == 0) {
37333                 cls += ' fc-event-start';
37334             }
37335             if ((i+1) == rows.length) {
37336                 cls += ' fc-event-end';
37337             }
37338             
37339             //Roo.log(ev.data);
37340             // how many rows should it span..
37341             var cg = this.eventTmpl.append(ctr,Roo.apply({
37342                 fccls : cls
37343                 
37344             }, ev.data) , true);
37345             
37346             
37347             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37348             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37349             cg.on('click', this.onEventClick, this, ev);
37350             
37351             ev.els.push(cg);
37352             
37353             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37354             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37355             //Roo.log(cg);
37356              
37357             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37358             cg.setWidth(ebox.right - sbox.x -2);
37359         }
37360     },
37361     
37362     renderEvents: function()
37363     {   
37364         // first make sure there is enough space..
37365         
37366         if (!this.eventTmpl) {
37367             this.eventTmpl = new Roo.Template(
37368                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37369                     '<div class="fc-event-inner">' +
37370                         '<span class="fc-event-time">{time}</span>' +
37371                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37372                     '</div>' +
37373                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37374                 '</div>'
37375             );
37376                 
37377         }
37378                
37379         
37380         
37381         this.cells.each(function(c) {
37382             //Roo.log(c.select('.fc-day-content div',true).first());
37383             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37384         });
37385         
37386         var ctr = this.view.el.select('.fc-event-container',true).first();
37387         
37388         var cls;
37389         this.eventStore.each(function(ev){
37390             
37391             this.renderEvent(ev);
37392              
37393              
37394         }, this);
37395         this.view.layout();
37396         
37397     },
37398     
37399     onEventEnter: function (e, el,event,d) {
37400         this.fireEvent('evententer', this, el, event);
37401     },
37402     
37403     onEventLeave: function (e, el,event,d) {
37404         this.fireEvent('eventleave', this, el, event);
37405     },
37406     
37407     onEventClick: function (e, el,event,d) {
37408         this.fireEvent('eventclick', this, el, event);
37409     },
37410     
37411     onMonthChange: function () {
37412         this.store.load();
37413     },
37414     
37415     onLoad: function () {
37416         
37417         //Roo.log('calendar onload');
37418 //         
37419         if(this.eventStore.getCount() > 0){
37420             
37421            
37422             
37423             this.eventStore.each(function(d){
37424                 
37425                 
37426                 // FIXME..
37427                 var add =   d.data;
37428                 if (typeof(add.end_dt) == 'undefined')  {
37429                     Roo.log("Missing End time in calendar data: ");
37430                     Roo.log(d);
37431                     return;
37432                 }
37433                 if (typeof(add.start_dt) == 'undefined')  {
37434                     Roo.log("Missing Start time in calendar data: ");
37435                     Roo.log(d);
37436                     return;
37437                 }
37438                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37439                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37440                 add.id = add.id || d.id;
37441                 add.title = add.title || '??';
37442                 
37443                 this.addItem(d);
37444                 
37445              
37446             },this);
37447         }
37448         
37449         this.renderEvents();
37450     }
37451     
37452
37453 });
37454 /*
37455  grid : {
37456                 xtype: 'Grid',
37457                 xns: Roo.grid,
37458                 listeners : {
37459                     render : function ()
37460                     {
37461                         _this.grid = this;
37462                         
37463                         if (!this.view.el.hasClass('course-timesheet')) {
37464                             this.view.el.addClass('course-timesheet');
37465                         }
37466                         if (this.tsStyle) {
37467                             this.ds.load({});
37468                             return; 
37469                         }
37470                         Roo.log('width');
37471                         Roo.log(_this.grid.view.el.getWidth());
37472                         
37473                         
37474                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37475                             '.course-timesheet .x-grid-row' : {
37476                                 height: '80px'
37477                             },
37478                             '.x-grid-row td' : {
37479                                 'vertical-align' : 0
37480                             },
37481                             '.course-edit-link' : {
37482                                 'color' : 'blue',
37483                                 'text-overflow' : 'ellipsis',
37484                                 'overflow' : 'hidden',
37485                                 'white-space' : 'nowrap',
37486                                 'cursor' : 'pointer'
37487                             },
37488                             '.sub-link' : {
37489                                 'color' : 'green'
37490                             },
37491                             '.de-act-sup-link' : {
37492                                 'color' : 'purple',
37493                                 'text-decoration' : 'line-through'
37494                             },
37495                             '.de-act-link' : {
37496                                 'color' : 'red',
37497                                 'text-decoration' : 'line-through'
37498                             },
37499                             '.course-timesheet .course-highlight' : {
37500                                 'border-top-style': 'dashed !important',
37501                                 'border-bottom-bottom': 'dashed !important'
37502                             },
37503                             '.course-timesheet .course-item' : {
37504                                 'font-family'   : 'tahoma, arial, helvetica',
37505                                 'font-size'     : '11px',
37506                                 'overflow'      : 'hidden',
37507                                 'padding-left'  : '10px',
37508                                 'padding-right' : '10px',
37509                                 'padding-top' : '10px' 
37510                             }
37511                             
37512                         }, Roo.id());
37513                                 this.ds.load({});
37514                     }
37515                 },
37516                 autoWidth : true,
37517                 monitorWindowResize : false,
37518                 cellrenderer : function(v,x,r)
37519                 {
37520                     return v;
37521                 },
37522                 sm : {
37523                     xtype: 'CellSelectionModel',
37524                     xns: Roo.grid
37525                 },
37526                 dataSource : {
37527                     xtype: 'Store',
37528                     xns: Roo.data,
37529                     listeners : {
37530                         beforeload : function (_self, options)
37531                         {
37532                             options.params = options.params || {};
37533                             options.params._month = _this.monthField.getValue();
37534                             options.params.limit = 9999;
37535                             options.params['sort'] = 'when_dt';    
37536                             options.params['dir'] = 'ASC';    
37537                             this.proxy.loadResponse = this.loadResponse;
37538                             Roo.log("load?");
37539                             //this.addColumns();
37540                         },
37541                         load : function (_self, records, options)
37542                         {
37543                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37544                                 // if you click on the translation.. you can edit it...
37545                                 var el = Roo.get(this);
37546                                 var id = el.dom.getAttribute('data-id');
37547                                 var d = el.dom.getAttribute('data-date');
37548                                 var t = el.dom.getAttribute('data-time');
37549                                 //var id = this.child('span').dom.textContent;
37550                                 
37551                                 //Roo.log(this);
37552                                 Pman.Dialog.CourseCalendar.show({
37553                                     id : id,
37554                                     when_d : d,
37555                                     when_t : t,
37556                                     productitem_active : id ? 1 : 0
37557                                 }, function() {
37558                                     _this.grid.ds.load({});
37559                                 });
37560                            
37561                            });
37562                            
37563                            _this.panel.fireEvent('resize', [ '', '' ]);
37564                         }
37565                     },
37566                     loadResponse : function(o, success, response){
37567                             // this is overridden on before load..
37568                             
37569                             Roo.log("our code?");       
37570                             //Roo.log(success);
37571                             //Roo.log(response)
37572                             delete this.activeRequest;
37573                             if(!success){
37574                                 this.fireEvent("loadexception", this, o, response);
37575                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37576                                 return;
37577                             }
37578                             var result;
37579                             try {
37580                                 result = o.reader.read(response);
37581                             }catch(e){
37582                                 Roo.log("load exception?");
37583                                 this.fireEvent("loadexception", this, o, response, e);
37584                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37585                                 return;
37586                             }
37587                             Roo.log("ready...");        
37588                             // loop through result.records;
37589                             // and set this.tdate[date] = [] << array of records..
37590                             _this.tdata  = {};
37591                             Roo.each(result.records, function(r){
37592                                 //Roo.log(r.data);
37593                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37594                                     _this.tdata[r.data.when_dt.format('j')] = [];
37595                                 }
37596                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37597                             });
37598                             
37599                             //Roo.log(_this.tdata);
37600                             
37601                             result.records = [];
37602                             result.totalRecords = 6;
37603                     
37604                             // let's generate some duumy records for the rows.
37605                             //var st = _this.dateField.getValue();
37606                             
37607                             // work out monday..
37608                             //st = st.add(Date.DAY, -1 * st.format('w'));
37609                             
37610                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37611                             
37612                             var firstOfMonth = date.getFirstDayOfMonth();
37613                             var days = date.getDaysInMonth();
37614                             var d = 1;
37615                             var firstAdded = false;
37616                             for (var i = 0; i < result.totalRecords ; i++) {
37617                                 //var d= st.add(Date.DAY, i);
37618                                 var row = {};
37619                                 var added = 0;
37620                                 for(var w = 0 ; w < 7 ; w++){
37621                                     if(!firstAdded && firstOfMonth != w){
37622                                         continue;
37623                                     }
37624                                     if(d > days){
37625                                         continue;
37626                                     }
37627                                     firstAdded = true;
37628                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37629                                     row['weekday'+w] = String.format(
37630                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37631                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37632                                                     d,
37633                                                     date.format('Y-m-')+dd
37634                                                 );
37635                                     added++;
37636                                     if(typeof(_this.tdata[d]) != 'undefined'){
37637                                         Roo.each(_this.tdata[d], function(r){
37638                                             var is_sub = '';
37639                                             var deactive = '';
37640                                             var id = r.id;
37641                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37642                                             if(r.parent_id*1>0){
37643                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37644                                                 id = r.parent_id;
37645                                             }
37646                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37647                                                 deactive = 'de-act-link';
37648                                             }
37649                                             
37650                                             row['weekday'+w] += String.format(
37651                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37652                                                     id, //0
37653                                                     r.product_id_name, //1
37654                                                     r.when_dt.format('h:ia'), //2
37655                                                     is_sub, //3
37656                                                     deactive, //4
37657                                                     desc // 5
37658                                             );
37659                                         });
37660                                     }
37661                                     d++;
37662                                 }
37663                                 
37664                                 // only do this if something added..
37665                                 if(added > 0){ 
37666                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37667                                 }
37668                                 
37669                                 
37670                                 // push it twice. (second one with an hour..
37671                                 
37672                             }
37673                             //Roo.log(result);
37674                             this.fireEvent("load", this, o, o.request.arg);
37675                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37676                         },
37677                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37678                     proxy : {
37679                         xtype: 'HttpProxy',
37680                         xns: Roo.data,
37681                         method : 'GET',
37682                         url : baseURL + '/Roo/Shop_course.php'
37683                     },
37684                     reader : {
37685                         xtype: 'JsonReader',
37686                         xns: Roo.data,
37687                         id : 'id',
37688                         fields : [
37689                             {
37690                                 'name': 'id',
37691                                 'type': 'int'
37692                             },
37693                             {
37694                                 'name': 'when_dt',
37695                                 'type': 'string'
37696                             },
37697                             {
37698                                 'name': 'end_dt',
37699                                 'type': 'string'
37700                             },
37701                             {
37702                                 'name': 'parent_id',
37703                                 'type': 'int'
37704                             },
37705                             {
37706                                 'name': 'product_id',
37707                                 'type': 'int'
37708                             },
37709                             {
37710                                 'name': 'productitem_id',
37711                                 'type': 'int'
37712                             },
37713                             {
37714                                 'name': 'guid',
37715                                 'type': 'int'
37716                             }
37717                         ]
37718                     }
37719                 },
37720                 toolbar : {
37721                     xtype: 'Toolbar',
37722                     xns: Roo,
37723                     items : [
37724                         {
37725                             xtype: 'Button',
37726                             xns: Roo.Toolbar,
37727                             listeners : {
37728                                 click : function (_self, e)
37729                                 {
37730                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37731                                     sd.setMonth(sd.getMonth()-1);
37732                                     _this.monthField.setValue(sd.format('Y-m-d'));
37733                                     _this.grid.ds.load({});
37734                                 }
37735                             },
37736                             text : "Back"
37737                         },
37738                         {
37739                             xtype: 'Separator',
37740                             xns: Roo.Toolbar
37741                         },
37742                         {
37743                             xtype: 'MonthField',
37744                             xns: Roo.form,
37745                             listeners : {
37746                                 render : function (_self)
37747                                 {
37748                                     _this.monthField = _self;
37749                                    // _this.monthField.set  today
37750                                 },
37751                                 select : function (combo, date)
37752                                 {
37753                                     _this.grid.ds.load({});
37754                                 }
37755                             },
37756                             value : (function() { return new Date(); })()
37757                         },
37758                         {
37759                             xtype: 'Separator',
37760                             xns: Roo.Toolbar
37761                         },
37762                         {
37763                             xtype: 'TextItem',
37764                             xns: Roo.Toolbar,
37765                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37766                         },
37767                         {
37768                             xtype: 'Fill',
37769                             xns: Roo.Toolbar
37770                         },
37771                         {
37772                             xtype: 'Button',
37773                             xns: Roo.Toolbar,
37774                             listeners : {
37775                                 click : function (_self, e)
37776                                 {
37777                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37778                                     sd.setMonth(sd.getMonth()+1);
37779                                     _this.monthField.setValue(sd.format('Y-m-d'));
37780                                     _this.grid.ds.load({});
37781                                 }
37782                             },
37783                             text : "Next"
37784                         }
37785                     ]
37786                 },
37787                  
37788             }
37789         };
37790         
37791         *//*
37792  * Based on:
37793  * Ext JS Library 1.1.1
37794  * Copyright(c) 2006-2007, Ext JS, LLC.
37795  *
37796  * Originally Released Under LGPL - original licence link has changed is not relivant.
37797  *
37798  * Fork - LGPL
37799  * <script type="text/javascript">
37800  */
37801  
37802 /**
37803  * @class Roo.LoadMask
37804  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37805  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37806  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37807  * element's UpdateManager load indicator and will be destroyed after the initial load.
37808  * @constructor
37809  * Create a new LoadMask
37810  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37811  * @param {Object} config The config object
37812  */
37813 Roo.LoadMask = function(el, config){
37814     this.el = Roo.get(el);
37815     Roo.apply(this, config);
37816     if(this.store){
37817         this.store.on('beforeload', this.onBeforeLoad, this);
37818         this.store.on('load', this.onLoad, this);
37819         this.store.on('loadexception', this.onLoadException, this);
37820         this.removeMask = false;
37821     }else{
37822         var um = this.el.getUpdateManager();
37823         um.showLoadIndicator = false; // disable the default indicator
37824         um.on('beforeupdate', this.onBeforeLoad, this);
37825         um.on('update', this.onLoad, this);
37826         um.on('failure', this.onLoad, this);
37827         this.removeMask = true;
37828     }
37829 };
37830
37831 Roo.LoadMask.prototype = {
37832     /**
37833      * @cfg {Boolean} removeMask
37834      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37835      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37836      */
37837     /**
37838      * @cfg {String} msg
37839      * The text to display in a centered loading message box (defaults to 'Loading...')
37840      */
37841     msg : 'Loading...',
37842     /**
37843      * @cfg {String} msgCls
37844      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37845      */
37846     msgCls : 'x-mask-loading',
37847
37848     /**
37849      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37850      * @type Boolean
37851      */
37852     disabled: false,
37853
37854     /**
37855      * Disables the mask to prevent it from being displayed
37856      */
37857     disable : function(){
37858        this.disabled = true;
37859     },
37860
37861     /**
37862      * Enables the mask so that it can be displayed
37863      */
37864     enable : function(){
37865         this.disabled = false;
37866     },
37867     
37868     onLoadException : function()
37869     {
37870         Roo.log(arguments);
37871         
37872         if (typeof(arguments[3]) != 'undefined') {
37873             Roo.MessageBox.alert("Error loading",arguments[3]);
37874         } 
37875         /*
37876         try {
37877             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37878                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37879             }   
37880         } catch(e) {
37881             
37882         }
37883         */
37884     
37885         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37886     },
37887     // private
37888     onLoad : function()
37889     {
37890         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37891     },
37892
37893     // private
37894     onBeforeLoad : function(){
37895         if(!this.disabled){
37896             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37897         }
37898     },
37899
37900     // private
37901     destroy : function(){
37902         if(this.store){
37903             this.store.un('beforeload', this.onBeforeLoad, this);
37904             this.store.un('load', this.onLoad, this);
37905             this.store.un('loadexception', this.onLoadException, this);
37906         }else{
37907             var um = this.el.getUpdateManager();
37908             um.un('beforeupdate', this.onBeforeLoad, this);
37909             um.un('update', this.onLoad, this);
37910             um.un('failure', this.onLoad, this);
37911         }
37912     }
37913 };/*
37914  * Based on:
37915  * Ext JS Library 1.1.1
37916  * Copyright(c) 2006-2007, Ext JS, LLC.
37917  *
37918  * Originally Released Under LGPL - original licence link has changed is not relivant.
37919  *
37920  * Fork - LGPL
37921  * <script type="text/javascript">
37922  */
37923
37924
37925 /**
37926  * @class Roo.XTemplate
37927  * @extends Roo.Template
37928  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37929 <pre><code>
37930 var t = new Roo.XTemplate(
37931         '&lt;select name="{name}"&gt;',
37932                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37933         '&lt;/select&gt;'
37934 );
37935  
37936 // then append, applying the master template values
37937  </code></pre>
37938  *
37939  * Supported features:
37940  *
37941  *  Tags:
37942
37943 <pre><code>
37944       {a_variable} - output encoded.
37945       {a_variable.format:("Y-m-d")} - call a method on the variable
37946       {a_variable:raw} - unencoded output
37947       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37948       {a_variable:this.method_on_template(...)} - call a method on the template object.
37949  
37950 </code></pre>
37951  *  The tpl tag:
37952 <pre><code>
37953         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37954         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37955         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37956         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37957   
37958         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37959         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37960 </code></pre>
37961  *      
37962  */
37963 Roo.XTemplate = function()
37964 {
37965     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37966     if (this.html) {
37967         this.compile();
37968     }
37969 };
37970
37971
37972 Roo.extend(Roo.XTemplate, Roo.Template, {
37973
37974     /**
37975      * The various sub templates
37976      */
37977     tpls : false,
37978     /**
37979      *
37980      * basic tag replacing syntax
37981      * WORD:WORD()
37982      *
37983      * // you can fake an object call by doing this
37984      *  x.t:(test,tesT) 
37985      * 
37986      */
37987     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37988
37989     /**
37990      * compile the template
37991      *
37992      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37993      *
37994      */
37995     compile: function()
37996     {
37997         var s = this.html;
37998      
37999         s = ['<tpl>', s, '</tpl>'].join('');
38000     
38001         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38002             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38003             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38004             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38005             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38006             m,
38007             id     = 0,
38008             tpls   = [];
38009     
38010         while(true == !!(m = s.match(re))){
38011             var forMatch   = m[0].match(nameRe),
38012                 ifMatch   = m[0].match(ifRe),
38013                 execMatch   = m[0].match(execRe),
38014                 namedMatch   = m[0].match(namedRe),
38015                 
38016                 exp  = null, 
38017                 fn   = null,
38018                 exec = null,
38019                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38020                 
38021             if (ifMatch) {
38022                 // if - puts fn into test..
38023                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38024                 if(exp){
38025                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38026                 }
38027             }
38028             
38029             if (execMatch) {
38030                 // exec - calls a function... returns empty if true is  returned.
38031                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38032                 if(exp){
38033                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38034                 }
38035             }
38036             
38037             
38038             if (name) {
38039                 // for = 
38040                 switch(name){
38041                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38042                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38043                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38044                 }
38045             }
38046             var uid = namedMatch ? namedMatch[1] : id;
38047             
38048             
38049             tpls.push({
38050                 id:     namedMatch ? namedMatch[1] : id,
38051                 target: name,
38052                 exec:   exec,
38053                 test:   fn,
38054                 body:   m[1] || ''
38055             });
38056             if (namedMatch) {
38057                 s = s.replace(m[0], '');
38058             } else { 
38059                 s = s.replace(m[0], '{xtpl'+ id + '}');
38060             }
38061             ++id;
38062         }
38063         this.tpls = [];
38064         for(var i = tpls.length-1; i >= 0; --i){
38065             this.compileTpl(tpls[i]);
38066             this.tpls[tpls[i].id] = tpls[i];
38067         }
38068         this.master = tpls[tpls.length-1];
38069         return this;
38070     },
38071     /**
38072      * same as applyTemplate, except it's done to one of the subTemplates
38073      * when using named templates, you can do:
38074      *
38075      * var str = pl.applySubTemplate('your-name', values);
38076      *
38077      * 
38078      * @param {Number} id of the template
38079      * @param {Object} values to apply to template
38080      * @param {Object} parent (normaly the instance of this object)
38081      */
38082     applySubTemplate : function(id, values, parent)
38083     {
38084         
38085         
38086         var t = this.tpls[id];
38087         
38088         
38089         try { 
38090             if(t.test && !t.test.call(this, values, parent)){
38091                 return '';
38092             }
38093         } catch(e) {
38094             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38095             Roo.log(e.toString());
38096             Roo.log(t.test);
38097             return ''
38098         }
38099         try { 
38100             
38101             if(t.exec && t.exec.call(this, values, parent)){
38102                 return '';
38103             }
38104         } catch(e) {
38105             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38106             Roo.log(e.toString());
38107             Roo.log(t.exec);
38108             return ''
38109         }
38110         try {
38111             var vs = t.target ? t.target.call(this, values, parent) : values;
38112             parent = t.target ? values : parent;
38113             if(t.target && vs instanceof Array){
38114                 var buf = [];
38115                 for(var i = 0, len = vs.length; i < len; i++){
38116                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38117                 }
38118                 return buf.join('');
38119             }
38120             return t.compiled.call(this, vs, parent);
38121         } catch (e) {
38122             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38123             Roo.log(e.toString());
38124             Roo.log(t.compiled);
38125             return '';
38126         }
38127     },
38128
38129     compileTpl : function(tpl)
38130     {
38131         var fm = Roo.util.Format;
38132         var useF = this.disableFormats !== true;
38133         var sep = Roo.isGecko ? "+" : ",";
38134         var undef = function(str) {
38135             Roo.log("Property not found :"  + str);
38136             return '';
38137         };
38138         
38139         var fn = function(m, name, format, args)
38140         {
38141             //Roo.log(arguments);
38142             args = args ? args.replace(/\\'/g,"'") : args;
38143             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38144             if (typeof(format) == 'undefined') {
38145                 format= 'htmlEncode';
38146             }
38147             if (format == 'raw' ) {
38148                 format = false;
38149             }
38150             
38151             if(name.substr(0, 4) == 'xtpl'){
38152                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38153             }
38154             
38155             // build an array of options to determine if value is undefined..
38156             
38157             // basically get 'xxxx.yyyy' then do
38158             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38159             //    (function () { Roo.log("Property not found"); return ''; })() :
38160             //    ......
38161             
38162             var udef_ar = [];
38163             var lookfor = '';
38164             Roo.each(name.split('.'), function(st) {
38165                 lookfor += (lookfor.length ? '.': '') + st;
38166                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38167             });
38168             
38169             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38170             
38171             
38172             if(format && useF){
38173                 
38174                 args = args ? ',' + args : "";
38175                  
38176                 if(format.substr(0, 5) != "this."){
38177                     format = "fm." + format + '(';
38178                 }else{
38179                     format = 'this.call("'+ format.substr(5) + '", ';
38180                     args = ", values";
38181                 }
38182                 
38183                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38184             }
38185              
38186             if (args.length) {
38187                 // called with xxyx.yuu:(test,test)
38188                 // change to ()
38189                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38190             }
38191             // raw.. - :raw modifier..
38192             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38193             
38194         };
38195         var body;
38196         // branched to use + in gecko and [].join() in others
38197         if(Roo.isGecko){
38198             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38199                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38200                     "';};};";
38201         }else{
38202             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38203             body.push(tpl.body.replace(/(\r\n|\n)/g,
38204                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38205             body.push("'].join('');};};");
38206             body = body.join('');
38207         }
38208         
38209         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38210        
38211         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38212         eval(body);
38213         
38214         return this;
38215     },
38216
38217     applyTemplate : function(values){
38218         return this.master.compiled.call(this, values, {});
38219         //var s = this.subs;
38220     },
38221
38222     apply : function(){
38223         return this.applyTemplate.apply(this, arguments);
38224     }
38225
38226  });
38227
38228 Roo.XTemplate.from = function(el){
38229     el = Roo.getDom(el);
38230     return new Roo.XTemplate(el.value || el.innerHTML);
38231 };