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             
20518         }
20519         
20520         st +=  '<style type="text/css">' +
20521             'IMG { cursor: pointer } ' +
20522         '</style>';
20523
20524         
20525         return '<html><head>' + st  +
20526             //<style type="text/css">' +
20527             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20528             //'</style>' +
20529             ' </head><body class="roo-htmleditor-body"></body></html>';
20530     },
20531
20532     // private
20533     onRender : function(ct, position)
20534     {
20535         var _t = this;
20536         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20537         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20538         
20539         
20540         this.el.dom.style.border = '0 none';
20541         this.el.dom.setAttribute('tabIndex', -1);
20542         this.el.addClass('x-hidden hide');
20543         
20544         
20545         
20546         if(Roo.isIE){ // fix IE 1px bogus margin
20547             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20548         }
20549        
20550         
20551         this.frameId = Roo.id();
20552         
20553          
20554         
20555         var iframe = this.owner.wrap.createChild({
20556             tag: 'iframe',
20557             cls: 'form-control', // bootstrap..
20558             id: this.frameId,
20559             name: this.frameId,
20560             frameBorder : 'no',
20561             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20562         }, this.el
20563         );
20564         
20565         
20566         this.iframe = iframe.dom;
20567
20568          this.assignDocWin();
20569         
20570         this.doc.designMode = 'on';
20571        
20572         this.doc.open();
20573         this.doc.write(this.getDocMarkup());
20574         this.doc.close();
20575
20576         
20577         var task = { // must defer to wait for browser to be ready
20578             run : function(){
20579                 //console.log("run task?" + this.doc.readyState);
20580                 this.assignDocWin();
20581                 if(this.doc.body || this.doc.readyState == 'complete'){
20582                     try {
20583                         this.doc.designMode="on";
20584                     } catch (e) {
20585                         return;
20586                     }
20587                     Roo.TaskMgr.stop(task);
20588                     this.initEditor.defer(10, this);
20589                 }
20590             },
20591             interval : 10,
20592             duration: 10000,
20593             scope: this
20594         };
20595         Roo.TaskMgr.start(task);
20596
20597     },
20598
20599     // private
20600     onResize : function(w, h)
20601     {
20602          Roo.log('resize: ' +w + ',' + h );
20603         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20604         if(!this.iframe){
20605             return;
20606         }
20607         if(typeof w == 'number'){
20608             
20609             this.iframe.style.width = w + 'px';
20610         }
20611         if(typeof h == 'number'){
20612             
20613             this.iframe.style.height = h + 'px';
20614             if(this.doc){
20615                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20616             }
20617         }
20618         
20619     },
20620
20621     /**
20622      * Toggles the editor between standard and source edit mode.
20623      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20624      */
20625     toggleSourceEdit : function(sourceEditMode){
20626         
20627         this.sourceEditMode = sourceEditMode === true;
20628         
20629         if(this.sourceEditMode){
20630  
20631             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20632             
20633         }else{
20634             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20635             //this.iframe.className = '';
20636             this.deferFocus();
20637         }
20638         //this.setSize(this.owner.wrap.getSize());
20639         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20640     },
20641
20642     
20643   
20644
20645     /**
20646      * Protected method that will not generally be called directly. If you need/want
20647      * custom HTML cleanup, this is the method you should override.
20648      * @param {String} html The HTML to be cleaned
20649      * return {String} The cleaned HTML
20650      */
20651     cleanHtml : function(html){
20652         html = String(html);
20653         if(html.length > 5){
20654             if(Roo.isSafari){ // strip safari nonsense
20655                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20656             }
20657         }
20658         if(html == '&nbsp;'){
20659             html = '';
20660         }
20661         return html;
20662     },
20663
20664     /**
20665      * HTML Editor -> Textarea
20666      * Protected method that will not generally be called directly. Syncs the contents
20667      * of the editor iframe with the textarea.
20668      */
20669     syncValue : function(){
20670         if(this.initialized){
20671             var bd = (this.doc.body || this.doc.documentElement);
20672             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20673             var html = bd.innerHTML;
20674             if(Roo.isSafari){
20675                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20676                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20677                 if(m && m[1]){
20678                     html = '<div style="'+m[0]+'">' + html + '</div>';
20679                 }
20680             }
20681             html = this.cleanHtml(html);
20682             // fix up the special chars.. normaly like back quotes in word...
20683             // however we do not want to do this with chinese..
20684             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20685                 var cc = b.charCodeAt();
20686                 if (
20687                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20688                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20689                     (cc >= 0xf900 && cc < 0xfb00 )
20690                 ) {
20691                         return b;
20692                 }
20693                 return "&#"+cc+";" 
20694             });
20695             if(this.owner.fireEvent('beforesync', this, html) !== false){
20696                 this.el.dom.value = html;
20697                 this.owner.fireEvent('sync', this, html);
20698             }
20699         }
20700     },
20701
20702     /**
20703      * Protected method that will not generally be called directly. Pushes the value of the textarea
20704      * into the iframe editor.
20705      */
20706     pushValue : function(){
20707         if(this.initialized){
20708             var v = this.el.dom.value.trim();
20709             
20710 //            if(v.length < 1){
20711 //                v = '&#160;';
20712 //            }
20713             
20714             if(this.owner.fireEvent('beforepush', this, v) !== false){
20715                 var d = (this.doc.body || this.doc.documentElement);
20716                 d.innerHTML = v;
20717                 this.cleanUpPaste();
20718                 this.el.dom.value = d.innerHTML;
20719                 this.owner.fireEvent('push', this, v);
20720             }
20721         }
20722     },
20723
20724     // private
20725     deferFocus : function(){
20726         this.focus.defer(10, this);
20727     },
20728
20729     // doc'ed in Field
20730     focus : function(){
20731         if(this.win && !this.sourceEditMode){
20732             this.win.focus();
20733         }else{
20734             this.el.focus();
20735         }
20736     },
20737     
20738     assignDocWin: function()
20739     {
20740         var iframe = this.iframe;
20741         
20742          if(Roo.isIE){
20743             this.doc = iframe.contentWindow.document;
20744             this.win = iframe.contentWindow;
20745         } else {
20746 //            if (!Roo.get(this.frameId)) {
20747 //                return;
20748 //            }
20749 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20750 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20751             
20752             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20753                 return;
20754             }
20755             
20756             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20757             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20758         }
20759     },
20760     
20761     // private
20762     initEditor : function(){
20763         //console.log("INIT EDITOR");
20764         this.assignDocWin();
20765         
20766         
20767         
20768         this.doc.designMode="on";
20769         this.doc.open();
20770         this.doc.write(this.getDocMarkup());
20771         this.doc.close();
20772         
20773         var dbody = (this.doc.body || this.doc.documentElement);
20774         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20775         // this copies styles from the containing element into thsi one..
20776         // not sure why we need all of this..
20777         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20778         
20779         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20780         //ss['background-attachment'] = 'fixed'; // w3c
20781         dbody.bgProperties = 'fixed'; // ie
20782         //Roo.DomHelper.applyStyles(dbody, ss);
20783         Roo.EventManager.on(this.doc, {
20784             //'mousedown': this.onEditorEvent,
20785             'mouseup': this.onEditorEvent,
20786             'dblclick': this.onEditorEvent,
20787             'click': this.onEditorEvent,
20788             'keyup': this.onEditorEvent,
20789             buffer:100,
20790             scope: this
20791         });
20792         if(Roo.isGecko){
20793             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20794         }
20795         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20796             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20797         }
20798         this.initialized = true;
20799
20800         this.owner.fireEvent('initialize', this);
20801         this.pushValue();
20802     },
20803
20804     // private
20805     onDestroy : function(){
20806         
20807         
20808         
20809         if(this.rendered){
20810             
20811             //for (var i =0; i < this.toolbars.length;i++) {
20812             //    // fixme - ask toolbars for heights?
20813             //    this.toolbars[i].onDestroy();
20814            // }
20815             
20816             //this.wrap.dom.innerHTML = '';
20817             //this.wrap.remove();
20818         }
20819     },
20820
20821     // private
20822     onFirstFocus : function(){
20823         
20824         this.assignDocWin();
20825         
20826         
20827         this.activated = true;
20828          
20829     
20830         if(Roo.isGecko){ // prevent silly gecko errors
20831             this.win.focus();
20832             var s = this.win.getSelection();
20833             if(!s.focusNode || s.focusNode.nodeType != 3){
20834                 var r = s.getRangeAt(0);
20835                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20836                 r.collapse(true);
20837                 this.deferFocus();
20838             }
20839             try{
20840                 this.execCmd('useCSS', true);
20841                 this.execCmd('styleWithCSS', false);
20842             }catch(e){}
20843         }
20844         this.owner.fireEvent('activate', this);
20845     },
20846
20847     // private
20848     adjustFont: function(btn){
20849         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20850         //if(Roo.isSafari){ // safari
20851         //    adjust *= 2;
20852        // }
20853         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20854         if(Roo.isSafari){ // safari
20855             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20856             v =  (v < 10) ? 10 : v;
20857             v =  (v > 48) ? 48 : v;
20858             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20859             
20860         }
20861         
20862         
20863         v = Math.max(1, v+adjust);
20864         
20865         this.execCmd('FontSize', v  );
20866     },
20867
20868     onEditorEvent : function(e)
20869     {
20870         this.owner.fireEvent('editorevent', this, e);
20871       //  this.updateToolbar();
20872         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20873     },
20874
20875     insertTag : function(tg)
20876     {
20877         // could be a bit smarter... -> wrap the current selected tRoo..
20878         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20879             
20880             range = this.createRange(this.getSelection());
20881             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20882             wrappingNode.appendChild(range.extractContents());
20883             range.insertNode(wrappingNode);
20884
20885             return;
20886             
20887             
20888             
20889         }
20890         this.execCmd("formatblock",   tg);
20891         
20892     },
20893     
20894     insertText : function(txt)
20895     {
20896         
20897         
20898         var range = this.createRange();
20899         range.deleteContents();
20900                //alert(Sender.getAttribute('label'));
20901                
20902         range.insertNode(this.doc.createTextNode(txt));
20903     } ,
20904     
20905      
20906
20907     /**
20908      * Executes a Midas editor command on the editor document and performs necessary focus and
20909      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20910      * @param {String} cmd The Midas command
20911      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20912      */
20913     relayCmd : function(cmd, value){
20914         this.win.focus();
20915         this.execCmd(cmd, value);
20916         this.owner.fireEvent('editorevent', this);
20917         //this.updateToolbar();
20918         this.owner.deferFocus();
20919     },
20920
20921     /**
20922      * Executes a Midas editor command directly on the editor document.
20923      * For visual commands, you should use {@link #relayCmd} instead.
20924      * <b>This should only be called after the editor is initialized.</b>
20925      * @param {String} cmd The Midas command
20926      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20927      */
20928     execCmd : function(cmd, value){
20929         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20930         this.syncValue();
20931     },
20932  
20933  
20934    
20935     /**
20936      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20937      * to insert tRoo.
20938      * @param {String} text | dom node.. 
20939      */
20940     insertAtCursor : function(text)
20941     {
20942         
20943         if(!this.activated){
20944             return;
20945         }
20946         /*
20947         if(Roo.isIE){
20948             this.win.focus();
20949             var r = this.doc.selection.createRange();
20950             if(r){
20951                 r.collapse(true);
20952                 r.pasteHTML(text);
20953                 this.syncValue();
20954                 this.deferFocus();
20955             
20956             }
20957             return;
20958         }
20959         */
20960         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20961             this.win.focus();
20962             
20963             
20964             // from jquery ui (MIT licenced)
20965             var range, node;
20966             var win = this.win;
20967             
20968             if (win.getSelection && win.getSelection().getRangeAt) {
20969                 range = win.getSelection().getRangeAt(0);
20970                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20971                 range.insertNode(node);
20972             } else if (win.document.selection && win.document.selection.createRange) {
20973                 // no firefox support
20974                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20975                 win.document.selection.createRange().pasteHTML(txt);
20976             } else {
20977                 // no firefox support
20978                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20979                 this.execCmd('InsertHTML', txt);
20980             } 
20981             
20982             this.syncValue();
20983             
20984             this.deferFocus();
20985         }
20986     },
20987  // private
20988     mozKeyPress : function(e){
20989         if(e.ctrlKey){
20990             var c = e.getCharCode(), cmd;
20991           
20992             if(c > 0){
20993                 c = String.fromCharCode(c).toLowerCase();
20994                 switch(c){
20995                     case 'b':
20996                         cmd = 'bold';
20997                         break;
20998                     case 'i':
20999                         cmd = 'italic';
21000                         break;
21001                     
21002                     case 'u':
21003                         cmd = 'underline';
21004                         break;
21005                     
21006                     case 'v':
21007                         this.cleanUpPaste.defer(100, this);
21008                         return;
21009                         
21010                 }
21011                 if(cmd){
21012                     this.win.focus();
21013                     this.execCmd(cmd);
21014                     this.deferFocus();
21015                     e.preventDefault();
21016                 }
21017                 
21018             }
21019         }
21020     },
21021
21022     // private
21023     fixKeys : function(){ // load time branching for fastest keydown performance
21024         if(Roo.isIE){
21025             return function(e){
21026                 var k = e.getKey(), r;
21027                 if(k == e.TAB){
21028                     e.stopEvent();
21029                     r = this.doc.selection.createRange();
21030                     if(r){
21031                         r.collapse(true);
21032                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21033                         this.deferFocus();
21034                     }
21035                     return;
21036                 }
21037                 
21038                 if(k == e.ENTER){
21039                     r = this.doc.selection.createRange();
21040                     if(r){
21041                         var target = r.parentElement();
21042                         if(!target || target.tagName.toLowerCase() != 'li'){
21043                             e.stopEvent();
21044                             r.pasteHTML('<br />');
21045                             r.collapse(false);
21046                             r.select();
21047                         }
21048                     }
21049                 }
21050                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21051                     this.cleanUpPaste.defer(100, this);
21052                     return;
21053                 }
21054                 
21055                 
21056             };
21057         }else if(Roo.isOpera){
21058             return function(e){
21059                 var k = e.getKey();
21060                 if(k == e.TAB){
21061                     e.stopEvent();
21062                     this.win.focus();
21063                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21064                     this.deferFocus();
21065                 }
21066                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21067                     this.cleanUpPaste.defer(100, this);
21068                     return;
21069                 }
21070                 
21071             };
21072         }else if(Roo.isSafari){
21073             return function(e){
21074                 var k = e.getKey();
21075                 
21076                 if(k == e.TAB){
21077                     e.stopEvent();
21078                     this.execCmd('InsertText','\t');
21079                     this.deferFocus();
21080                     return;
21081                 }
21082                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21083                     this.cleanUpPaste.defer(100, this);
21084                     return;
21085                 }
21086                 
21087              };
21088         }
21089     }(),
21090     
21091     getAllAncestors: function()
21092     {
21093         var p = this.getSelectedNode();
21094         var a = [];
21095         if (!p) {
21096             a.push(p); // push blank onto stack..
21097             p = this.getParentElement();
21098         }
21099         
21100         
21101         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21102             a.push(p);
21103             p = p.parentNode;
21104         }
21105         a.push(this.doc.body);
21106         return a;
21107     },
21108     lastSel : false,
21109     lastSelNode : false,
21110     
21111     
21112     getSelection : function() 
21113     {
21114         this.assignDocWin();
21115         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21116     },
21117     
21118     getSelectedNode: function() 
21119     {
21120         // this may only work on Gecko!!!
21121         
21122         // should we cache this!!!!
21123         
21124         
21125         
21126          
21127         var range = this.createRange(this.getSelection()).cloneRange();
21128         
21129         if (Roo.isIE) {
21130             var parent = range.parentElement();
21131             while (true) {
21132                 var testRange = range.duplicate();
21133                 testRange.moveToElementText(parent);
21134                 if (testRange.inRange(range)) {
21135                     break;
21136                 }
21137                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21138                     break;
21139                 }
21140                 parent = parent.parentElement;
21141             }
21142             return parent;
21143         }
21144         
21145         // is ancestor a text element.
21146         var ac =  range.commonAncestorContainer;
21147         if (ac.nodeType == 3) {
21148             ac = ac.parentNode;
21149         }
21150         
21151         var ar = ac.childNodes;
21152          
21153         var nodes = [];
21154         var other_nodes = [];
21155         var has_other_nodes = false;
21156         for (var i=0;i<ar.length;i++) {
21157             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21158                 continue;
21159             }
21160             // fullly contained node.
21161             
21162             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21163                 nodes.push(ar[i]);
21164                 continue;
21165             }
21166             
21167             // probably selected..
21168             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21169                 other_nodes.push(ar[i]);
21170                 continue;
21171             }
21172             // outer..
21173             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21174                 continue;
21175             }
21176             
21177             
21178             has_other_nodes = true;
21179         }
21180         if (!nodes.length && other_nodes.length) {
21181             nodes= other_nodes;
21182         }
21183         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21184             return false;
21185         }
21186         
21187         return nodes[0];
21188     },
21189     createRange: function(sel)
21190     {
21191         // this has strange effects when using with 
21192         // top toolbar - not sure if it's a great idea.
21193         //this.editor.contentWindow.focus();
21194         if (typeof sel != "undefined") {
21195             try {
21196                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21197             } catch(e) {
21198                 return this.doc.createRange();
21199             }
21200         } else {
21201             return this.doc.createRange();
21202         }
21203     },
21204     getParentElement: function()
21205     {
21206         
21207         this.assignDocWin();
21208         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21209         
21210         var range = this.createRange(sel);
21211          
21212         try {
21213             var p = range.commonAncestorContainer;
21214             while (p.nodeType == 3) { // text node
21215                 p = p.parentNode;
21216             }
21217             return p;
21218         } catch (e) {
21219             return null;
21220         }
21221     
21222     },
21223     /***
21224      *
21225      * Range intersection.. the hard stuff...
21226      *  '-1' = before
21227      *  '0' = hits..
21228      *  '1' = after.
21229      *         [ -- selected range --- ]
21230      *   [fail]                        [fail]
21231      *
21232      *    basically..
21233      *      if end is before start or  hits it. fail.
21234      *      if start is after end or hits it fail.
21235      *
21236      *   if either hits (but other is outside. - then it's not 
21237      *   
21238      *    
21239      **/
21240     
21241     
21242     // @see http://www.thismuchiknow.co.uk/?p=64.
21243     rangeIntersectsNode : function(range, node)
21244     {
21245         var nodeRange = node.ownerDocument.createRange();
21246         try {
21247             nodeRange.selectNode(node);
21248         } catch (e) {
21249             nodeRange.selectNodeContents(node);
21250         }
21251     
21252         var rangeStartRange = range.cloneRange();
21253         rangeStartRange.collapse(true);
21254     
21255         var rangeEndRange = range.cloneRange();
21256         rangeEndRange.collapse(false);
21257     
21258         var nodeStartRange = nodeRange.cloneRange();
21259         nodeStartRange.collapse(true);
21260     
21261         var nodeEndRange = nodeRange.cloneRange();
21262         nodeEndRange.collapse(false);
21263     
21264         return rangeStartRange.compareBoundaryPoints(
21265                  Range.START_TO_START, nodeEndRange) == -1 &&
21266                rangeEndRange.compareBoundaryPoints(
21267                  Range.START_TO_START, nodeStartRange) == 1;
21268         
21269          
21270     },
21271     rangeCompareNode : function(range, node)
21272     {
21273         var nodeRange = node.ownerDocument.createRange();
21274         try {
21275             nodeRange.selectNode(node);
21276         } catch (e) {
21277             nodeRange.selectNodeContents(node);
21278         }
21279         
21280         
21281         range.collapse(true);
21282     
21283         nodeRange.collapse(true);
21284      
21285         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21286         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21287          
21288         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21289         
21290         var nodeIsBefore   =  ss == 1;
21291         var nodeIsAfter    = ee == -1;
21292         
21293         if (nodeIsBefore && nodeIsAfter) {
21294             return 0; // outer
21295         }
21296         if (!nodeIsBefore && nodeIsAfter) {
21297             return 1; //right trailed.
21298         }
21299         
21300         if (nodeIsBefore && !nodeIsAfter) {
21301             return 2;  // left trailed.
21302         }
21303         // fully contined.
21304         return 3;
21305     },
21306
21307     // private? - in a new class?
21308     cleanUpPaste :  function()
21309     {
21310         // cleans up the whole document..
21311         Roo.log('cleanuppaste');
21312         
21313         this.cleanUpChildren(this.doc.body);
21314         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21315         if (clean != this.doc.body.innerHTML) {
21316             this.doc.body.innerHTML = clean;
21317         }
21318         
21319     },
21320     
21321     cleanWordChars : function(input) {// change the chars to hex code
21322         var he = Roo.HtmlEditorCore;
21323         
21324         var output = input;
21325         Roo.each(he.swapCodes, function(sw) { 
21326             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21327             
21328             output = output.replace(swapper, sw[1]);
21329         });
21330         
21331         return output;
21332     },
21333     
21334     
21335     cleanUpChildren : function (n)
21336     {
21337         if (!n.childNodes.length) {
21338             return;
21339         }
21340         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21341            this.cleanUpChild(n.childNodes[i]);
21342         }
21343     },
21344     
21345     
21346         
21347     
21348     cleanUpChild : function (node)
21349     {
21350         var ed = this;
21351         //console.log(node);
21352         if (node.nodeName == "#text") {
21353             // clean up silly Windows -- stuff?
21354             return; 
21355         }
21356         if (node.nodeName == "#comment") {
21357             node.parentNode.removeChild(node);
21358             // clean up silly Windows -- stuff?
21359             return; 
21360         }
21361         var lcname = node.tagName.toLowerCase();
21362         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21363         // whitelist of tags..
21364         
21365         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21366             // remove node.
21367             node.parentNode.removeChild(node);
21368             return;
21369             
21370         }
21371         
21372         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21373         
21374         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21375         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21376         
21377         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21378         //    remove_keep_children = true;
21379         //}
21380         
21381         if (remove_keep_children) {
21382             this.cleanUpChildren(node);
21383             // inserts everything just before this node...
21384             while (node.childNodes.length) {
21385                 var cn = node.childNodes[0];
21386                 node.removeChild(cn);
21387                 node.parentNode.insertBefore(cn, node);
21388             }
21389             node.parentNode.removeChild(node);
21390             return;
21391         }
21392         
21393         if (!node.attributes || !node.attributes.length) {
21394             this.cleanUpChildren(node);
21395             return;
21396         }
21397         
21398         function cleanAttr(n,v)
21399         {
21400             
21401             if (v.match(/^\./) || v.match(/^\//)) {
21402                 return;
21403             }
21404             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21405                 return;
21406             }
21407             if (v.match(/^#/)) {
21408                 return;
21409             }
21410 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21411             node.removeAttribute(n);
21412             
21413         }
21414         
21415         var cwhite = this.cwhite;
21416         var cblack = this.cblack;
21417             
21418         function cleanStyle(n,v)
21419         {
21420             if (v.match(/expression/)) { //XSS?? should we even bother..
21421                 node.removeAttribute(n);
21422                 return;
21423             }
21424             
21425             var parts = v.split(/;/);
21426             var clean = [];
21427             
21428             Roo.each(parts, function(p) {
21429                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21430                 if (!p.length) {
21431                     return true;
21432                 }
21433                 var l = p.split(':').shift().replace(/\s+/g,'');
21434                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21435                 
21436                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21437 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21438                     //node.removeAttribute(n);
21439                     return true;
21440                 }
21441                 //Roo.log()
21442                 // only allow 'c whitelisted system attributes'
21443                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21444 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21445                     //node.removeAttribute(n);
21446                     return true;
21447                 }
21448                 
21449                 
21450                  
21451                 
21452                 clean.push(p);
21453                 return true;
21454             });
21455             if (clean.length) { 
21456                 node.setAttribute(n, clean.join(';'));
21457             } else {
21458                 node.removeAttribute(n);
21459             }
21460             
21461         }
21462         
21463         
21464         for (var i = node.attributes.length-1; i > -1 ; i--) {
21465             var a = node.attributes[i];
21466             //console.log(a);
21467             
21468             if (a.name.toLowerCase().substr(0,2)=='on')  {
21469                 node.removeAttribute(a.name);
21470                 continue;
21471             }
21472             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21473                 node.removeAttribute(a.name);
21474                 continue;
21475             }
21476             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21477                 cleanAttr(a.name,a.value); // fixme..
21478                 continue;
21479             }
21480             if (a.name == 'style') {
21481                 cleanStyle(a.name,a.value);
21482                 continue;
21483             }
21484             /// clean up MS crap..
21485             // tecnically this should be a list of valid class'es..
21486             
21487             
21488             if (a.name == 'class') {
21489                 if (a.value.match(/^Mso/)) {
21490                     node.className = '';
21491                 }
21492                 
21493                 if (a.value.match(/^body$/)) {
21494                     node.className = '';
21495                 }
21496                 continue;
21497             }
21498             
21499             // style cleanup!?
21500             // class cleanup?
21501             
21502         }
21503         
21504         
21505         this.cleanUpChildren(node);
21506         
21507         
21508     },
21509     
21510     /**
21511      * Clean up MS wordisms...
21512      */
21513     cleanWord : function(node)
21514     {
21515         
21516         
21517         if (!node) {
21518             this.cleanWord(this.doc.body);
21519             return;
21520         }
21521         if (node.nodeName == "#text") {
21522             // clean up silly Windows -- stuff?
21523             return; 
21524         }
21525         if (node.nodeName == "#comment") {
21526             node.parentNode.removeChild(node);
21527             // clean up silly Windows -- stuff?
21528             return; 
21529         }
21530         
21531         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21532             node.parentNode.removeChild(node);
21533             return;
21534         }
21535         
21536         // remove - but keep children..
21537         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21538             while (node.childNodes.length) {
21539                 var cn = node.childNodes[0];
21540                 node.removeChild(cn);
21541                 node.parentNode.insertBefore(cn, node);
21542             }
21543             node.parentNode.removeChild(node);
21544             this.iterateChildren(node, this.cleanWord);
21545             return;
21546         }
21547         // clean styles
21548         if (node.className.length) {
21549             
21550             var cn = node.className.split(/\W+/);
21551             var cna = [];
21552             Roo.each(cn, function(cls) {
21553                 if (cls.match(/Mso[a-zA-Z]+/)) {
21554                     return;
21555                 }
21556                 cna.push(cls);
21557             });
21558             node.className = cna.length ? cna.join(' ') : '';
21559             if (!cna.length) {
21560                 node.removeAttribute("class");
21561             }
21562         }
21563         
21564         if (node.hasAttribute("lang")) {
21565             node.removeAttribute("lang");
21566         }
21567         
21568         if (node.hasAttribute("style")) {
21569             
21570             var styles = node.getAttribute("style").split(";");
21571             var nstyle = [];
21572             Roo.each(styles, function(s) {
21573                 if (!s.match(/:/)) {
21574                     return;
21575                 }
21576                 var kv = s.split(":");
21577                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21578                     return;
21579                 }
21580                 // what ever is left... we allow.
21581                 nstyle.push(s);
21582             });
21583             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21584             if (!nstyle.length) {
21585                 node.removeAttribute('style');
21586             }
21587         }
21588         this.iterateChildren(node, this.cleanWord);
21589         
21590         
21591         
21592     },
21593     /**
21594      * iterateChildren of a Node, calling fn each time, using this as the scole..
21595      * @param {DomNode} node node to iterate children of.
21596      * @param {Function} fn method of this class to call on each item.
21597      */
21598     iterateChildren : function(node, fn)
21599     {
21600         if (!node.childNodes.length) {
21601                 return;
21602         }
21603         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21604            fn.call(this, node.childNodes[i])
21605         }
21606     },
21607     
21608     
21609     /**
21610      * cleanTableWidths.
21611      *
21612      * Quite often pasting from word etc.. results in tables with column and widths.
21613      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21614      *
21615      */
21616     cleanTableWidths : function(node)
21617     {
21618          
21619          
21620         if (!node) {
21621             this.cleanTableWidths(this.doc.body);
21622             return;
21623         }
21624         
21625         // ignore list...
21626         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21627             return; 
21628         }
21629         Roo.log(node.tagName);
21630         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21631             this.iterateChildren(node, this.cleanTableWidths);
21632             return;
21633         }
21634         if (node.hasAttribute('width')) {
21635             node.removeAttribute('width');
21636         }
21637         
21638          
21639         if (node.hasAttribute("style")) {
21640             // pretty basic...
21641             
21642             var styles = node.getAttribute("style").split(";");
21643             var nstyle = [];
21644             Roo.each(styles, function(s) {
21645                 if (!s.match(/:/)) {
21646                     return;
21647                 }
21648                 var kv = s.split(":");
21649                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21650                     return;
21651                 }
21652                 // what ever is left... we allow.
21653                 nstyle.push(s);
21654             });
21655             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21656             if (!nstyle.length) {
21657                 node.removeAttribute('style');
21658             }
21659         }
21660         
21661         this.iterateChildren(node, this.cleanTableWidths);
21662         
21663         
21664     },
21665     
21666     
21667     
21668     
21669     domToHTML : function(currentElement, depth, nopadtext) {
21670         
21671         depth = depth || 0;
21672         nopadtext = nopadtext || false;
21673     
21674         if (!currentElement) {
21675             return this.domToHTML(this.doc.body);
21676         }
21677         
21678         //Roo.log(currentElement);
21679         var j;
21680         var allText = false;
21681         var nodeName = currentElement.nodeName;
21682         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21683         
21684         if  (nodeName == '#text') {
21685             
21686             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21687         }
21688         
21689         
21690         var ret = '';
21691         if (nodeName != 'BODY') {
21692              
21693             var i = 0;
21694             // Prints the node tagName, such as <A>, <IMG>, etc
21695             if (tagName) {
21696                 var attr = [];
21697                 for(i = 0; i < currentElement.attributes.length;i++) {
21698                     // quoting?
21699                     var aname = currentElement.attributes.item(i).name;
21700                     if (!currentElement.attributes.item(i).value.length) {
21701                         continue;
21702                     }
21703                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21704                 }
21705                 
21706                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21707             } 
21708             else {
21709                 
21710                 // eack
21711             }
21712         } else {
21713             tagName = false;
21714         }
21715         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21716             return ret;
21717         }
21718         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21719             nopadtext = true;
21720         }
21721         
21722         
21723         // Traverse the tree
21724         i = 0;
21725         var currentElementChild = currentElement.childNodes.item(i);
21726         var allText = true;
21727         var innerHTML  = '';
21728         lastnode = '';
21729         while (currentElementChild) {
21730             // Formatting code (indent the tree so it looks nice on the screen)
21731             var nopad = nopadtext;
21732             if (lastnode == 'SPAN') {
21733                 nopad  = true;
21734             }
21735             // text
21736             if  (currentElementChild.nodeName == '#text') {
21737                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21738                 toadd = nopadtext ? toadd : toadd.trim();
21739                 if (!nopad && toadd.length > 80) {
21740                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21741                 }
21742                 innerHTML  += toadd;
21743                 
21744                 i++;
21745                 currentElementChild = currentElement.childNodes.item(i);
21746                 lastNode = '';
21747                 continue;
21748             }
21749             allText = false;
21750             
21751             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21752                 
21753             // Recursively traverse the tree structure of the child node
21754             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21755             lastnode = currentElementChild.nodeName;
21756             i++;
21757             currentElementChild=currentElement.childNodes.item(i);
21758         }
21759         
21760         ret += innerHTML;
21761         
21762         if (!allText) {
21763                 // The remaining code is mostly for formatting the tree
21764             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21765         }
21766         
21767         
21768         if (tagName) {
21769             ret+= "</"+tagName+">";
21770         }
21771         return ret;
21772         
21773     },
21774         
21775     applyBlacklists : function()
21776     {
21777         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21778         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21779         
21780         this.white = [];
21781         this.black = [];
21782         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21783             if (b.indexOf(tag) > -1) {
21784                 return;
21785             }
21786             this.white.push(tag);
21787             
21788         }, this);
21789         
21790         Roo.each(w, function(tag) {
21791             if (b.indexOf(tag) > -1) {
21792                 return;
21793             }
21794             if (this.white.indexOf(tag) > -1) {
21795                 return;
21796             }
21797             this.white.push(tag);
21798             
21799         }, this);
21800         
21801         
21802         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21803             if (w.indexOf(tag) > -1) {
21804                 return;
21805             }
21806             this.black.push(tag);
21807             
21808         }, this);
21809         
21810         Roo.each(b, function(tag) {
21811             if (w.indexOf(tag) > -1) {
21812                 return;
21813             }
21814             if (this.black.indexOf(tag) > -1) {
21815                 return;
21816             }
21817             this.black.push(tag);
21818             
21819         }, this);
21820         
21821         
21822         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21823         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21824         
21825         this.cwhite = [];
21826         this.cblack = [];
21827         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21828             if (b.indexOf(tag) > -1) {
21829                 return;
21830             }
21831             this.cwhite.push(tag);
21832             
21833         }, this);
21834         
21835         Roo.each(w, function(tag) {
21836             if (b.indexOf(tag) > -1) {
21837                 return;
21838             }
21839             if (this.cwhite.indexOf(tag) > -1) {
21840                 return;
21841             }
21842             this.cwhite.push(tag);
21843             
21844         }, this);
21845         
21846         
21847         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21848             if (w.indexOf(tag) > -1) {
21849                 return;
21850             }
21851             this.cblack.push(tag);
21852             
21853         }, this);
21854         
21855         Roo.each(b, function(tag) {
21856             if (w.indexOf(tag) > -1) {
21857                 return;
21858             }
21859             if (this.cblack.indexOf(tag) > -1) {
21860                 return;
21861             }
21862             this.cblack.push(tag);
21863             
21864         }, this);
21865     },
21866     
21867     setStylesheets : function(stylesheets)
21868     {
21869         if(typeof(stylesheets) == 'string'){
21870             Roo.get(this.iframe.contentDocument.head).createChild({
21871                 tag : 'link',
21872                 rel : 'stylesheet',
21873                 type : 'text/css',
21874                 href : stylesheets
21875             });
21876             
21877             return;
21878         }
21879         var _this = this;
21880      
21881         Roo.each(stylesheets, function(s) {
21882             if(!s.length){
21883                 return;
21884             }
21885             
21886             Roo.get(_this.iframe.contentDocument.head).createChild({
21887                 tag : 'link',
21888                 rel : 'stylesheet',
21889                 type : 'text/css',
21890                 href : s
21891             });
21892         });
21893
21894         
21895     },
21896     
21897     removeStylesheets : function()
21898     {
21899         var _this = this;
21900         
21901         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21902             s.remove();
21903         });
21904     }
21905     
21906     // hide stuff that is not compatible
21907     /**
21908      * @event blur
21909      * @hide
21910      */
21911     /**
21912      * @event change
21913      * @hide
21914      */
21915     /**
21916      * @event focus
21917      * @hide
21918      */
21919     /**
21920      * @event specialkey
21921      * @hide
21922      */
21923     /**
21924      * @cfg {String} fieldClass @hide
21925      */
21926     /**
21927      * @cfg {String} focusClass @hide
21928      */
21929     /**
21930      * @cfg {String} autoCreate @hide
21931      */
21932     /**
21933      * @cfg {String} inputType @hide
21934      */
21935     /**
21936      * @cfg {String} invalidClass @hide
21937      */
21938     /**
21939      * @cfg {String} invalidText @hide
21940      */
21941     /**
21942      * @cfg {String} msgFx @hide
21943      */
21944     /**
21945      * @cfg {String} validateOnBlur @hide
21946      */
21947 });
21948
21949 Roo.HtmlEditorCore.white = [
21950         'area', 'br', 'img', 'input', 'hr', 'wbr',
21951         
21952        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21953        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21954        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21955        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21956        'table',   'ul',         'xmp', 
21957        
21958        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21959       'thead',   'tr', 
21960      
21961       'dir', 'menu', 'ol', 'ul', 'dl',
21962        
21963       'embed',  'object'
21964 ];
21965
21966
21967 Roo.HtmlEditorCore.black = [
21968     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21969         'applet', // 
21970         'base',   'basefont', 'bgsound', 'blink',  'body', 
21971         'frame',  'frameset', 'head',    'html',   'ilayer', 
21972         'iframe', 'layer',  'link',     'meta',    'object',   
21973         'script', 'style' ,'title',  'xml' // clean later..
21974 ];
21975 Roo.HtmlEditorCore.clean = [
21976     'script', 'style', 'title', 'xml'
21977 ];
21978 Roo.HtmlEditorCore.remove = [
21979     'font'
21980 ];
21981 // attributes..
21982
21983 Roo.HtmlEditorCore.ablack = [
21984     'on'
21985 ];
21986     
21987 Roo.HtmlEditorCore.aclean = [ 
21988     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21989 ];
21990
21991 // protocols..
21992 Roo.HtmlEditorCore.pwhite= [
21993         'http',  'https',  'mailto'
21994 ];
21995
21996 // white listed style attributes.
21997 Roo.HtmlEditorCore.cwhite= [
21998       //  'text-align', /// default is to allow most things..
21999       
22000          
22001 //        'font-size'//??
22002 ];
22003
22004 // black listed style attributes.
22005 Roo.HtmlEditorCore.cblack= [
22006       //  'font-size' -- this can be set by the project 
22007 ];
22008
22009
22010 Roo.HtmlEditorCore.swapCodes   =[ 
22011     [    8211, "--" ], 
22012     [    8212, "--" ], 
22013     [    8216,  "'" ],  
22014     [    8217, "'" ],  
22015     [    8220, '"' ],  
22016     [    8221, '"' ],  
22017     [    8226, "*" ],  
22018     [    8230, "..." ]
22019 ]; 
22020
22021     //<script type="text/javascript">
22022
22023 /*
22024  * Ext JS Library 1.1.1
22025  * Copyright(c) 2006-2007, Ext JS, LLC.
22026  * Licence LGPL
22027  * 
22028  */
22029  
22030  
22031 Roo.form.HtmlEditor = function(config){
22032     
22033     
22034     
22035     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22036     
22037     if (!this.toolbars) {
22038         this.toolbars = [];
22039     }
22040     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22041     
22042     
22043 };
22044
22045 /**
22046  * @class Roo.form.HtmlEditor
22047  * @extends Roo.form.Field
22048  * Provides a lightweight HTML Editor component.
22049  *
22050  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22051  * 
22052  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22053  * supported by this editor.</b><br/><br/>
22054  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22055  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22056  */
22057 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22058     /**
22059      * @cfg {Boolean} clearUp
22060      */
22061     clearUp : true,
22062       /**
22063      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22064      */
22065     toolbars : false,
22066    
22067      /**
22068      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22069      *                        Roo.resizable.
22070      */
22071     resizable : false,
22072      /**
22073      * @cfg {Number} height (in pixels)
22074      */   
22075     height: 300,
22076    /**
22077      * @cfg {Number} width (in pixels)
22078      */   
22079     width: 500,
22080     
22081     /**
22082      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22083      * 
22084      */
22085     stylesheets: false,
22086     
22087     
22088      /**
22089      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22090      * 
22091      */
22092     cblack: false,
22093     /**
22094      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22095      * 
22096      */
22097     cwhite: false,
22098     
22099      /**
22100      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22101      * 
22102      */
22103     black: false,
22104     /**
22105      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22106      * 
22107      */
22108     white: false,
22109     
22110     // id of frame..
22111     frameId: false,
22112     
22113     // private properties
22114     validationEvent : false,
22115     deferHeight: true,
22116     initialized : false,
22117     activated : false,
22118     
22119     onFocus : Roo.emptyFn,
22120     iframePad:3,
22121     hideMode:'offsets',
22122     
22123     actionMode : 'container', // defaults to hiding it...
22124     
22125     defaultAutoCreate : { // modified by initCompnoent..
22126         tag: "textarea",
22127         style:"width:500px;height:300px;",
22128         autocomplete: "new-password"
22129     },
22130
22131     // private
22132     initComponent : function(){
22133         this.addEvents({
22134             /**
22135              * @event initialize
22136              * Fires when the editor is fully initialized (including the iframe)
22137              * @param {HtmlEditor} this
22138              */
22139             initialize: true,
22140             /**
22141              * @event activate
22142              * Fires when the editor is first receives the focus. Any insertion must wait
22143              * until after this event.
22144              * @param {HtmlEditor} this
22145              */
22146             activate: true,
22147              /**
22148              * @event beforesync
22149              * Fires before the textarea is updated with content from the editor iframe. Return false
22150              * to cancel the sync.
22151              * @param {HtmlEditor} this
22152              * @param {String} html
22153              */
22154             beforesync: true,
22155              /**
22156              * @event beforepush
22157              * Fires before the iframe editor is updated with content from the textarea. Return false
22158              * to cancel the push.
22159              * @param {HtmlEditor} this
22160              * @param {String} html
22161              */
22162             beforepush: true,
22163              /**
22164              * @event sync
22165              * Fires when the textarea is updated with content from the editor iframe.
22166              * @param {HtmlEditor} this
22167              * @param {String} html
22168              */
22169             sync: true,
22170              /**
22171              * @event push
22172              * Fires when the iframe editor is updated with content from the textarea.
22173              * @param {HtmlEditor} this
22174              * @param {String} html
22175              */
22176             push: true,
22177              /**
22178              * @event editmodechange
22179              * Fires when the editor switches edit modes
22180              * @param {HtmlEditor} this
22181              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22182              */
22183             editmodechange: true,
22184             /**
22185              * @event editorevent
22186              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22187              * @param {HtmlEditor} this
22188              */
22189             editorevent: true,
22190             /**
22191              * @event firstfocus
22192              * Fires when on first focus - needed by toolbars..
22193              * @param {HtmlEditor} this
22194              */
22195             firstfocus: true,
22196             /**
22197              * @event autosave
22198              * Auto save the htmlEditor value as a file into Events
22199              * @param {HtmlEditor} this
22200              */
22201             autosave: true,
22202             /**
22203              * @event savedpreview
22204              * preview the saved version of htmlEditor
22205              * @param {HtmlEditor} this
22206              */
22207             savedpreview: true,
22208             
22209             /**
22210             * @event stylesheetsclick
22211             * Fires when press the Sytlesheets button
22212             * @param {Roo.HtmlEditorCore} this
22213             */
22214             stylesheetsclick: true
22215         });
22216         this.defaultAutoCreate =  {
22217             tag: "textarea",
22218             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22219             autocomplete: "new-password"
22220         };
22221     },
22222
22223     /**
22224      * Protected method that will not generally be called directly. It
22225      * is called when the editor creates its toolbar. Override this method if you need to
22226      * add custom toolbar buttons.
22227      * @param {HtmlEditor} editor
22228      */
22229     createToolbar : function(editor){
22230         Roo.log("create toolbars");
22231         if (!editor.toolbars || !editor.toolbars.length) {
22232             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22233         }
22234         
22235         for (var i =0 ; i < editor.toolbars.length;i++) {
22236             editor.toolbars[i] = Roo.factory(
22237                     typeof(editor.toolbars[i]) == 'string' ?
22238                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22239                 Roo.form.HtmlEditor);
22240             editor.toolbars[i].init(editor);
22241         }
22242          
22243         
22244     },
22245
22246      
22247     // private
22248     onRender : function(ct, position)
22249     {
22250         var _t = this;
22251         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22252         
22253         this.wrap = this.el.wrap({
22254             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22255         });
22256         
22257         this.editorcore.onRender(ct, position);
22258          
22259         if (this.resizable) {
22260             this.resizeEl = new Roo.Resizable(this.wrap, {
22261                 pinned : true,
22262                 wrap: true,
22263                 dynamic : true,
22264                 minHeight : this.height,
22265                 height: this.height,
22266                 handles : this.resizable,
22267                 width: this.width,
22268                 listeners : {
22269                     resize : function(r, w, h) {
22270                         _t.onResize(w,h); // -something
22271                     }
22272                 }
22273             });
22274             
22275         }
22276         this.createToolbar(this);
22277        
22278         
22279         if(!this.width){
22280             this.setSize(this.wrap.getSize());
22281         }
22282         if (this.resizeEl) {
22283             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22284             // should trigger onReize..
22285         }
22286         
22287         this.keyNav = new Roo.KeyNav(this.el, {
22288             
22289             "tab" : function(e){
22290                 e.preventDefault();
22291                 
22292                 var value = this.getValue();
22293                 
22294                 var start = this.el.dom.selectionStart;
22295                 var end = this.el.dom.selectionEnd;
22296                 
22297                 if(!e.shiftKey){
22298                     
22299                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22300                     this.el.dom.setSelectionRange(end + 1, end + 1);
22301                     return;
22302                 }
22303                 
22304                 var f = value.substring(0, start).split("\t");
22305                 
22306                 if(f.pop().length != 0){
22307                     return;
22308                 }
22309                 
22310                 this.setValue(f.join("\t") + value.substring(end));
22311                 this.el.dom.setSelectionRange(start - 1, start - 1);
22312                 
22313             },
22314             
22315             "home" : function(e){
22316                 e.preventDefault();
22317                 
22318                 var curr = this.el.dom.selectionStart;
22319                 var lines = this.getValue().split("\n");
22320                 
22321                 if(!lines.length){
22322                     return;
22323                 }
22324                 
22325                 if(e.ctrlKey){
22326                     this.el.dom.setSelectionRange(0, 0);
22327                     return;
22328                 }
22329                 
22330                 var pos = 0;
22331                 
22332                 for (var i = 0; i < lines.length;i++) {
22333                     pos += lines[i].length;
22334                     
22335                     if(i != 0){
22336                         pos += 1;
22337                     }
22338                     
22339                     if(pos < curr){
22340                         continue;
22341                     }
22342                     
22343                     pos -= lines[i].length;
22344                     
22345                     break;
22346                 }
22347                 
22348                 if(!e.shiftKey){
22349                     this.el.dom.setSelectionRange(pos, pos);
22350                     return;
22351                 }
22352                 
22353                 this.el.dom.selectionStart = pos;
22354                 this.el.dom.selectionEnd = curr;
22355             },
22356             
22357             "end" : function(e){
22358                 e.preventDefault();
22359                 
22360                 var curr = this.el.dom.selectionStart;
22361                 var lines = this.getValue().split("\n");
22362                 
22363                 if(!lines.length){
22364                     return;
22365                 }
22366                 
22367                 if(e.ctrlKey){
22368                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22369                     return;
22370                 }
22371                 
22372                 var pos = 0;
22373                 
22374                 for (var i = 0; i < lines.length;i++) {
22375                     
22376                     pos += lines[i].length;
22377                     
22378                     if(i != 0){
22379                         pos += 1;
22380                     }
22381                     
22382                     if(pos < curr){
22383                         continue;
22384                     }
22385                     
22386                     break;
22387                 }
22388                 
22389                 if(!e.shiftKey){
22390                     this.el.dom.setSelectionRange(pos, pos);
22391                     return;
22392                 }
22393                 
22394                 this.el.dom.selectionStart = curr;
22395                 this.el.dom.selectionEnd = pos;
22396             },
22397
22398             scope : this,
22399
22400             doRelay : function(foo, bar, hname){
22401                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22402             },
22403
22404             forceKeyDown: true
22405         });
22406         
22407 //        if(this.autosave && this.w){
22408 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22409 //        }
22410     },
22411
22412     // private
22413     onResize : function(w, h)
22414     {
22415         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22416         var ew = false;
22417         var eh = false;
22418         
22419         if(this.el ){
22420             if(typeof w == 'number'){
22421                 var aw = w - this.wrap.getFrameWidth('lr');
22422                 this.el.setWidth(this.adjustWidth('textarea', aw));
22423                 ew = aw;
22424             }
22425             if(typeof h == 'number'){
22426                 var tbh = 0;
22427                 for (var i =0; i < this.toolbars.length;i++) {
22428                     // fixme - ask toolbars for heights?
22429                     tbh += this.toolbars[i].tb.el.getHeight();
22430                     if (this.toolbars[i].footer) {
22431                         tbh += this.toolbars[i].footer.el.getHeight();
22432                     }
22433                 }
22434                 
22435                 
22436                 
22437                 
22438                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22439                 ah -= 5; // knock a few pixes off for look..
22440 //                Roo.log(ah);
22441                 this.el.setHeight(this.adjustWidth('textarea', ah));
22442                 var eh = ah;
22443             }
22444         }
22445         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22446         this.editorcore.onResize(ew,eh);
22447         
22448     },
22449
22450     /**
22451      * Toggles the editor between standard and source edit mode.
22452      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22453      */
22454     toggleSourceEdit : function(sourceEditMode)
22455     {
22456         this.editorcore.toggleSourceEdit(sourceEditMode);
22457         
22458         if(this.editorcore.sourceEditMode){
22459             Roo.log('editor - showing textarea');
22460             
22461 //            Roo.log('in');
22462 //            Roo.log(this.syncValue());
22463             this.editorcore.syncValue();
22464             this.el.removeClass('x-hidden');
22465             this.el.dom.removeAttribute('tabIndex');
22466             this.el.focus();
22467             
22468             for (var i = 0; i < this.toolbars.length; i++) {
22469                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22470                     this.toolbars[i].tb.hide();
22471                     this.toolbars[i].footer.hide();
22472                 }
22473             }
22474             
22475         }else{
22476             Roo.log('editor - hiding textarea');
22477 //            Roo.log('out')
22478 //            Roo.log(this.pushValue()); 
22479             this.editorcore.pushValue();
22480             
22481             this.el.addClass('x-hidden');
22482             this.el.dom.setAttribute('tabIndex', -1);
22483             
22484             for (var i = 0; i < this.toolbars.length; i++) {
22485                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22486                     this.toolbars[i].tb.show();
22487                     this.toolbars[i].footer.show();
22488                 }
22489             }
22490             
22491             //this.deferFocus();
22492         }
22493         
22494         this.setSize(this.wrap.getSize());
22495         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22496         
22497         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22498     },
22499  
22500     // private (for BoxComponent)
22501     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22502
22503     // private (for BoxComponent)
22504     getResizeEl : function(){
22505         return this.wrap;
22506     },
22507
22508     // private (for BoxComponent)
22509     getPositionEl : function(){
22510         return this.wrap;
22511     },
22512
22513     // private
22514     initEvents : function(){
22515         this.originalValue = this.getValue();
22516     },
22517
22518     /**
22519      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22520      * @method
22521      */
22522     markInvalid : Roo.emptyFn,
22523     /**
22524      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22525      * @method
22526      */
22527     clearInvalid : Roo.emptyFn,
22528
22529     setValue : function(v){
22530         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22531         this.editorcore.pushValue();
22532     },
22533
22534      
22535     // private
22536     deferFocus : function(){
22537         this.focus.defer(10, this);
22538     },
22539
22540     // doc'ed in Field
22541     focus : function(){
22542         this.editorcore.focus();
22543         
22544     },
22545       
22546
22547     // private
22548     onDestroy : function(){
22549         
22550         
22551         
22552         if(this.rendered){
22553             
22554             for (var i =0; i < this.toolbars.length;i++) {
22555                 // fixme - ask toolbars for heights?
22556                 this.toolbars[i].onDestroy();
22557             }
22558             
22559             this.wrap.dom.innerHTML = '';
22560             this.wrap.remove();
22561         }
22562     },
22563
22564     // private
22565     onFirstFocus : function(){
22566         //Roo.log("onFirstFocus");
22567         this.editorcore.onFirstFocus();
22568          for (var i =0; i < this.toolbars.length;i++) {
22569             this.toolbars[i].onFirstFocus();
22570         }
22571         
22572     },
22573     
22574     // private
22575     syncValue : function()
22576     {
22577         this.editorcore.syncValue();
22578     },
22579     
22580     pushValue : function()
22581     {
22582         this.editorcore.pushValue();
22583     },
22584     
22585     setStylesheets : function(stylesheets)
22586     {
22587         this.editorcore.setStylesheets(stylesheets);
22588     },
22589     
22590     removeStylesheets : function()
22591     {
22592         this.editorcore.removeStylesheets();
22593     }
22594      
22595     
22596     // hide stuff that is not compatible
22597     /**
22598      * @event blur
22599      * @hide
22600      */
22601     /**
22602      * @event change
22603      * @hide
22604      */
22605     /**
22606      * @event focus
22607      * @hide
22608      */
22609     /**
22610      * @event specialkey
22611      * @hide
22612      */
22613     /**
22614      * @cfg {String} fieldClass @hide
22615      */
22616     /**
22617      * @cfg {String} focusClass @hide
22618      */
22619     /**
22620      * @cfg {String} autoCreate @hide
22621      */
22622     /**
22623      * @cfg {String} inputType @hide
22624      */
22625     /**
22626      * @cfg {String} invalidClass @hide
22627      */
22628     /**
22629      * @cfg {String} invalidText @hide
22630      */
22631     /**
22632      * @cfg {String} msgFx @hide
22633      */
22634     /**
22635      * @cfg {String} validateOnBlur @hide
22636      */
22637 });
22638  
22639     // <script type="text/javascript">
22640 /*
22641  * Based on
22642  * Ext JS Library 1.1.1
22643  * Copyright(c) 2006-2007, Ext JS, LLC.
22644  *  
22645  
22646  */
22647
22648 /**
22649  * @class Roo.form.HtmlEditorToolbar1
22650  * Basic Toolbar
22651  * 
22652  * Usage:
22653  *
22654  new Roo.form.HtmlEditor({
22655     ....
22656     toolbars : [
22657         new Roo.form.HtmlEditorToolbar1({
22658             disable : { fonts: 1 , format: 1, ..., ... , ...],
22659             btns : [ .... ]
22660         })
22661     }
22662      
22663  * 
22664  * @cfg {Object} disable List of elements to disable..
22665  * @cfg {Array} btns List of additional buttons.
22666  * 
22667  * 
22668  * NEEDS Extra CSS? 
22669  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22670  */
22671  
22672 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22673 {
22674     
22675     Roo.apply(this, config);
22676     
22677     // default disabled, based on 'good practice'..
22678     this.disable = this.disable || {};
22679     Roo.applyIf(this.disable, {
22680         fontSize : true,
22681         colors : true,
22682         specialElements : true
22683     });
22684     
22685     
22686     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22687     // dont call parent... till later.
22688 }
22689
22690 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22691     
22692     tb: false,
22693     
22694     rendered: false,
22695     
22696     editor : false,
22697     editorcore : false,
22698     /**
22699      * @cfg {Object} disable  List of toolbar elements to disable
22700          
22701      */
22702     disable : false,
22703     
22704     
22705      /**
22706      * @cfg {String} createLinkText The default text for the create link prompt
22707      */
22708     createLinkText : 'Please enter the URL for the link:',
22709     /**
22710      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22711      */
22712     defaultLinkValue : 'http:/'+'/',
22713    
22714     
22715       /**
22716      * @cfg {Array} fontFamilies An array of available font families
22717      */
22718     fontFamilies : [
22719         'Arial',
22720         'Courier New',
22721         'Tahoma',
22722         'Times New Roman',
22723         'Verdana'
22724     ],
22725     
22726     specialChars : [
22727            "&#169;",
22728           "&#174;",     
22729           "&#8482;",    
22730           "&#163;" ,    
22731          // "&#8212;",    
22732           "&#8230;",    
22733           "&#247;" ,    
22734         //  "&#225;" ,     ?? a acute?
22735            "&#8364;"    , //Euro
22736        //   "&#8220;"    ,
22737         //  "&#8221;"    ,
22738         //  "&#8226;"    ,
22739           "&#176;"  //   , // degrees
22740
22741          // "&#233;"     , // e ecute
22742          // "&#250;"     , // u ecute?
22743     ],
22744     
22745     specialElements : [
22746         {
22747             text: "Insert Table",
22748             xtype: 'MenuItem',
22749             xns : Roo.Menu,
22750             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22751                 
22752         },
22753         {    
22754             text: "Insert Image",
22755             xtype: 'MenuItem',
22756             xns : Roo.Menu,
22757             ihtml : '<img src="about:blank"/>'
22758             
22759         }
22760         
22761          
22762     ],
22763     
22764     
22765     inputElements : [ 
22766             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22767             "input:submit", "input:button", "select", "textarea", "label" ],
22768     formats : [
22769         ["p"] ,  
22770         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22771         ["pre"],[ "code"], 
22772         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22773         ['div'],['span']
22774     ],
22775     
22776     cleanStyles : [
22777         "font-size"
22778     ],
22779      /**
22780      * @cfg {String} defaultFont default font to use.
22781      */
22782     defaultFont: 'tahoma',
22783    
22784     fontSelect : false,
22785     
22786     
22787     formatCombo : false,
22788     
22789     init : function(editor)
22790     {
22791         this.editor = editor;
22792         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22793         var editorcore = this.editorcore;
22794         
22795         var _t = this;
22796         
22797         var fid = editorcore.frameId;
22798         var etb = this;
22799         function btn(id, toggle, handler){
22800             var xid = fid + '-'+ id ;
22801             return {
22802                 id : xid,
22803                 cmd : id,
22804                 cls : 'x-btn-icon x-edit-'+id,
22805                 enableToggle:toggle !== false,
22806                 scope: _t, // was editor...
22807                 handler:handler||_t.relayBtnCmd,
22808                 clickEvent:'mousedown',
22809                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22810                 tabIndex:-1
22811             };
22812         }
22813         
22814         
22815         
22816         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22817         this.tb = tb;
22818          // stop form submits
22819         tb.el.on('click', function(e){
22820             e.preventDefault(); // what does this do?
22821         });
22822
22823         if(!this.disable.font) { // && !Roo.isSafari){
22824             /* why no safari for fonts 
22825             editor.fontSelect = tb.el.createChild({
22826                 tag:'select',
22827                 tabIndex: -1,
22828                 cls:'x-font-select',
22829                 html: this.createFontOptions()
22830             });
22831             
22832             editor.fontSelect.on('change', function(){
22833                 var font = editor.fontSelect.dom.value;
22834                 editor.relayCmd('fontname', font);
22835                 editor.deferFocus();
22836             }, editor);
22837             
22838             tb.add(
22839                 editor.fontSelect.dom,
22840                 '-'
22841             );
22842             */
22843             
22844         };
22845         if(!this.disable.formats){
22846             this.formatCombo = new Roo.form.ComboBox({
22847                 store: new Roo.data.SimpleStore({
22848                     id : 'tag',
22849                     fields: ['tag'],
22850                     data : this.formats // from states.js
22851                 }),
22852                 blockFocus : true,
22853                 name : '',
22854                 //autoCreate : {tag: "div",  size: "20"},
22855                 displayField:'tag',
22856                 typeAhead: false,
22857                 mode: 'local',
22858                 editable : false,
22859                 triggerAction: 'all',
22860                 emptyText:'Add tag',
22861                 selectOnFocus:true,
22862                 width:135,
22863                 listeners : {
22864                     'select': function(c, r, i) {
22865                         editorcore.insertTag(r.get('tag'));
22866                         editor.focus();
22867                     }
22868                 }
22869
22870             });
22871             tb.addField(this.formatCombo);
22872             
22873         }
22874         
22875         if(!this.disable.format){
22876             tb.add(
22877                 btn('bold'),
22878                 btn('italic'),
22879                 btn('underline'),
22880                 btn('strikethrough')
22881             );
22882         };
22883         if(!this.disable.fontSize){
22884             tb.add(
22885                 '-',
22886                 
22887                 
22888                 btn('increasefontsize', false, editorcore.adjustFont),
22889                 btn('decreasefontsize', false, editorcore.adjustFont)
22890             );
22891         };
22892         
22893         
22894         if(!this.disable.colors){
22895             tb.add(
22896                 '-', {
22897                     id:editorcore.frameId +'-forecolor',
22898                     cls:'x-btn-icon x-edit-forecolor',
22899                     clickEvent:'mousedown',
22900                     tooltip: this.buttonTips['forecolor'] || undefined,
22901                     tabIndex:-1,
22902                     menu : new Roo.menu.ColorMenu({
22903                         allowReselect: true,
22904                         focus: Roo.emptyFn,
22905                         value:'000000',
22906                         plain:true,
22907                         selectHandler: function(cp, color){
22908                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22909                             editor.deferFocus();
22910                         },
22911                         scope: editorcore,
22912                         clickEvent:'mousedown'
22913                     })
22914                 }, {
22915                     id:editorcore.frameId +'backcolor',
22916                     cls:'x-btn-icon x-edit-backcolor',
22917                     clickEvent:'mousedown',
22918                     tooltip: this.buttonTips['backcolor'] || undefined,
22919                     tabIndex:-1,
22920                     menu : new Roo.menu.ColorMenu({
22921                         focus: Roo.emptyFn,
22922                         value:'FFFFFF',
22923                         plain:true,
22924                         allowReselect: true,
22925                         selectHandler: function(cp, color){
22926                             if(Roo.isGecko){
22927                                 editorcore.execCmd('useCSS', false);
22928                                 editorcore.execCmd('hilitecolor', color);
22929                                 editorcore.execCmd('useCSS', true);
22930                                 editor.deferFocus();
22931                             }else{
22932                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22933                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22934                                 editor.deferFocus();
22935                             }
22936                         },
22937                         scope:editorcore,
22938                         clickEvent:'mousedown'
22939                     })
22940                 }
22941             );
22942         };
22943         // now add all the items...
22944         
22945
22946         if(!this.disable.alignments){
22947             tb.add(
22948                 '-',
22949                 btn('justifyleft'),
22950                 btn('justifycenter'),
22951                 btn('justifyright')
22952             );
22953         };
22954
22955         //if(!Roo.isSafari){
22956             if(!this.disable.links){
22957                 tb.add(
22958                     '-',
22959                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22960                 );
22961             };
22962
22963             if(!this.disable.lists){
22964                 tb.add(
22965                     '-',
22966                     btn('insertorderedlist'),
22967                     btn('insertunorderedlist')
22968                 );
22969             }
22970             if(!this.disable.sourceEdit){
22971                 tb.add(
22972                     '-',
22973                     btn('sourceedit', true, function(btn){
22974                         this.toggleSourceEdit(btn.pressed);
22975                     })
22976                 );
22977             }
22978         //}
22979         
22980         var smenu = { };
22981         // special menu.. - needs to be tidied up..
22982         if (!this.disable.special) {
22983             smenu = {
22984                 text: "&#169;",
22985                 cls: 'x-edit-none',
22986                 
22987                 menu : {
22988                     items : []
22989                 }
22990             };
22991             for (var i =0; i < this.specialChars.length; i++) {
22992                 smenu.menu.items.push({
22993                     
22994                     html: this.specialChars[i],
22995                     handler: function(a,b) {
22996                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
22997                         //editor.insertAtCursor(a.html);
22998                         
22999                     },
23000                     tabIndex:-1
23001                 });
23002             }
23003             
23004             
23005             tb.add(smenu);
23006             
23007             
23008         }
23009         
23010         var cmenu = { };
23011         if (!this.disable.cleanStyles) {
23012             cmenu = {
23013                 cls: 'x-btn-icon x-btn-clear',
23014                 
23015                 menu : {
23016                     items : []
23017                 }
23018             };
23019             for (var i =0; i < this.cleanStyles.length; i++) {
23020                 cmenu.menu.items.push({
23021                     actiontype : this.cleanStyles[i],
23022                     html: 'Remove ' + this.cleanStyles[i],
23023                     handler: function(a,b) {
23024 //                        Roo.log(a);
23025 //                        Roo.log(b);
23026                         var c = Roo.get(editorcore.doc.body);
23027                         c.select('[style]').each(function(s) {
23028                             s.dom.style.removeProperty(a.actiontype);
23029                         });
23030                         editorcore.syncValue();
23031                     },
23032                     tabIndex:-1
23033                 });
23034             }
23035              cmenu.menu.items.push({
23036                 actiontype : 'tablewidths',
23037                 html: 'Remove Table Widths',
23038                 handler: function(a,b) {
23039                     editorcore.cleanTableWidths();
23040                     editorcore.syncValue();
23041                 },
23042                 tabIndex:-1
23043             });
23044             cmenu.menu.items.push({
23045                 actiontype : 'word',
23046                 html: 'Remove MS Word Formating',
23047                 handler: function(a,b) {
23048                     editorcore.cleanWord();
23049                     editorcore.syncValue();
23050                 },
23051                 tabIndex:-1
23052             });
23053             
23054             cmenu.menu.items.push({
23055                 actiontype : 'all',
23056                 html: 'Remove All Styles',
23057                 handler: function(a,b) {
23058                     
23059                     var c = Roo.get(editorcore.doc.body);
23060                     c.select('[style]').each(function(s) {
23061                         s.dom.removeAttribute('style');
23062                     });
23063                     editorcore.syncValue();
23064                 },
23065                 tabIndex:-1
23066             });
23067             
23068             cmenu.menu.items.push({
23069                 actiontype : 'all',
23070                 html: 'Remove All CSS Classes',
23071                 handler: function(a,b) {
23072                     
23073                     var c = Roo.get(editorcore.doc.body);
23074                     c.select('[class]').each(function(s) {
23075                         s.dom.className = '';
23076                     });
23077                     editorcore.syncValue();
23078                 },
23079                 tabIndex:-1
23080             });
23081             
23082              cmenu.menu.items.push({
23083                 actiontype : 'tidy',
23084                 html: 'Tidy HTML Source',
23085                 handler: function(a,b) {
23086                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23087                     editorcore.syncValue();
23088                 },
23089                 tabIndex:-1
23090             });
23091             
23092             
23093             tb.add(cmenu);
23094         }
23095          
23096         if (!this.disable.specialElements) {
23097             var semenu = {
23098                 text: "Other;",
23099                 cls: 'x-edit-none',
23100                 menu : {
23101                     items : []
23102                 }
23103             };
23104             for (var i =0; i < this.specialElements.length; i++) {
23105                 semenu.menu.items.push(
23106                     Roo.apply({ 
23107                         handler: function(a,b) {
23108                             editor.insertAtCursor(this.ihtml);
23109                         }
23110                     }, this.specialElements[i])
23111                 );
23112                     
23113             }
23114             
23115             tb.add(semenu);
23116             
23117             
23118         }
23119          
23120         
23121         if (this.btns) {
23122             for(var i =0; i< this.btns.length;i++) {
23123                 var b = Roo.factory(this.btns[i],Roo.form);
23124                 b.cls =  'x-edit-none';
23125                 
23126                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23127                     b.cls += ' x-init-enable';
23128                 }
23129                 
23130                 b.scope = editorcore;
23131                 tb.add(b);
23132             }
23133         
23134         }
23135         
23136         
23137         
23138         // disable everything...
23139         
23140         this.tb.items.each(function(item){
23141             
23142            if(
23143                 item.id != editorcore.frameId+ '-sourceedit' && 
23144                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23145             ){
23146                 
23147                 item.disable();
23148             }
23149         });
23150         this.rendered = true;
23151         
23152         // the all the btns;
23153         editor.on('editorevent', this.updateToolbar, this);
23154         // other toolbars need to implement this..
23155         //editor.on('editmodechange', this.updateToolbar, this);
23156     },
23157     
23158     
23159     relayBtnCmd : function(btn) {
23160         this.editorcore.relayCmd(btn.cmd);
23161     },
23162     // private used internally
23163     createLink : function(){
23164         Roo.log("create link?");
23165         var url = prompt(this.createLinkText, this.defaultLinkValue);
23166         if(url && url != 'http:/'+'/'){
23167             this.editorcore.relayCmd('createlink', url);
23168         }
23169     },
23170
23171     
23172     /**
23173      * Protected method that will not generally be called directly. It triggers
23174      * a toolbar update by reading the markup state of the current selection in the editor.
23175      */
23176     updateToolbar: function(){
23177
23178         if(!this.editorcore.activated){
23179             this.editor.onFirstFocus();
23180             return;
23181         }
23182
23183         var btns = this.tb.items.map, 
23184             doc = this.editorcore.doc,
23185             frameId = this.editorcore.frameId;
23186
23187         if(!this.disable.font && !Roo.isSafari){
23188             /*
23189             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23190             if(name != this.fontSelect.dom.value){
23191                 this.fontSelect.dom.value = name;
23192             }
23193             */
23194         }
23195         if(!this.disable.format){
23196             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23197             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23198             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23199             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23200         }
23201         if(!this.disable.alignments){
23202             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23203             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23204             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23205         }
23206         if(!Roo.isSafari && !this.disable.lists){
23207             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23208             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23209         }
23210         
23211         var ans = this.editorcore.getAllAncestors();
23212         if (this.formatCombo) {
23213             
23214             
23215             var store = this.formatCombo.store;
23216             this.formatCombo.setValue("");
23217             for (var i =0; i < ans.length;i++) {
23218                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23219                     // select it..
23220                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23221                     break;
23222                 }
23223             }
23224         }
23225         
23226         
23227         
23228         // hides menus... - so this cant be on a menu...
23229         Roo.menu.MenuMgr.hideAll();
23230
23231         //this.editorsyncValue();
23232     },
23233    
23234     
23235     createFontOptions : function(){
23236         var buf = [], fs = this.fontFamilies, ff, lc;
23237         
23238         
23239         
23240         for(var i = 0, len = fs.length; i< len; i++){
23241             ff = fs[i];
23242             lc = ff.toLowerCase();
23243             buf.push(
23244                 '<option value="',lc,'" style="font-family:',ff,';"',
23245                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23246                     ff,
23247                 '</option>'
23248             );
23249         }
23250         return buf.join('');
23251     },
23252     
23253     toggleSourceEdit : function(sourceEditMode){
23254         
23255         Roo.log("toolbar toogle");
23256         if(sourceEditMode === undefined){
23257             sourceEditMode = !this.sourceEditMode;
23258         }
23259         this.sourceEditMode = sourceEditMode === true;
23260         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23261         // just toggle the button?
23262         if(btn.pressed !== this.sourceEditMode){
23263             btn.toggle(this.sourceEditMode);
23264             return;
23265         }
23266         
23267         if(sourceEditMode){
23268             Roo.log("disabling buttons");
23269             this.tb.items.each(function(item){
23270                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23271                     item.disable();
23272                 }
23273             });
23274           
23275         }else{
23276             Roo.log("enabling buttons");
23277             if(this.editorcore.initialized){
23278                 this.tb.items.each(function(item){
23279                     item.enable();
23280                 });
23281             }
23282             
23283         }
23284         Roo.log("calling toggole on editor");
23285         // tell the editor that it's been pressed..
23286         this.editor.toggleSourceEdit(sourceEditMode);
23287        
23288     },
23289      /**
23290      * Object collection of toolbar tooltips for the buttons in the editor. The key
23291      * is the command id associated with that button and the value is a valid QuickTips object.
23292      * For example:
23293 <pre><code>
23294 {
23295     bold : {
23296         title: 'Bold (Ctrl+B)',
23297         text: 'Make the selected text bold.',
23298         cls: 'x-html-editor-tip'
23299     },
23300     italic : {
23301         title: 'Italic (Ctrl+I)',
23302         text: 'Make the selected text italic.',
23303         cls: 'x-html-editor-tip'
23304     },
23305     ...
23306 </code></pre>
23307     * @type Object
23308      */
23309     buttonTips : {
23310         bold : {
23311             title: 'Bold (Ctrl+B)',
23312             text: 'Make the selected text bold.',
23313             cls: 'x-html-editor-tip'
23314         },
23315         italic : {
23316             title: 'Italic (Ctrl+I)',
23317             text: 'Make the selected text italic.',
23318             cls: 'x-html-editor-tip'
23319         },
23320         underline : {
23321             title: 'Underline (Ctrl+U)',
23322             text: 'Underline the selected text.',
23323             cls: 'x-html-editor-tip'
23324         },
23325         strikethrough : {
23326             title: 'Strikethrough',
23327             text: 'Strikethrough the selected text.',
23328             cls: 'x-html-editor-tip'
23329         },
23330         increasefontsize : {
23331             title: 'Grow Text',
23332             text: 'Increase the font size.',
23333             cls: 'x-html-editor-tip'
23334         },
23335         decreasefontsize : {
23336             title: 'Shrink Text',
23337             text: 'Decrease the font size.',
23338             cls: 'x-html-editor-tip'
23339         },
23340         backcolor : {
23341             title: 'Text Highlight Color',
23342             text: 'Change the background color of the selected text.',
23343             cls: 'x-html-editor-tip'
23344         },
23345         forecolor : {
23346             title: 'Font Color',
23347             text: 'Change the color of the selected text.',
23348             cls: 'x-html-editor-tip'
23349         },
23350         justifyleft : {
23351             title: 'Align Text Left',
23352             text: 'Align text to the left.',
23353             cls: 'x-html-editor-tip'
23354         },
23355         justifycenter : {
23356             title: 'Center Text',
23357             text: 'Center text in the editor.',
23358             cls: 'x-html-editor-tip'
23359         },
23360         justifyright : {
23361             title: 'Align Text Right',
23362             text: 'Align text to the right.',
23363             cls: 'x-html-editor-tip'
23364         },
23365         insertunorderedlist : {
23366             title: 'Bullet List',
23367             text: 'Start a bulleted list.',
23368             cls: 'x-html-editor-tip'
23369         },
23370         insertorderedlist : {
23371             title: 'Numbered List',
23372             text: 'Start a numbered list.',
23373             cls: 'x-html-editor-tip'
23374         },
23375         createlink : {
23376             title: 'Hyperlink',
23377             text: 'Make the selected text a hyperlink.',
23378             cls: 'x-html-editor-tip'
23379         },
23380         sourceedit : {
23381             title: 'Source Edit',
23382             text: 'Switch to source editing mode.',
23383             cls: 'x-html-editor-tip'
23384         }
23385     },
23386     // private
23387     onDestroy : function(){
23388         if(this.rendered){
23389             
23390             this.tb.items.each(function(item){
23391                 if(item.menu){
23392                     item.menu.removeAll();
23393                     if(item.menu.el){
23394                         item.menu.el.destroy();
23395                     }
23396                 }
23397                 item.destroy();
23398             });
23399              
23400         }
23401     },
23402     onFirstFocus: function() {
23403         this.tb.items.each(function(item){
23404            item.enable();
23405         });
23406     }
23407 });
23408
23409
23410
23411
23412 // <script type="text/javascript">
23413 /*
23414  * Based on
23415  * Ext JS Library 1.1.1
23416  * Copyright(c) 2006-2007, Ext JS, LLC.
23417  *  
23418  
23419  */
23420
23421  
23422 /**
23423  * @class Roo.form.HtmlEditor.ToolbarContext
23424  * Context Toolbar
23425  * 
23426  * Usage:
23427  *
23428  new Roo.form.HtmlEditor({
23429     ....
23430     toolbars : [
23431         { xtype: 'ToolbarStandard', styles : {} }
23432         { xtype: 'ToolbarContext', disable : {} }
23433     ]
23434 })
23435
23436      
23437  * 
23438  * @config : {Object} disable List of elements to disable.. (not done yet.)
23439  * @config : {Object} styles  Map of styles available.
23440  * 
23441  */
23442
23443 Roo.form.HtmlEditor.ToolbarContext = function(config)
23444 {
23445     
23446     Roo.apply(this, config);
23447     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23448     // dont call parent... till later.
23449     this.styles = this.styles || {};
23450 }
23451
23452  
23453
23454 Roo.form.HtmlEditor.ToolbarContext.types = {
23455     'IMG' : {
23456         width : {
23457             title: "Width",
23458             width: 40
23459         },
23460         height:  {
23461             title: "Height",
23462             width: 40
23463         },
23464         align: {
23465             title: "Align",
23466             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23467             width : 80
23468             
23469         },
23470         border: {
23471             title: "Border",
23472             width: 40
23473         },
23474         alt: {
23475             title: "Alt",
23476             width: 120
23477         },
23478         src : {
23479             title: "Src",
23480             width: 220
23481         }
23482         
23483     },
23484     'A' : {
23485         name : {
23486             title: "Name",
23487             width: 50
23488         },
23489         target:  {
23490             title: "Target",
23491             width: 120
23492         },
23493         href:  {
23494             title: "Href",
23495             width: 220
23496         } // border?
23497         
23498     },
23499     'TABLE' : {
23500         rows : {
23501             title: "Rows",
23502             width: 20
23503         },
23504         cols : {
23505             title: "Cols",
23506             width: 20
23507         },
23508         width : {
23509             title: "Width",
23510             width: 40
23511         },
23512         height : {
23513             title: "Height",
23514             width: 40
23515         },
23516         border : {
23517             title: "Border",
23518             width: 20
23519         }
23520     },
23521     'TD' : {
23522         width : {
23523             title: "Width",
23524             width: 40
23525         },
23526         height : {
23527             title: "Height",
23528             width: 40
23529         },   
23530         align: {
23531             title: "Align",
23532             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23533             width: 80
23534         },
23535         valign: {
23536             title: "Valign",
23537             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23538             width: 80
23539         },
23540         colspan: {
23541             title: "Colspan",
23542             width: 20
23543             
23544         },
23545          'font-family'  : {
23546             title : "Font",
23547             style : 'fontFamily',
23548             displayField: 'display',
23549             optname : 'font-family',
23550             width: 140
23551         }
23552     },
23553     'INPUT' : {
23554         name : {
23555             title: "name",
23556             width: 120
23557         },
23558         value : {
23559             title: "Value",
23560             width: 120
23561         },
23562         width : {
23563             title: "Width",
23564             width: 40
23565         }
23566     },
23567     'LABEL' : {
23568         'for' : {
23569             title: "For",
23570             width: 120
23571         }
23572     },
23573     'TEXTAREA' : {
23574           name : {
23575             title: "name",
23576             width: 120
23577         },
23578         rows : {
23579             title: "Rows",
23580             width: 20
23581         },
23582         cols : {
23583             title: "Cols",
23584             width: 20
23585         }
23586     },
23587     'SELECT' : {
23588         name : {
23589             title: "name",
23590             width: 120
23591         },
23592         selectoptions : {
23593             title: "Options",
23594             width: 200
23595         }
23596     },
23597     
23598     // should we really allow this??
23599     // should this just be 
23600     'BODY' : {
23601         title : {
23602             title: "Title",
23603             width: 200,
23604             disabled : true
23605         }
23606     },
23607     'SPAN' : {
23608         'font-family'  : {
23609             title : "Font",
23610             style : 'fontFamily',
23611             displayField: 'display',
23612             optname : 'font-family',
23613             width: 140
23614         }
23615     },
23616     'DIV' : {
23617         'font-family'  : {
23618             title : "Font",
23619             style : 'fontFamily',
23620             displayField: 'display',
23621             optname : 'font-family',
23622             width: 140
23623         }
23624     },
23625      'P' : {
23626         'font-family'  : {
23627             title : "Font",
23628             style : 'fontFamily',
23629             displayField: 'display',
23630             optname : 'font-family',
23631             width: 140
23632         }
23633     },
23634     
23635     '*' : {
23636         // empty..
23637     }
23638
23639 };
23640
23641 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23642 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23643
23644 Roo.form.HtmlEditor.ToolbarContext.options = {
23645         'font-family'  : [ 
23646                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23647                 [ 'Courier New', 'Courier New'],
23648                 [ 'Tahoma', 'Tahoma'],
23649                 [ 'Times New Roman,serif', 'Times'],
23650                 [ 'Verdana','Verdana' ]
23651         ]
23652 };
23653
23654 // fixme - these need to be configurable..
23655  
23656
23657 //Roo.form.HtmlEditor.ToolbarContext.types
23658
23659
23660 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23661     
23662     tb: false,
23663     
23664     rendered: false,
23665     
23666     editor : false,
23667     editorcore : false,
23668     /**
23669      * @cfg {Object} disable  List of toolbar elements to disable
23670          
23671      */
23672     disable : false,
23673     /**
23674      * @cfg {Object} styles List of styles 
23675      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23676      *
23677      * These must be defined in the page, so they get rendered correctly..
23678      * .headline { }
23679      * TD.underline { }
23680      * 
23681      */
23682     styles : false,
23683     
23684     options: false,
23685     
23686     toolbars : false,
23687     
23688     init : function(editor)
23689     {
23690         this.editor = editor;
23691         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23692         var editorcore = this.editorcore;
23693         
23694         var fid = editorcore.frameId;
23695         var etb = this;
23696         function btn(id, toggle, handler){
23697             var xid = fid + '-'+ id ;
23698             return {
23699                 id : xid,
23700                 cmd : id,
23701                 cls : 'x-btn-icon x-edit-'+id,
23702                 enableToggle:toggle !== false,
23703                 scope: editorcore, // was editor...
23704                 handler:handler||editorcore.relayBtnCmd,
23705                 clickEvent:'mousedown',
23706                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23707                 tabIndex:-1
23708             };
23709         }
23710         // create a new element.
23711         var wdiv = editor.wrap.createChild({
23712                 tag: 'div'
23713             }, editor.wrap.dom.firstChild.nextSibling, true);
23714         
23715         // can we do this more than once??
23716         
23717          // stop form submits
23718       
23719  
23720         // disable everything...
23721         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23722         this.toolbars = {};
23723            
23724         for (var i in  ty) {
23725           
23726             this.toolbars[i] = this.buildToolbar(ty[i],i);
23727         }
23728         this.tb = this.toolbars.BODY;
23729         this.tb.el.show();
23730         this.buildFooter();
23731         this.footer.show();
23732         editor.on('hide', function( ) { this.footer.hide() }, this);
23733         editor.on('show', function( ) { this.footer.show() }, this);
23734         
23735          
23736         this.rendered = true;
23737         
23738         // the all the btns;
23739         editor.on('editorevent', this.updateToolbar, this);
23740         // other toolbars need to implement this..
23741         //editor.on('editmodechange', this.updateToolbar, this);
23742     },
23743     
23744     
23745     
23746     /**
23747      * Protected method that will not generally be called directly. It triggers
23748      * a toolbar update by reading the markup state of the current selection in the editor.
23749      *
23750      * Note you can force an update by calling on('editorevent', scope, false)
23751      */
23752     updateToolbar: function(editor,ev,sel){
23753
23754         //Roo.log(ev);
23755         // capture mouse up - this is handy for selecting images..
23756         // perhaps should go somewhere else...
23757         if(!this.editorcore.activated){
23758              this.editor.onFirstFocus();
23759             return;
23760         }
23761         
23762         
23763         
23764         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23765         // selectNode - might want to handle IE?
23766         if (ev &&
23767             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23768             ev.target && ev.target.tagName == 'IMG') {
23769             // they have click on an image...
23770             // let's see if we can change the selection...
23771             sel = ev.target;
23772          
23773               var nodeRange = sel.ownerDocument.createRange();
23774             try {
23775                 nodeRange.selectNode(sel);
23776             } catch (e) {
23777                 nodeRange.selectNodeContents(sel);
23778             }
23779             //nodeRange.collapse(true);
23780             var s = this.editorcore.win.getSelection();
23781             s.removeAllRanges();
23782             s.addRange(nodeRange);
23783         }  
23784         
23785       
23786         var updateFooter = sel ? false : true;
23787         
23788         
23789         var ans = this.editorcore.getAllAncestors();
23790         
23791         // pick
23792         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23793         
23794         if (!sel) { 
23795             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23796             sel = sel ? sel : this.editorcore.doc.body;
23797             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23798             
23799         }
23800         // pick a menu that exists..
23801         var tn = sel.tagName.toUpperCase();
23802         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23803         
23804         tn = sel.tagName.toUpperCase();
23805         
23806         var lastSel = this.tb.selectedNode;
23807         
23808         this.tb.selectedNode = sel;
23809         
23810         // if current menu does not match..
23811         
23812         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23813                 
23814             this.tb.el.hide();
23815             ///console.log("show: " + tn);
23816             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23817             this.tb.el.show();
23818             // update name
23819             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23820             
23821             
23822             // update attributes
23823             if (this.tb.fields) {
23824                 this.tb.fields.each(function(e) {
23825                     if (e.stylename) {
23826                         e.setValue(sel.style[e.stylename]);
23827                         return;
23828                     } 
23829                    e.setValue(sel.getAttribute(e.attrname));
23830                 });
23831             }
23832             
23833             var hasStyles = false;
23834             for(var i in this.styles) {
23835                 hasStyles = true;
23836                 break;
23837             }
23838             
23839             // update styles
23840             if (hasStyles) { 
23841                 var st = this.tb.fields.item(0);
23842                 
23843                 st.store.removeAll();
23844                
23845                 
23846                 var cn = sel.className.split(/\s+/);
23847                 
23848                 var avs = [];
23849                 if (this.styles['*']) {
23850                     
23851                     Roo.each(this.styles['*'], function(v) {
23852                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23853                     });
23854                 }
23855                 if (this.styles[tn]) { 
23856                     Roo.each(this.styles[tn], function(v) {
23857                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23858                     });
23859                 }
23860                 
23861                 st.store.loadData(avs);
23862                 st.collapse();
23863                 st.setValue(cn);
23864             }
23865             // flag our selected Node.
23866             this.tb.selectedNode = sel;
23867            
23868            
23869             Roo.menu.MenuMgr.hideAll();
23870
23871         }
23872         
23873         if (!updateFooter) {
23874             //this.footDisp.dom.innerHTML = ''; 
23875             return;
23876         }
23877         // update the footer
23878         //
23879         var html = '';
23880         
23881         this.footerEls = ans.reverse();
23882         Roo.each(this.footerEls, function(a,i) {
23883             if (!a) { return; }
23884             html += html.length ? ' &gt; '  :  '';
23885             
23886             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23887             
23888         });
23889        
23890         // 
23891         var sz = this.footDisp.up('td').getSize();
23892         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23893         this.footDisp.dom.style.marginLeft = '5px';
23894         
23895         this.footDisp.dom.style.overflow = 'hidden';
23896         
23897         this.footDisp.dom.innerHTML = html;
23898             
23899         //this.editorsyncValue();
23900     },
23901      
23902     
23903    
23904        
23905     // private
23906     onDestroy : function(){
23907         if(this.rendered){
23908             
23909             this.tb.items.each(function(item){
23910                 if(item.menu){
23911                     item.menu.removeAll();
23912                     if(item.menu.el){
23913                         item.menu.el.destroy();
23914                     }
23915                 }
23916                 item.destroy();
23917             });
23918              
23919         }
23920     },
23921     onFirstFocus: function() {
23922         // need to do this for all the toolbars..
23923         this.tb.items.each(function(item){
23924            item.enable();
23925         });
23926     },
23927     buildToolbar: function(tlist, nm)
23928     {
23929         var editor = this.editor;
23930         var editorcore = this.editorcore;
23931          // create a new element.
23932         var wdiv = editor.wrap.createChild({
23933                 tag: 'div'
23934             }, editor.wrap.dom.firstChild.nextSibling, true);
23935         
23936        
23937         var tb = new Roo.Toolbar(wdiv);
23938         // add the name..
23939         
23940         tb.add(nm+ ":&nbsp;");
23941         
23942         var styles = [];
23943         for(var i in this.styles) {
23944             styles.push(i);
23945         }
23946         
23947         // styles...
23948         if (styles && styles.length) {
23949             
23950             // this needs a multi-select checkbox...
23951             tb.addField( new Roo.form.ComboBox({
23952                 store: new Roo.data.SimpleStore({
23953                     id : 'val',
23954                     fields: ['val', 'selected'],
23955                     data : [] 
23956                 }),
23957                 name : '-roo-edit-className',
23958                 attrname : 'className',
23959                 displayField: 'val',
23960                 typeAhead: false,
23961                 mode: 'local',
23962                 editable : false,
23963                 triggerAction: 'all',
23964                 emptyText:'Select Style',
23965                 selectOnFocus:true,
23966                 width: 130,
23967                 listeners : {
23968                     'select': function(c, r, i) {
23969                         // initial support only for on class per el..
23970                         tb.selectedNode.className =  r ? r.get('val') : '';
23971                         editorcore.syncValue();
23972                     }
23973                 }
23974     
23975             }));
23976         }
23977         
23978         var tbc = Roo.form.HtmlEditor.ToolbarContext;
23979         var tbops = tbc.options;
23980         
23981         for (var i in tlist) {
23982             
23983             var item = tlist[i];
23984             tb.add(item.title + ":&nbsp;");
23985             
23986             
23987             //optname == used so you can configure the options available..
23988             var opts = item.opts ? item.opts : false;
23989             if (item.optname) {
23990                 opts = tbops[item.optname];
23991            
23992             }
23993             
23994             if (opts) {
23995                 // opts == pulldown..
23996                 tb.addField( new Roo.form.ComboBox({
23997                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
23998                         id : 'val',
23999                         fields: ['val', 'display'],
24000                         data : opts  
24001                     }),
24002                     name : '-roo-edit-' + i,
24003                     attrname : i,
24004                     stylename : item.style ? item.style : false,
24005                     displayField: item.displayField ? item.displayField : 'val',
24006                     valueField :  'val',
24007                     typeAhead: false,
24008                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24009                     editable : false,
24010                     triggerAction: 'all',
24011                     emptyText:'Select',
24012                     selectOnFocus:true,
24013                     width: item.width ? item.width  : 130,
24014                     listeners : {
24015                         'select': function(c, r, i) {
24016                             if (c.stylename) {
24017                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24018                                 return;
24019                             }
24020                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24021                         }
24022                     }
24023
24024                 }));
24025                 continue;
24026                     
24027                  
24028                 
24029                 tb.addField( new Roo.form.TextField({
24030                     name: i,
24031                     width: 100,
24032                     //allowBlank:false,
24033                     value: ''
24034                 }));
24035                 continue;
24036             }
24037             tb.addField( new Roo.form.TextField({
24038                 name: '-roo-edit-' + i,
24039                 attrname : i,
24040                 
24041                 width: item.width,
24042                 //allowBlank:true,
24043                 value: '',
24044                 listeners: {
24045                     'change' : function(f, nv, ov) {
24046                         tb.selectedNode.setAttribute(f.attrname, nv);
24047                         editorcore.syncValue();
24048                     }
24049                 }
24050             }));
24051              
24052         }
24053         
24054         var _this = this;
24055         
24056         if(nm == 'BODY'){
24057             tb.addSeparator();
24058         
24059             tb.addButton( {
24060                 text: 'Stylesheets',
24061
24062                 listeners : {
24063                     click : function ()
24064                     {
24065                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24066                     }
24067                 }
24068             });
24069         }
24070         
24071         tb.addFill();
24072         tb.addButton( {
24073             text: 'Remove Tag',
24074     
24075             listeners : {
24076                 click : function ()
24077                 {
24078                     // remove
24079                     // undo does not work.
24080                      
24081                     var sn = tb.selectedNode;
24082                     
24083                     var pn = sn.parentNode;
24084                     
24085                     var stn =  sn.childNodes[0];
24086                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24087                     while (sn.childNodes.length) {
24088                         var node = sn.childNodes[0];
24089                         sn.removeChild(node);
24090                         //Roo.log(node);
24091                         pn.insertBefore(node, sn);
24092                         
24093                     }
24094                     pn.removeChild(sn);
24095                     var range = editorcore.createRange();
24096         
24097                     range.setStart(stn,0);
24098                     range.setEnd(en,0); //????
24099                     //range.selectNode(sel);
24100                     
24101                     
24102                     var selection = editorcore.getSelection();
24103                     selection.removeAllRanges();
24104                     selection.addRange(range);
24105                     
24106                     
24107                     
24108                     //_this.updateToolbar(null, null, pn);
24109                     _this.updateToolbar(null, null, null);
24110                     _this.footDisp.dom.innerHTML = ''; 
24111                 }
24112             }
24113             
24114                     
24115                 
24116             
24117         });
24118         
24119         
24120         tb.el.on('click', function(e){
24121             e.preventDefault(); // what does this do?
24122         });
24123         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24124         tb.el.hide();
24125         tb.name = nm;
24126         // dont need to disable them... as they will get hidden
24127         return tb;
24128          
24129         
24130     },
24131     buildFooter : function()
24132     {
24133         
24134         var fel = this.editor.wrap.createChild();
24135         this.footer = new Roo.Toolbar(fel);
24136         // toolbar has scrolly on left / right?
24137         var footDisp= new Roo.Toolbar.Fill();
24138         var _t = this;
24139         this.footer.add(
24140             {
24141                 text : '&lt;',
24142                 xtype: 'Button',
24143                 handler : function() {
24144                     _t.footDisp.scrollTo('left',0,true)
24145                 }
24146             }
24147         );
24148         this.footer.add( footDisp );
24149         this.footer.add( 
24150             {
24151                 text : '&gt;',
24152                 xtype: 'Button',
24153                 handler : function() {
24154                     // no animation..
24155                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24156                 }
24157             }
24158         );
24159         var fel = Roo.get(footDisp.el);
24160         fel.addClass('x-editor-context');
24161         this.footDispWrap = fel; 
24162         this.footDispWrap.overflow  = 'hidden';
24163         
24164         this.footDisp = fel.createChild();
24165         this.footDispWrap.on('click', this.onContextClick, this)
24166         
24167         
24168     },
24169     onContextClick : function (ev,dom)
24170     {
24171         ev.preventDefault();
24172         var  cn = dom.className;
24173         //Roo.log(cn);
24174         if (!cn.match(/x-ed-loc-/)) {
24175             return;
24176         }
24177         var n = cn.split('-').pop();
24178         var ans = this.footerEls;
24179         var sel = ans[n];
24180         
24181          // pick
24182         var range = this.editorcore.createRange();
24183         
24184         range.selectNodeContents(sel);
24185         //range.selectNode(sel);
24186         
24187         
24188         var selection = this.editorcore.getSelection();
24189         selection.removeAllRanges();
24190         selection.addRange(range);
24191         
24192         
24193         
24194         this.updateToolbar(null, null, sel);
24195         
24196         
24197     }
24198     
24199     
24200     
24201     
24202     
24203 });
24204
24205
24206
24207
24208
24209 /*
24210  * Based on:
24211  * Ext JS Library 1.1.1
24212  * Copyright(c) 2006-2007, Ext JS, LLC.
24213  *
24214  * Originally Released Under LGPL - original licence link has changed is not relivant.
24215  *
24216  * Fork - LGPL
24217  * <script type="text/javascript">
24218  */
24219  
24220 /**
24221  * @class Roo.form.BasicForm
24222  * @extends Roo.util.Observable
24223  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24224  * @constructor
24225  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24226  * @param {Object} config Configuration options
24227  */
24228 Roo.form.BasicForm = function(el, config){
24229     this.allItems = [];
24230     this.childForms = [];
24231     Roo.apply(this, config);
24232     /*
24233      * The Roo.form.Field items in this form.
24234      * @type MixedCollection
24235      */
24236      
24237      
24238     this.items = new Roo.util.MixedCollection(false, function(o){
24239         return o.id || (o.id = Roo.id());
24240     });
24241     this.addEvents({
24242         /**
24243          * @event beforeaction
24244          * Fires before any action is performed. Return false to cancel the action.
24245          * @param {Form} this
24246          * @param {Action} action The action to be performed
24247          */
24248         beforeaction: true,
24249         /**
24250          * @event actionfailed
24251          * Fires when an action fails.
24252          * @param {Form} this
24253          * @param {Action} action The action that failed
24254          */
24255         actionfailed : true,
24256         /**
24257          * @event actioncomplete
24258          * Fires when an action is completed.
24259          * @param {Form} this
24260          * @param {Action} action The action that completed
24261          */
24262         actioncomplete : true
24263     });
24264     if(el){
24265         this.initEl(el);
24266     }
24267     Roo.form.BasicForm.superclass.constructor.call(this);
24268 };
24269
24270 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24271     /**
24272      * @cfg {String} method
24273      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24274      */
24275     /**
24276      * @cfg {DataReader} reader
24277      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24278      * This is optional as there is built-in support for processing JSON.
24279      */
24280     /**
24281      * @cfg {DataReader} errorReader
24282      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24283      * This is completely optional as there is built-in support for processing JSON.
24284      */
24285     /**
24286      * @cfg {String} url
24287      * The URL to use for form actions if one isn't supplied in the action options.
24288      */
24289     /**
24290      * @cfg {Boolean} fileUpload
24291      * Set to true if this form is a file upload.
24292      */
24293      
24294     /**
24295      * @cfg {Object} baseParams
24296      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24297      */
24298      /**
24299      
24300     /**
24301      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24302      */
24303     timeout: 30,
24304
24305     // private
24306     activeAction : null,
24307
24308     /**
24309      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24310      * or setValues() data instead of when the form was first created.
24311      */
24312     trackResetOnLoad : false,
24313     
24314     
24315     /**
24316      * childForms - used for multi-tab forms
24317      * @type {Array}
24318      */
24319     childForms : false,
24320     
24321     /**
24322      * allItems - full list of fields.
24323      * @type {Array}
24324      */
24325     allItems : false,
24326     
24327     /**
24328      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24329      * element by passing it or its id or mask the form itself by passing in true.
24330      * @type Mixed
24331      */
24332     waitMsgTarget : false,
24333
24334     // private
24335     initEl : function(el){
24336         this.el = Roo.get(el);
24337         this.id = this.el.id || Roo.id();
24338         this.el.on('submit', this.onSubmit, this);
24339         this.el.addClass('x-form');
24340     },
24341
24342     // private
24343     onSubmit : function(e){
24344         e.stopEvent();
24345     },
24346
24347     /**
24348      * Returns true if client-side validation on the form is successful.
24349      * @return Boolean
24350      */
24351     isValid : function(){
24352         var valid = true;
24353         this.items.each(function(f){
24354            if(!f.validate()){
24355                valid = false;
24356            }
24357         });
24358         return valid;
24359     },
24360
24361     /**
24362      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24363      * @return Boolean
24364      */
24365     isDirty : function(){
24366         var dirty = false;
24367         this.items.each(function(f){
24368            if(f.isDirty()){
24369                dirty = true;
24370                return false;
24371            }
24372         });
24373         return dirty;
24374     },
24375     
24376     /**
24377      * Returns true if any fields in this form have changed since their original load. (New version)
24378      * @return Boolean
24379      */
24380     
24381     hasChanged : function()
24382     {
24383         var dirty = false;
24384         this.items.each(function(f){
24385            if(f.hasChanged()){
24386                dirty = true;
24387                return false;
24388            }
24389         });
24390         return dirty;
24391         
24392     },
24393     /**
24394      * Resets all hasChanged to 'false' -
24395      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24396      * So hasChanged storage is only to be used for this purpose
24397      * @return Boolean
24398      */
24399     resetHasChanged : function()
24400     {
24401         this.items.each(function(f){
24402            f.resetHasChanged();
24403         });
24404         
24405     },
24406     
24407     
24408     /**
24409      * Performs a predefined action (submit or load) or custom actions you define on this form.
24410      * @param {String} actionName The name of the action type
24411      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24412      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24413      * accept other config options):
24414      * <pre>
24415 Property          Type             Description
24416 ----------------  ---------------  ----------------------------------------------------------------------------------
24417 url               String           The url for the action (defaults to the form's url)
24418 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24419 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24420 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24421                                    validate the form on the client (defaults to false)
24422      * </pre>
24423      * @return {BasicForm} this
24424      */
24425     doAction : function(action, options){
24426         if(typeof action == 'string'){
24427             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24428         }
24429         if(this.fireEvent('beforeaction', this, action) !== false){
24430             this.beforeAction(action);
24431             action.run.defer(100, action);
24432         }
24433         return this;
24434     },
24435
24436     /**
24437      * Shortcut to do a submit action.
24438      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24439      * @return {BasicForm} this
24440      */
24441     submit : function(options){
24442         this.doAction('submit', options);
24443         return this;
24444     },
24445
24446     /**
24447      * Shortcut to do a load action.
24448      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24449      * @return {BasicForm} this
24450      */
24451     load : function(options){
24452         this.doAction('load', options);
24453         return this;
24454     },
24455
24456     /**
24457      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24458      * @param {Record} record The record to edit
24459      * @return {BasicForm} this
24460      */
24461     updateRecord : function(record){
24462         record.beginEdit();
24463         var fs = record.fields;
24464         fs.each(function(f){
24465             var field = this.findField(f.name);
24466             if(field){
24467                 record.set(f.name, field.getValue());
24468             }
24469         }, this);
24470         record.endEdit();
24471         return this;
24472     },
24473
24474     /**
24475      * Loads an Roo.data.Record into this form.
24476      * @param {Record} record The record to load
24477      * @return {BasicForm} this
24478      */
24479     loadRecord : function(record){
24480         this.setValues(record.data);
24481         return this;
24482     },
24483
24484     // private
24485     beforeAction : function(action){
24486         var o = action.options;
24487         
24488        
24489         if(this.waitMsgTarget === true){
24490             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24491         }else if(this.waitMsgTarget){
24492             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24493             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24494         }else {
24495             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24496         }
24497          
24498     },
24499
24500     // private
24501     afterAction : function(action, success){
24502         this.activeAction = null;
24503         var o = action.options;
24504         
24505         if(this.waitMsgTarget === true){
24506             this.el.unmask();
24507         }else if(this.waitMsgTarget){
24508             this.waitMsgTarget.unmask();
24509         }else{
24510             Roo.MessageBox.updateProgress(1);
24511             Roo.MessageBox.hide();
24512         }
24513          
24514         if(success){
24515             if(o.reset){
24516                 this.reset();
24517             }
24518             Roo.callback(o.success, o.scope, [this, action]);
24519             this.fireEvent('actioncomplete', this, action);
24520             
24521         }else{
24522             
24523             // failure condition..
24524             // we have a scenario where updates need confirming.
24525             // eg. if a locking scenario exists..
24526             // we look for { errors : { needs_confirm : true }} in the response.
24527             if (
24528                 (typeof(action.result) != 'undefined')  &&
24529                 (typeof(action.result.errors) != 'undefined')  &&
24530                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24531            ){
24532                 var _t = this;
24533                 Roo.MessageBox.confirm(
24534                     "Change requires confirmation",
24535                     action.result.errorMsg,
24536                     function(r) {
24537                         if (r != 'yes') {
24538                             return;
24539                         }
24540                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24541                     }
24542                     
24543                 );
24544                 
24545                 
24546                 
24547                 return;
24548             }
24549             
24550             Roo.callback(o.failure, o.scope, [this, action]);
24551             // show an error message if no failed handler is set..
24552             if (!this.hasListener('actionfailed')) {
24553                 Roo.MessageBox.alert("Error",
24554                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24555                         action.result.errorMsg :
24556                         "Saving Failed, please check your entries or try again"
24557                 );
24558             }
24559             
24560             this.fireEvent('actionfailed', this, action);
24561         }
24562         
24563     },
24564
24565     /**
24566      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24567      * @param {String} id The value to search for
24568      * @return Field
24569      */
24570     findField : function(id){
24571         var field = this.items.get(id);
24572         if(!field){
24573             this.items.each(function(f){
24574                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24575                     field = f;
24576                     return false;
24577                 }
24578             });
24579         }
24580         return field || null;
24581     },
24582
24583     /**
24584      * Add a secondary form to this one, 
24585      * Used to provide tabbed forms. One form is primary, with hidden values 
24586      * which mirror the elements from the other forms.
24587      * 
24588      * @param {Roo.form.Form} form to add.
24589      * 
24590      */
24591     addForm : function(form)
24592     {
24593        
24594         if (this.childForms.indexOf(form) > -1) {
24595             // already added..
24596             return;
24597         }
24598         this.childForms.push(form);
24599         var n = '';
24600         Roo.each(form.allItems, function (fe) {
24601             
24602             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24603             if (this.findField(n)) { // already added..
24604                 return;
24605             }
24606             var add = new Roo.form.Hidden({
24607                 name : n
24608             });
24609             add.render(this.el);
24610             
24611             this.add( add );
24612         }, this);
24613         
24614     },
24615     /**
24616      * Mark fields in this form invalid in bulk.
24617      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24618      * @return {BasicForm} this
24619      */
24620     markInvalid : function(errors){
24621         if(errors instanceof Array){
24622             for(var i = 0, len = errors.length; i < len; i++){
24623                 var fieldError = errors[i];
24624                 var f = this.findField(fieldError.id);
24625                 if(f){
24626                     f.markInvalid(fieldError.msg);
24627                 }
24628             }
24629         }else{
24630             var field, id;
24631             for(id in errors){
24632                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24633                     field.markInvalid(errors[id]);
24634                 }
24635             }
24636         }
24637         Roo.each(this.childForms || [], function (f) {
24638             f.markInvalid(errors);
24639         });
24640         
24641         return this;
24642     },
24643
24644     /**
24645      * Set values for fields in this form in bulk.
24646      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24647      * @return {BasicForm} this
24648      */
24649     setValues : function(values){
24650         if(values instanceof Array){ // array of objects
24651             for(var i = 0, len = values.length; i < len; i++){
24652                 var v = values[i];
24653                 var f = this.findField(v.id);
24654                 if(f){
24655                     f.setValue(v.value);
24656                     if(this.trackResetOnLoad){
24657                         f.originalValue = f.getValue();
24658                     }
24659                 }
24660             }
24661         }else{ // object hash
24662             var field, id;
24663             for(id in values){
24664                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24665                     
24666                     if (field.setFromData && 
24667                         field.valueField && 
24668                         field.displayField &&
24669                         // combos' with local stores can 
24670                         // be queried via setValue()
24671                         // to set their value..
24672                         (field.store && !field.store.isLocal)
24673                         ) {
24674                         // it's a combo
24675                         var sd = { };
24676                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24677                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24678                         field.setFromData(sd);
24679                         
24680                     } else {
24681                         field.setValue(values[id]);
24682                     }
24683                     
24684                     
24685                     if(this.trackResetOnLoad){
24686                         field.originalValue = field.getValue();
24687                     }
24688                 }
24689             }
24690         }
24691         this.resetHasChanged();
24692         
24693         
24694         Roo.each(this.childForms || [], function (f) {
24695             f.setValues(values);
24696             f.resetHasChanged();
24697         });
24698                 
24699         return this;
24700     },
24701
24702     /**
24703      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24704      * they are returned as an array.
24705      * @param {Boolean} asString
24706      * @return {Object}
24707      */
24708     getValues : function(asString){
24709         if (this.childForms) {
24710             // copy values from the child forms
24711             Roo.each(this.childForms, function (f) {
24712                 this.setValues(f.getValues());
24713             }, this);
24714         }
24715         
24716         
24717         
24718         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24719         if(asString === true){
24720             return fs;
24721         }
24722         return Roo.urlDecode(fs);
24723     },
24724     
24725     /**
24726      * Returns the fields in this form as an object with key/value pairs. 
24727      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24728      * @return {Object}
24729      */
24730     getFieldValues : function(with_hidden)
24731     {
24732         if (this.childForms) {
24733             // copy values from the child forms
24734             // should this call getFieldValues - probably not as we do not currently copy
24735             // hidden fields when we generate..
24736             Roo.each(this.childForms, function (f) {
24737                 this.setValues(f.getValues());
24738             }, this);
24739         }
24740         
24741         var ret = {};
24742         this.items.each(function(f){
24743             if (!f.getName()) {
24744                 return;
24745             }
24746             var v = f.getValue();
24747             if (f.inputType =='radio') {
24748                 if (typeof(ret[f.getName()]) == 'undefined') {
24749                     ret[f.getName()] = ''; // empty..
24750                 }
24751                 
24752                 if (!f.el.dom.checked) {
24753                     return;
24754                     
24755                 }
24756                 v = f.el.dom.value;
24757                 
24758             }
24759             
24760             // not sure if this supported any more..
24761             if ((typeof(v) == 'object') && f.getRawValue) {
24762                 v = f.getRawValue() ; // dates..
24763             }
24764             // combo boxes where name != hiddenName...
24765             if (f.name != f.getName()) {
24766                 ret[f.name] = f.getRawValue();
24767             }
24768             ret[f.getName()] = v;
24769         });
24770         
24771         return ret;
24772     },
24773
24774     /**
24775      * Clears all invalid messages in this form.
24776      * @return {BasicForm} this
24777      */
24778     clearInvalid : function(){
24779         this.items.each(function(f){
24780            f.clearInvalid();
24781         });
24782         
24783         Roo.each(this.childForms || [], function (f) {
24784             f.clearInvalid();
24785         });
24786         
24787         
24788         return this;
24789     },
24790
24791     /**
24792      * Resets this form.
24793      * @return {BasicForm} this
24794      */
24795     reset : function(){
24796         this.items.each(function(f){
24797             f.reset();
24798         });
24799         
24800         Roo.each(this.childForms || [], function (f) {
24801             f.reset();
24802         });
24803         this.resetHasChanged();
24804         
24805         return this;
24806     },
24807
24808     /**
24809      * Add Roo.form components to this form.
24810      * @param {Field} field1
24811      * @param {Field} field2 (optional)
24812      * @param {Field} etc (optional)
24813      * @return {BasicForm} this
24814      */
24815     add : function(){
24816         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24817         return this;
24818     },
24819
24820
24821     /**
24822      * Removes a field from the items collection (does NOT remove its markup).
24823      * @param {Field} field
24824      * @return {BasicForm} this
24825      */
24826     remove : function(field){
24827         this.items.remove(field);
24828         return this;
24829     },
24830
24831     /**
24832      * Looks at the fields in this form, checks them for an id attribute,
24833      * and calls applyTo on the existing dom element with that id.
24834      * @return {BasicForm} this
24835      */
24836     render : function(){
24837         this.items.each(function(f){
24838             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24839                 f.applyTo(f.id);
24840             }
24841         });
24842         return this;
24843     },
24844
24845     /**
24846      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24847      * @param {Object} values
24848      * @return {BasicForm} this
24849      */
24850     applyToFields : function(o){
24851         this.items.each(function(f){
24852            Roo.apply(f, o);
24853         });
24854         return this;
24855     },
24856
24857     /**
24858      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24859      * @param {Object} values
24860      * @return {BasicForm} this
24861      */
24862     applyIfToFields : function(o){
24863         this.items.each(function(f){
24864            Roo.applyIf(f, o);
24865         });
24866         return this;
24867     }
24868 });
24869
24870 // back compat
24871 Roo.BasicForm = Roo.form.BasicForm;/*
24872  * Based on:
24873  * Ext JS Library 1.1.1
24874  * Copyright(c) 2006-2007, Ext JS, LLC.
24875  *
24876  * Originally Released Under LGPL - original licence link has changed is not relivant.
24877  *
24878  * Fork - LGPL
24879  * <script type="text/javascript">
24880  */
24881
24882 /**
24883  * @class Roo.form.Form
24884  * @extends Roo.form.BasicForm
24885  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24886  * @constructor
24887  * @param {Object} config Configuration options
24888  */
24889 Roo.form.Form = function(config){
24890     var xitems =  [];
24891     if (config.items) {
24892         xitems = config.items;
24893         delete config.items;
24894     }
24895    
24896     
24897     Roo.form.Form.superclass.constructor.call(this, null, config);
24898     this.url = this.url || this.action;
24899     if(!this.root){
24900         this.root = new Roo.form.Layout(Roo.applyIf({
24901             id: Roo.id()
24902         }, config));
24903     }
24904     this.active = this.root;
24905     /**
24906      * Array of all the buttons that have been added to this form via {@link addButton}
24907      * @type Array
24908      */
24909     this.buttons = [];
24910     this.allItems = [];
24911     this.addEvents({
24912         /**
24913          * @event clientvalidation
24914          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24915          * @param {Form} this
24916          * @param {Boolean} valid true if the form has passed client-side validation
24917          */
24918         clientvalidation: true,
24919         /**
24920          * @event rendered
24921          * Fires when the form is rendered
24922          * @param {Roo.form.Form} form
24923          */
24924         rendered : true
24925     });
24926     
24927     if (this.progressUrl) {
24928             // push a hidden field onto the list of fields..
24929             this.addxtype( {
24930                     xns: Roo.form, 
24931                     xtype : 'Hidden', 
24932                     name : 'UPLOAD_IDENTIFIER' 
24933             });
24934         }
24935         
24936     
24937     Roo.each(xitems, this.addxtype, this);
24938     
24939     
24940     
24941 };
24942
24943 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24944     /**
24945      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24946      */
24947     /**
24948      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24949      */
24950     /**
24951      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24952      */
24953     buttonAlign:'center',
24954
24955     /**
24956      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24957      */
24958     minButtonWidth:75,
24959
24960     /**
24961      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24962      * This property cascades to child containers if not set.
24963      */
24964     labelAlign:'left',
24965
24966     /**
24967      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24968      * fires a looping event with that state. This is required to bind buttons to the valid
24969      * state using the config value formBind:true on the button.
24970      */
24971     monitorValid : false,
24972
24973     /**
24974      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24975      */
24976     monitorPoll : 200,
24977     
24978     /**
24979      * @cfg {String} progressUrl - Url to return progress data 
24980      */
24981     
24982     progressUrl : false,
24983   
24984     /**
24985      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
24986      * fields are added and the column is closed. If no fields are passed the column remains open
24987      * until end() is called.
24988      * @param {Object} config The config to pass to the column
24989      * @param {Field} field1 (optional)
24990      * @param {Field} field2 (optional)
24991      * @param {Field} etc (optional)
24992      * @return Column The column container object
24993      */
24994     column : function(c){
24995         var col = new Roo.form.Column(c);
24996         this.start(col);
24997         if(arguments.length > 1){ // duplicate code required because of Opera
24998             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
24999             this.end();
25000         }
25001         return col;
25002     },
25003
25004     /**
25005      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25006      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25007      * until end() is called.
25008      * @param {Object} config The config to pass to the fieldset
25009      * @param {Field} field1 (optional)
25010      * @param {Field} field2 (optional)
25011      * @param {Field} etc (optional)
25012      * @return FieldSet The fieldset container object
25013      */
25014     fieldset : function(c){
25015         var fs = new Roo.form.FieldSet(c);
25016         this.start(fs);
25017         if(arguments.length > 1){ // duplicate code required because of Opera
25018             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25019             this.end();
25020         }
25021         return fs;
25022     },
25023
25024     /**
25025      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25026      * fields are added and the container is closed. If no fields are passed the container remains open
25027      * until end() is called.
25028      * @param {Object} config The config to pass to the Layout
25029      * @param {Field} field1 (optional)
25030      * @param {Field} field2 (optional)
25031      * @param {Field} etc (optional)
25032      * @return Layout The container object
25033      */
25034     container : function(c){
25035         var l = new Roo.form.Layout(c);
25036         this.start(l);
25037         if(arguments.length > 1){ // duplicate code required because of Opera
25038             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25039             this.end();
25040         }
25041         return l;
25042     },
25043
25044     /**
25045      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25046      * @param {Object} container A Roo.form.Layout or subclass of Layout
25047      * @return {Form} this
25048      */
25049     start : function(c){
25050         // cascade label info
25051         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25052         this.active.stack.push(c);
25053         c.ownerCt = this.active;
25054         this.active = c;
25055         return this;
25056     },
25057
25058     /**
25059      * Closes the current open container
25060      * @return {Form} this
25061      */
25062     end : function(){
25063         if(this.active == this.root){
25064             return this;
25065         }
25066         this.active = this.active.ownerCt;
25067         return this;
25068     },
25069
25070     /**
25071      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25072      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25073      * as the label of the field.
25074      * @param {Field} field1
25075      * @param {Field} field2 (optional)
25076      * @param {Field} etc. (optional)
25077      * @return {Form} this
25078      */
25079     add : function(){
25080         this.active.stack.push.apply(this.active.stack, arguments);
25081         this.allItems.push.apply(this.allItems,arguments);
25082         var r = [];
25083         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25084             if(a[i].isFormField){
25085                 r.push(a[i]);
25086             }
25087         }
25088         if(r.length > 0){
25089             Roo.form.Form.superclass.add.apply(this, r);
25090         }
25091         return this;
25092     },
25093     
25094
25095     
25096     
25097     
25098      /**
25099      * Find any element that has been added to a form, using it's ID or name
25100      * This can include framesets, columns etc. along with regular fields..
25101      * @param {String} id - id or name to find.
25102      
25103      * @return {Element} e - or false if nothing found.
25104      */
25105     findbyId : function(id)
25106     {
25107         var ret = false;
25108         if (!id) {
25109             return ret;
25110         }
25111         Roo.each(this.allItems, function(f){
25112             if (f.id == id || f.name == id ){
25113                 ret = f;
25114                 return false;
25115             }
25116         });
25117         return ret;
25118     },
25119
25120     
25121     
25122     /**
25123      * Render this form into the passed container. This should only be called once!
25124      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25125      * @return {Form} this
25126      */
25127     render : function(ct)
25128     {
25129         
25130         
25131         
25132         ct = Roo.get(ct);
25133         var o = this.autoCreate || {
25134             tag: 'form',
25135             method : this.method || 'POST',
25136             id : this.id || Roo.id()
25137         };
25138         this.initEl(ct.createChild(o));
25139
25140         this.root.render(this.el);
25141         
25142        
25143              
25144         this.items.each(function(f){
25145             f.render('x-form-el-'+f.id);
25146         });
25147
25148         if(this.buttons.length > 0){
25149             // tables are required to maintain order and for correct IE layout
25150             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25151                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25152                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25153             }}, null, true);
25154             var tr = tb.getElementsByTagName('tr')[0];
25155             for(var i = 0, len = this.buttons.length; i < len; i++) {
25156                 var b = this.buttons[i];
25157                 var td = document.createElement('td');
25158                 td.className = 'x-form-btn-td';
25159                 b.render(tr.appendChild(td));
25160             }
25161         }
25162         if(this.monitorValid){ // initialize after render
25163             this.startMonitoring();
25164         }
25165         this.fireEvent('rendered', this);
25166         return this;
25167     },
25168
25169     /**
25170      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25171      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25172      * object or a valid Roo.DomHelper element config
25173      * @param {Function} handler The function called when the button is clicked
25174      * @param {Object} scope (optional) The scope of the handler function
25175      * @return {Roo.Button}
25176      */
25177     addButton : function(config, handler, scope){
25178         var bc = {
25179             handler: handler,
25180             scope: scope,
25181             minWidth: this.minButtonWidth,
25182             hideParent:true
25183         };
25184         if(typeof config == "string"){
25185             bc.text = config;
25186         }else{
25187             Roo.apply(bc, config);
25188         }
25189         var btn = new Roo.Button(null, bc);
25190         this.buttons.push(btn);
25191         return btn;
25192     },
25193
25194      /**
25195      * Adds a series of form elements (using the xtype property as the factory method.
25196      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25197      * @param {Object} config 
25198      */
25199     
25200     addxtype : function()
25201     {
25202         var ar = Array.prototype.slice.call(arguments, 0);
25203         var ret = false;
25204         for(var i = 0; i < ar.length; i++) {
25205             if (!ar[i]) {
25206                 continue; // skip -- if this happends something invalid got sent, we 
25207                 // should ignore it, as basically that interface element will not show up
25208                 // and that should be pretty obvious!!
25209             }
25210             
25211             if (Roo.form[ar[i].xtype]) {
25212                 ar[i].form = this;
25213                 var fe = Roo.factory(ar[i], Roo.form);
25214                 if (!ret) {
25215                     ret = fe;
25216                 }
25217                 fe.form = this;
25218                 if (fe.store) {
25219                     fe.store.form = this;
25220                 }
25221                 if (fe.isLayout) {  
25222                          
25223                     this.start(fe);
25224                     this.allItems.push(fe);
25225                     if (fe.items && fe.addxtype) {
25226                         fe.addxtype.apply(fe, fe.items);
25227                         delete fe.items;
25228                     }
25229                      this.end();
25230                     continue;
25231                 }
25232                 
25233                 
25234                  
25235                 this.add(fe);
25236               //  console.log('adding ' + ar[i].xtype);
25237             }
25238             if (ar[i].xtype == 'Button') {  
25239                 //console.log('adding button');
25240                 //console.log(ar[i]);
25241                 this.addButton(ar[i]);
25242                 this.allItems.push(fe);
25243                 continue;
25244             }
25245             
25246             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25247                 alert('end is not supported on xtype any more, use items');
25248             //    this.end();
25249             //    //console.log('adding end');
25250             }
25251             
25252         }
25253         return ret;
25254     },
25255     
25256     /**
25257      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25258      * option "monitorValid"
25259      */
25260     startMonitoring : function(){
25261         if(!this.bound){
25262             this.bound = true;
25263             Roo.TaskMgr.start({
25264                 run : this.bindHandler,
25265                 interval : this.monitorPoll || 200,
25266                 scope: this
25267             });
25268         }
25269     },
25270
25271     /**
25272      * Stops monitoring of the valid state of this form
25273      */
25274     stopMonitoring : function(){
25275         this.bound = false;
25276     },
25277
25278     // private
25279     bindHandler : function(){
25280         if(!this.bound){
25281             return false; // stops binding
25282         }
25283         var valid = true;
25284         this.items.each(function(f){
25285             if(!f.isValid(true)){
25286                 valid = false;
25287                 return false;
25288             }
25289         });
25290         for(var i = 0, len = this.buttons.length; i < len; i++){
25291             var btn = this.buttons[i];
25292             if(btn.formBind === true && btn.disabled === valid){
25293                 btn.setDisabled(!valid);
25294             }
25295         }
25296         this.fireEvent('clientvalidation', this, valid);
25297     }
25298     
25299     
25300     
25301     
25302     
25303     
25304     
25305     
25306 });
25307
25308
25309 // back compat
25310 Roo.Form = Roo.form.Form;
25311 /*
25312  * Based on:
25313  * Ext JS Library 1.1.1
25314  * Copyright(c) 2006-2007, Ext JS, LLC.
25315  *
25316  * Originally Released Under LGPL - original licence link has changed is not relivant.
25317  *
25318  * Fork - LGPL
25319  * <script type="text/javascript">
25320  */
25321
25322 // as we use this in bootstrap.
25323 Roo.namespace('Roo.form');
25324  /**
25325  * @class Roo.form.Action
25326  * Internal Class used to handle form actions
25327  * @constructor
25328  * @param {Roo.form.BasicForm} el The form element or its id
25329  * @param {Object} config Configuration options
25330  */
25331
25332  
25333  
25334 // define the action interface
25335 Roo.form.Action = function(form, options){
25336     this.form = form;
25337     this.options = options || {};
25338 };
25339 /**
25340  * Client Validation Failed
25341  * @const 
25342  */
25343 Roo.form.Action.CLIENT_INVALID = 'client';
25344 /**
25345  * Server Validation Failed
25346  * @const 
25347  */
25348 Roo.form.Action.SERVER_INVALID = 'server';
25349  /**
25350  * Connect to Server Failed
25351  * @const 
25352  */
25353 Roo.form.Action.CONNECT_FAILURE = 'connect';
25354 /**
25355  * Reading Data from Server Failed
25356  * @const 
25357  */
25358 Roo.form.Action.LOAD_FAILURE = 'load';
25359
25360 Roo.form.Action.prototype = {
25361     type : 'default',
25362     failureType : undefined,
25363     response : undefined,
25364     result : undefined,
25365
25366     // interface method
25367     run : function(options){
25368
25369     },
25370
25371     // interface method
25372     success : function(response){
25373
25374     },
25375
25376     // interface method
25377     handleResponse : function(response){
25378
25379     },
25380
25381     // default connection failure
25382     failure : function(response){
25383         
25384         this.response = response;
25385         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25386         this.form.afterAction(this, false);
25387     },
25388
25389     processResponse : function(response){
25390         this.response = response;
25391         if(!response.responseText){
25392             return true;
25393         }
25394         this.result = this.handleResponse(response);
25395         return this.result;
25396     },
25397
25398     // utility functions used internally
25399     getUrl : function(appendParams){
25400         var url = this.options.url || this.form.url || this.form.el.dom.action;
25401         if(appendParams){
25402             var p = this.getParams();
25403             if(p){
25404                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25405             }
25406         }
25407         return url;
25408     },
25409
25410     getMethod : function(){
25411         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25412     },
25413
25414     getParams : function(){
25415         var bp = this.form.baseParams;
25416         var p = this.options.params;
25417         if(p){
25418             if(typeof p == "object"){
25419                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25420             }else if(typeof p == 'string' && bp){
25421                 p += '&' + Roo.urlEncode(bp);
25422             }
25423         }else if(bp){
25424             p = Roo.urlEncode(bp);
25425         }
25426         return p;
25427     },
25428
25429     createCallback : function(){
25430         return {
25431             success: this.success,
25432             failure: this.failure,
25433             scope: this,
25434             timeout: (this.form.timeout*1000),
25435             upload: this.form.fileUpload ? this.success : undefined
25436         };
25437     }
25438 };
25439
25440 Roo.form.Action.Submit = function(form, options){
25441     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25442 };
25443
25444 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25445     type : 'submit',
25446
25447     haveProgress : false,
25448     uploadComplete : false,
25449     
25450     // uploadProgress indicator.
25451     uploadProgress : function()
25452     {
25453         if (!this.form.progressUrl) {
25454             return;
25455         }
25456         
25457         if (!this.haveProgress) {
25458             Roo.MessageBox.progress("Uploading", "Uploading");
25459         }
25460         if (this.uploadComplete) {
25461            Roo.MessageBox.hide();
25462            return;
25463         }
25464         
25465         this.haveProgress = true;
25466    
25467         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25468         
25469         var c = new Roo.data.Connection();
25470         c.request({
25471             url : this.form.progressUrl,
25472             params: {
25473                 id : uid
25474             },
25475             method: 'GET',
25476             success : function(req){
25477                //console.log(data);
25478                 var rdata = false;
25479                 var edata;
25480                 try  {
25481                    rdata = Roo.decode(req.responseText)
25482                 } catch (e) {
25483                     Roo.log("Invalid data from server..");
25484                     Roo.log(edata);
25485                     return;
25486                 }
25487                 if (!rdata || !rdata.success) {
25488                     Roo.log(rdata);
25489                     Roo.MessageBox.alert(Roo.encode(rdata));
25490                     return;
25491                 }
25492                 var data = rdata.data;
25493                 
25494                 if (this.uploadComplete) {
25495                    Roo.MessageBox.hide();
25496                    return;
25497                 }
25498                    
25499                 if (data){
25500                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25501                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25502                     );
25503                 }
25504                 this.uploadProgress.defer(2000,this);
25505             },
25506        
25507             failure: function(data) {
25508                 Roo.log('progress url failed ');
25509                 Roo.log(data);
25510             },
25511             scope : this
25512         });
25513            
25514     },
25515     
25516     
25517     run : function()
25518     {
25519         // run get Values on the form, so it syncs any secondary forms.
25520         this.form.getValues();
25521         
25522         var o = this.options;
25523         var method = this.getMethod();
25524         var isPost = method == 'POST';
25525         if(o.clientValidation === false || this.form.isValid()){
25526             
25527             if (this.form.progressUrl) {
25528                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25529                     (new Date() * 1) + '' + Math.random());
25530                     
25531             } 
25532             
25533             
25534             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25535                 form:this.form.el.dom,
25536                 url:this.getUrl(!isPost),
25537                 method: method,
25538                 params:isPost ? this.getParams() : null,
25539                 isUpload: this.form.fileUpload
25540             }));
25541             
25542             this.uploadProgress();
25543
25544         }else if (o.clientValidation !== false){ // client validation failed
25545             this.failureType = Roo.form.Action.CLIENT_INVALID;
25546             this.form.afterAction(this, false);
25547         }
25548     },
25549
25550     success : function(response)
25551     {
25552         this.uploadComplete= true;
25553         if (this.haveProgress) {
25554             Roo.MessageBox.hide();
25555         }
25556         
25557         
25558         var result = this.processResponse(response);
25559         if(result === true || result.success){
25560             this.form.afterAction(this, true);
25561             return;
25562         }
25563         if(result.errors){
25564             this.form.markInvalid(result.errors);
25565             this.failureType = Roo.form.Action.SERVER_INVALID;
25566         }
25567         this.form.afterAction(this, false);
25568     },
25569     failure : function(response)
25570     {
25571         this.uploadComplete= true;
25572         if (this.haveProgress) {
25573             Roo.MessageBox.hide();
25574         }
25575         
25576         this.response = response;
25577         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25578         this.form.afterAction(this, false);
25579     },
25580     
25581     handleResponse : function(response){
25582         if(this.form.errorReader){
25583             var rs = this.form.errorReader.read(response);
25584             var errors = [];
25585             if(rs.records){
25586                 for(var i = 0, len = rs.records.length; i < len; i++) {
25587                     var r = rs.records[i];
25588                     errors[i] = r.data;
25589                 }
25590             }
25591             if(errors.length < 1){
25592                 errors = null;
25593             }
25594             return {
25595                 success : rs.success,
25596                 errors : errors
25597             };
25598         }
25599         var ret = false;
25600         try {
25601             ret = Roo.decode(response.responseText);
25602         } catch (e) {
25603             ret = {
25604                 success: false,
25605                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25606                 errors : []
25607             };
25608         }
25609         return ret;
25610         
25611     }
25612 });
25613
25614
25615 Roo.form.Action.Load = function(form, options){
25616     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25617     this.reader = this.form.reader;
25618 };
25619
25620 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25621     type : 'load',
25622
25623     run : function(){
25624         
25625         Roo.Ajax.request(Roo.apply(
25626                 this.createCallback(), {
25627                     method:this.getMethod(),
25628                     url:this.getUrl(false),
25629                     params:this.getParams()
25630         }));
25631     },
25632
25633     success : function(response){
25634         
25635         var result = this.processResponse(response);
25636         if(result === true || !result.success || !result.data){
25637             this.failureType = Roo.form.Action.LOAD_FAILURE;
25638             this.form.afterAction(this, false);
25639             return;
25640         }
25641         this.form.clearInvalid();
25642         this.form.setValues(result.data);
25643         this.form.afterAction(this, true);
25644     },
25645
25646     handleResponse : function(response){
25647         if(this.form.reader){
25648             var rs = this.form.reader.read(response);
25649             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25650             return {
25651                 success : rs.success,
25652                 data : data
25653             };
25654         }
25655         return Roo.decode(response.responseText);
25656     }
25657 });
25658
25659 Roo.form.Action.ACTION_TYPES = {
25660     'load' : Roo.form.Action.Load,
25661     'submit' : Roo.form.Action.Submit
25662 };/*
25663  * Based on:
25664  * Ext JS Library 1.1.1
25665  * Copyright(c) 2006-2007, Ext JS, LLC.
25666  *
25667  * Originally Released Under LGPL - original licence link has changed is not relivant.
25668  *
25669  * Fork - LGPL
25670  * <script type="text/javascript">
25671  */
25672  
25673 /**
25674  * @class Roo.form.Layout
25675  * @extends Roo.Component
25676  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25677  * @constructor
25678  * @param {Object} config Configuration options
25679  */
25680 Roo.form.Layout = function(config){
25681     var xitems = [];
25682     if (config.items) {
25683         xitems = config.items;
25684         delete config.items;
25685     }
25686     Roo.form.Layout.superclass.constructor.call(this, config);
25687     this.stack = [];
25688     Roo.each(xitems, this.addxtype, this);
25689      
25690 };
25691
25692 Roo.extend(Roo.form.Layout, Roo.Component, {
25693     /**
25694      * @cfg {String/Object} autoCreate
25695      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25696      */
25697     /**
25698      * @cfg {String/Object/Function} style
25699      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25700      * a function which returns such a specification.
25701      */
25702     /**
25703      * @cfg {String} labelAlign
25704      * Valid values are "left," "top" and "right" (defaults to "left")
25705      */
25706     /**
25707      * @cfg {Number} labelWidth
25708      * Fixed width in pixels of all field labels (defaults to undefined)
25709      */
25710     /**
25711      * @cfg {Boolean} clear
25712      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25713      */
25714     clear : true,
25715     /**
25716      * @cfg {String} labelSeparator
25717      * The separator to use after field labels (defaults to ':')
25718      */
25719     labelSeparator : ':',
25720     /**
25721      * @cfg {Boolean} hideLabels
25722      * True to suppress the display of field labels in this layout (defaults to false)
25723      */
25724     hideLabels : false,
25725
25726     // private
25727     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25728     
25729     isLayout : true,
25730     
25731     // private
25732     onRender : function(ct, position){
25733         if(this.el){ // from markup
25734             this.el = Roo.get(this.el);
25735         }else {  // generate
25736             var cfg = this.getAutoCreate();
25737             this.el = ct.createChild(cfg, position);
25738         }
25739         if(this.style){
25740             this.el.applyStyles(this.style);
25741         }
25742         if(this.labelAlign){
25743             this.el.addClass('x-form-label-'+this.labelAlign);
25744         }
25745         if(this.hideLabels){
25746             this.labelStyle = "display:none";
25747             this.elementStyle = "padding-left:0;";
25748         }else{
25749             if(typeof this.labelWidth == 'number'){
25750                 this.labelStyle = "width:"+this.labelWidth+"px;";
25751                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25752             }
25753             if(this.labelAlign == 'top'){
25754                 this.labelStyle = "width:auto;";
25755                 this.elementStyle = "padding-left:0;";
25756             }
25757         }
25758         var stack = this.stack;
25759         var slen = stack.length;
25760         if(slen > 0){
25761             if(!this.fieldTpl){
25762                 var t = new Roo.Template(
25763                     '<div class="x-form-item {5}">',
25764                         '<label for="{0}" style="{2}">{1}{4}</label>',
25765                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25766                         '</div>',
25767                     '</div><div class="x-form-clear-left"></div>'
25768                 );
25769                 t.disableFormats = true;
25770                 t.compile();
25771                 Roo.form.Layout.prototype.fieldTpl = t;
25772             }
25773             for(var i = 0; i < slen; i++) {
25774                 if(stack[i].isFormField){
25775                     this.renderField(stack[i]);
25776                 }else{
25777                     this.renderComponent(stack[i]);
25778                 }
25779             }
25780         }
25781         if(this.clear){
25782             this.el.createChild({cls:'x-form-clear'});
25783         }
25784     },
25785
25786     // private
25787     renderField : function(f){
25788         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25789                f.id, //0
25790                f.fieldLabel, //1
25791                f.labelStyle||this.labelStyle||'', //2
25792                this.elementStyle||'', //3
25793                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25794                f.itemCls||this.itemCls||''  //5
25795        ], true).getPrevSibling());
25796     },
25797
25798     // private
25799     renderComponent : function(c){
25800         c.render(c.isLayout ? this.el : this.el.createChild());    
25801     },
25802     /**
25803      * Adds a object form elements (using the xtype property as the factory method.)
25804      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25805      * @param {Object} config 
25806      */
25807     addxtype : function(o)
25808     {
25809         // create the lement.
25810         o.form = this.form;
25811         var fe = Roo.factory(o, Roo.form);
25812         this.form.allItems.push(fe);
25813         this.stack.push(fe);
25814         
25815         if (fe.isFormField) {
25816             this.form.items.add(fe);
25817         }
25818          
25819         return fe;
25820     }
25821 });
25822
25823 /**
25824  * @class Roo.form.Column
25825  * @extends Roo.form.Layout
25826  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25827  * @constructor
25828  * @param {Object} config Configuration options
25829  */
25830 Roo.form.Column = function(config){
25831     Roo.form.Column.superclass.constructor.call(this, config);
25832 };
25833
25834 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25835     /**
25836      * @cfg {Number/String} width
25837      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25838      */
25839     /**
25840      * @cfg {String/Object} autoCreate
25841      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25842      */
25843
25844     // private
25845     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25846
25847     // private
25848     onRender : function(ct, position){
25849         Roo.form.Column.superclass.onRender.call(this, ct, position);
25850         if(this.width){
25851             this.el.setWidth(this.width);
25852         }
25853     }
25854 });
25855
25856
25857 /**
25858  * @class Roo.form.Row
25859  * @extends Roo.form.Layout
25860  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25861  * @constructor
25862  * @param {Object} config Configuration options
25863  */
25864
25865  
25866 Roo.form.Row = function(config){
25867     Roo.form.Row.superclass.constructor.call(this, config);
25868 };
25869  
25870 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25871       /**
25872      * @cfg {Number/String} width
25873      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25874      */
25875     /**
25876      * @cfg {Number/String} height
25877      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25878      */
25879     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25880     
25881     padWidth : 20,
25882     // private
25883     onRender : function(ct, position){
25884         //console.log('row render');
25885         if(!this.rowTpl){
25886             var t = new Roo.Template(
25887                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25888                     '<label for="{0}" style="{2}">{1}{4}</label>',
25889                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25890                     '</div>',
25891                 '</div>'
25892             );
25893             t.disableFormats = true;
25894             t.compile();
25895             Roo.form.Layout.prototype.rowTpl = t;
25896         }
25897         this.fieldTpl = this.rowTpl;
25898         
25899         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25900         var labelWidth = 100;
25901         
25902         if ((this.labelAlign != 'top')) {
25903             if (typeof this.labelWidth == 'number') {
25904                 labelWidth = this.labelWidth
25905             }
25906             this.padWidth =  20 + labelWidth;
25907             
25908         }
25909         
25910         Roo.form.Column.superclass.onRender.call(this, ct, position);
25911         if(this.width){
25912             this.el.setWidth(this.width);
25913         }
25914         if(this.height){
25915             this.el.setHeight(this.height);
25916         }
25917     },
25918     
25919     // private
25920     renderField : function(f){
25921         f.fieldEl = this.fieldTpl.append(this.el, [
25922                f.id, f.fieldLabel,
25923                f.labelStyle||this.labelStyle||'',
25924                this.elementStyle||'',
25925                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25926                f.itemCls||this.itemCls||'',
25927                f.width ? f.width + this.padWidth : 160 + this.padWidth
25928        ],true);
25929     }
25930 });
25931  
25932
25933 /**
25934  * @class Roo.form.FieldSet
25935  * @extends Roo.form.Layout
25936  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25937  * @constructor
25938  * @param {Object} config Configuration options
25939  */
25940 Roo.form.FieldSet = function(config){
25941     Roo.form.FieldSet.superclass.constructor.call(this, config);
25942 };
25943
25944 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25945     /**
25946      * @cfg {String} legend
25947      * The text to display as the legend for the FieldSet (defaults to '')
25948      */
25949     /**
25950      * @cfg {String/Object} autoCreate
25951      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25952      */
25953
25954     // private
25955     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25956
25957     // private
25958     onRender : function(ct, position){
25959         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25960         if(this.legend){
25961             this.setLegend(this.legend);
25962         }
25963     },
25964
25965     // private
25966     setLegend : function(text){
25967         if(this.rendered){
25968             this.el.child('legend').update(text);
25969         }
25970     }
25971 });/*
25972  * Based on:
25973  * Ext JS Library 1.1.1
25974  * Copyright(c) 2006-2007, Ext JS, LLC.
25975  *
25976  * Originally Released Under LGPL - original licence link has changed is not relivant.
25977  *
25978  * Fork - LGPL
25979  * <script type="text/javascript">
25980  */
25981 /**
25982  * @class Roo.form.VTypes
25983  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
25984  * @singleton
25985  */
25986 Roo.form.VTypes = function(){
25987     // closure these in so they are only created once.
25988     var alpha = /^[a-zA-Z_]+$/;
25989     var alphanum = /^[a-zA-Z0-9_]+$/;
25990     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
25991     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
25992
25993     // All these messages and functions are configurable
25994     return {
25995         /**
25996          * The function used to validate email addresses
25997          * @param {String} value The email address
25998          */
25999         'email' : function(v){
26000             return email.test(v);
26001         },
26002         /**
26003          * The error text to display when the email validation function returns false
26004          * @type String
26005          */
26006         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26007         /**
26008          * The keystroke filter mask to be applied on email input
26009          * @type RegExp
26010          */
26011         'emailMask' : /[a-z0-9_\.\-@]/i,
26012
26013         /**
26014          * The function used to validate URLs
26015          * @param {String} value The URL
26016          */
26017         'url' : function(v){
26018             return url.test(v);
26019         },
26020         /**
26021          * The error text to display when the url validation function returns false
26022          * @type String
26023          */
26024         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26025         
26026         /**
26027          * The function used to validate alpha values
26028          * @param {String} value The value
26029          */
26030         'alpha' : function(v){
26031             return alpha.test(v);
26032         },
26033         /**
26034          * The error text to display when the alpha validation function returns false
26035          * @type String
26036          */
26037         'alphaText' : 'This field should only contain letters and _',
26038         /**
26039          * The keystroke filter mask to be applied on alpha input
26040          * @type RegExp
26041          */
26042         'alphaMask' : /[a-z_]/i,
26043
26044         /**
26045          * The function used to validate alphanumeric values
26046          * @param {String} value The value
26047          */
26048         'alphanum' : function(v){
26049             return alphanum.test(v);
26050         },
26051         /**
26052          * The error text to display when the alphanumeric validation function returns false
26053          * @type String
26054          */
26055         'alphanumText' : 'This field should only contain letters, numbers and _',
26056         /**
26057          * The keystroke filter mask to be applied on alphanumeric input
26058          * @type RegExp
26059          */
26060         'alphanumMask' : /[a-z0-9_]/i
26061     };
26062 }();//<script type="text/javascript">
26063
26064 /**
26065  * @class Roo.form.FCKeditor
26066  * @extends Roo.form.TextArea
26067  * Wrapper around the FCKEditor http://www.fckeditor.net
26068  * @constructor
26069  * Creates a new FCKeditor
26070  * @param {Object} config Configuration options
26071  */
26072 Roo.form.FCKeditor = function(config){
26073     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26074     this.addEvents({
26075          /**
26076          * @event editorinit
26077          * Fired when the editor is initialized - you can add extra handlers here..
26078          * @param {FCKeditor} this
26079          * @param {Object} the FCK object.
26080          */
26081         editorinit : true
26082     });
26083     
26084     
26085 };
26086 Roo.form.FCKeditor.editors = { };
26087 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26088 {
26089     //defaultAutoCreate : {
26090     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26091     //},
26092     // private
26093     /**
26094      * @cfg {Object} fck options - see fck manual for details.
26095      */
26096     fckconfig : false,
26097     
26098     /**
26099      * @cfg {Object} fck toolbar set (Basic or Default)
26100      */
26101     toolbarSet : 'Basic',
26102     /**
26103      * @cfg {Object} fck BasePath
26104      */ 
26105     basePath : '/fckeditor/',
26106     
26107     
26108     frame : false,
26109     
26110     value : '',
26111     
26112    
26113     onRender : function(ct, position)
26114     {
26115         if(!this.el){
26116             this.defaultAutoCreate = {
26117                 tag: "textarea",
26118                 style:"width:300px;height:60px;",
26119                 autocomplete: "new-password"
26120             };
26121         }
26122         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26123         /*
26124         if(this.grow){
26125             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26126             if(this.preventScrollbars){
26127                 this.el.setStyle("overflow", "hidden");
26128             }
26129             this.el.setHeight(this.growMin);
26130         }
26131         */
26132         //console.log('onrender' + this.getId() );
26133         Roo.form.FCKeditor.editors[this.getId()] = this;
26134          
26135
26136         this.replaceTextarea() ;
26137         
26138     },
26139     
26140     getEditor : function() {
26141         return this.fckEditor;
26142     },
26143     /**
26144      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26145      * @param {Mixed} value The value to set
26146      */
26147     
26148     
26149     setValue : function(value)
26150     {
26151         //console.log('setValue: ' + value);
26152         
26153         if(typeof(value) == 'undefined') { // not sure why this is happending...
26154             return;
26155         }
26156         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26157         
26158         //if(!this.el || !this.getEditor()) {
26159         //    this.value = value;
26160             //this.setValue.defer(100,this,[value]);    
26161         //    return;
26162         //} 
26163         
26164         if(!this.getEditor()) {
26165             return;
26166         }
26167         
26168         this.getEditor().SetData(value);
26169         
26170         //
26171
26172     },
26173
26174     /**
26175      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26176      * @return {Mixed} value The field value
26177      */
26178     getValue : function()
26179     {
26180         
26181         if (this.frame && this.frame.dom.style.display == 'none') {
26182             return Roo.form.FCKeditor.superclass.getValue.call(this);
26183         }
26184         
26185         if(!this.el || !this.getEditor()) {
26186            
26187            // this.getValue.defer(100,this); 
26188             return this.value;
26189         }
26190        
26191         
26192         var value=this.getEditor().GetData();
26193         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26194         return Roo.form.FCKeditor.superclass.getValue.call(this);
26195         
26196
26197     },
26198
26199     /**
26200      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26201      * @return {Mixed} value The field value
26202      */
26203     getRawValue : function()
26204     {
26205         if (this.frame && this.frame.dom.style.display == 'none') {
26206             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26207         }
26208         
26209         if(!this.el || !this.getEditor()) {
26210             //this.getRawValue.defer(100,this); 
26211             return this.value;
26212             return;
26213         }
26214         
26215         
26216         
26217         var value=this.getEditor().GetData();
26218         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26219         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26220          
26221     },
26222     
26223     setSize : function(w,h) {
26224         
26225         
26226         
26227         //if (this.frame && this.frame.dom.style.display == 'none') {
26228         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26229         //    return;
26230         //}
26231         //if(!this.el || !this.getEditor()) {
26232         //    this.setSize.defer(100,this, [w,h]); 
26233         //    return;
26234         //}
26235         
26236         
26237         
26238         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26239         
26240         this.frame.dom.setAttribute('width', w);
26241         this.frame.dom.setAttribute('height', h);
26242         this.frame.setSize(w,h);
26243         
26244     },
26245     
26246     toggleSourceEdit : function(value) {
26247         
26248       
26249          
26250         this.el.dom.style.display = value ? '' : 'none';
26251         this.frame.dom.style.display = value ?  'none' : '';
26252         
26253     },
26254     
26255     
26256     focus: function(tag)
26257     {
26258         if (this.frame.dom.style.display == 'none') {
26259             return Roo.form.FCKeditor.superclass.focus.call(this);
26260         }
26261         if(!this.el || !this.getEditor()) {
26262             this.focus.defer(100,this, [tag]); 
26263             return;
26264         }
26265         
26266         
26267         
26268         
26269         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26270         this.getEditor().Focus();
26271         if (tgs.length) {
26272             if (!this.getEditor().Selection.GetSelection()) {
26273                 this.focus.defer(100,this, [tag]); 
26274                 return;
26275             }
26276             
26277             
26278             var r = this.getEditor().EditorDocument.createRange();
26279             r.setStart(tgs[0],0);
26280             r.setEnd(tgs[0],0);
26281             this.getEditor().Selection.GetSelection().removeAllRanges();
26282             this.getEditor().Selection.GetSelection().addRange(r);
26283             this.getEditor().Focus();
26284         }
26285         
26286     },
26287     
26288     
26289     
26290     replaceTextarea : function()
26291     {
26292         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26293             return ;
26294         }
26295         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26296         //{
26297             // We must check the elements firstly using the Id and then the name.
26298         var oTextarea = document.getElementById( this.getId() );
26299         
26300         var colElementsByName = document.getElementsByName( this.getId() ) ;
26301          
26302         oTextarea.style.display = 'none' ;
26303
26304         if ( oTextarea.tabIndex ) {            
26305             this.TabIndex = oTextarea.tabIndex ;
26306         }
26307         
26308         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26309         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26310         this.frame = Roo.get(this.getId() + '___Frame')
26311     },
26312     
26313     _getConfigHtml : function()
26314     {
26315         var sConfig = '' ;
26316
26317         for ( var o in this.fckconfig ) {
26318             sConfig += sConfig.length > 0  ? '&amp;' : '';
26319             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26320         }
26321
26322         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26323     },
26324     
26325     
26326     _getIFrameHtml : function()
26327     {
26328         var sFile = 'fckeditor.html' ;
26329         /* no idea what this is about..
26330         try
26331         {
26332             if ( (/fcksource=true/i).test( window.top.location.search ) )
26333                 sFile = 'fckeditor.original.html' ;
26334         }
26335         catch (e) { 
26336         */
26337
26338         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26339         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26340         
26341         
26342         var html = '<iframe id="' + this.getId() +
26343             '___Frame" src="' + sLink +
26344             '" width="' + this.width +
26345             '" height="' + this.height + '"' +
26346             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26347             ' frameborder="0" scrolling="no"></iframe>' ;
26348
26349         return html ;
26350     },
26351     
26352     _insertHtmlBefore : function( html, element )
26353     {
26354         if ( element.insertAdjacentHTML )       {
26355             // IE
26356             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26357         } else { // Gecko
26358             var oRange = document.createRange() ;
26359             oRange.setStartBefore( element ) ;
26360             var oFragment = oRange.createContextualFragment( html );
26361             element.parentNode.insertBefore( oFragment, element ) ;
26362         }
26363     }
26364     
26365     
26366   
26367     
26368     
26369     
26370     
26371
26372 });
26373
26374 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26375
26376 function FCKeditor_OnComplete(editorInstance){
26377     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26378     f.fckEditor = editorInstance;
26379     //console.log("loaded");
26380     f.fireEvent('editorinit', f, editorInstance);
26381
26382   
26383
26384  
26385
26386
26387
26388
26389
26390
26391
26392
26393
26394
26395
26396
26397
26398
26399
26400 //<script type="text/javascript">
26401 /**
26402  * @class Roo.form.GridField
26403  * @extends Roo.form.Field
26404  * Embed a grid (or editable grid into a form)
26405  * STATUS ALPHA
26406  * 
26407  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26408  * it needs 
26409  * xgrid.store = Roo.data.Store
26410  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26411  * xgrid.store.reader = Roo.data.JsonReader 
26412  * 
26413  * 
26414  * @constructor
26415  * Creates a new GridField
26416  * @param {Object} config Configuration options
26417  */
26418 Roo.form.GridField = function(config){
26419     Roo.form.GridField.superclass.constructor.call(this, config);
26420      
26421 };
26422
26423 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26424     /**
26425      * @cfg {Number} width  - used to restrict width of grid..
26426      */
26427     width : 100,
26428     /**
26429      * @cfg {Number} height - used to restrict height of grid..
26430      */
26431     height : 50,
26432      /**
26433      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26434          * 
26435          *}
26436      */
26437     xgrid : false, 
26438     /**
26439      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26440      * {tag: "input", type: "checkbox", autocomplete: "off"})
26441      */
26442    // defaultAutoCreate : { tag: 'div' },
26443     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26444     /**
26445      * @cfg {String} addTitle Text to include for adding a title.
26446      */
26447     addTitle : false,
26448     //
26449     onResize : function(){
26450         Roo.form.Field.superclass.onResize.apply(this, arguments);
26451     },
26452
26453     initEvents : function(){
26454         // Roo.form.Checkbox.superclass.initEvents.call(this);
26455         // has no events...
26456        
26457     },
26458
26459
26460     getResizeEl : function(){
26461         return this.wrap;
26462     },
26463
26464     getPositionEl : function(){
26465         return this.wrap;
26466     },
26467
26468     // private
26469     onRender : function(ct, position){
26470         
26471         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26472         var style = this.style;
26473         delete this.style;
26474         
26475         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26476         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26477         this.viewEl = this.wrap.createChild({ tag: 'div' });
26478         if (style) {
26479             this.viewEl.applyStyles(style);
26480         }
26481         if (this.width) {
26482             this.viewEl.setWidth(this.width);
26483         }
26484         if (this.height) {
26485             this.viewEl.setHeight(this.height);
26486         }
26487         //if(this.inputValue !== undefined){
26488         //this.setValue(this.value);
26489         
26490         
26491         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26492         
26493         
26494         this.grid.render();
26495         this.grid.getDataSource().on('remove', this.refreshValue, this);
26496         this.grid.getDataSource().on('update', this.refreshValue, this);
26497         this.grid.on('afteredit', this.refreshValue, this);
26498  
26499     },
26500      
26501     
26502     /**
26503      * Sets the value of the item. 
26504      * @param {String} either an object  or a string..
26505      */
26506     setValue : function(v){
26507         //this.value = v;
26508         v = v || []; // empty set..
26509         // this does not seem smart - it really only affects memoryproxy grids..
26510         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26511             var ds = this.grid.getDataSource();
26512             // assumes a json reader..
26513             var data = {}
26514             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26515             ds.loadData( data);
26516         }
26517         // clear selection so it does not get stale.
26518         if (this.grid.sm) { 
26519             this.grid.sm.clearSelections();
26520         }
26521         
26522         Roo.form.GridField.superclass.setValue.call(this, v);
26523         this.refreshValue();
26524         // should load data in the grid really....
26525     },
26526     
26527     // private
26528     refreshValue: function() {
26529          var val = [];
26530         this.grid.getDataSource().each(function(r) {
26531             val.push(r.data);
26532         });
26533         this.el.dom.value = Roo.encode(val);
26534     }
26535     
26536      
26537     
26538     
26539 });/*
26540  * Based on:
26541  * Ext JS Library 1.1.1
26542  * Copyright(c) 2006-2007, Ext JS, LLC.
26543  *
26544  * Originally Released Under LGPL - original licence link has changed is not relivant.
26545  *
26546  * Fork - LGPL
26547  * <script type="text/javascript">
26548  */
26549 /**
26550  * @class Roo.form.DisplayField
26551  * @extends Roo.form.Field
26552  * A generic Field to display non-editable data.
26553  * @cfg {Boolean} closable (true|false) default false
26554  * @constructor
26555  * Creates a new Display Field item.
26556  * @param {Object} config Configuration options
26557  */
26558 Roo.form.DisplayField = function(config){
26559     Roo.form.DisplayField.superclass.constructor.call(this, config);
26560     
26561     this.addEvents({
26562         /**
26563          * @event close
26564          * Fires after the click the close btn
26565              * @param {Roo.form.DisplayField} this
26566              */
26567         close : true
26568     });
26569 };
26570
26571 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26572     inputType:      'hidden',
26573     allowBlank:     true,
26574     readOnly:         true,
26575     
26576  
26577     /**
26578      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26579      */
26580     focusClass : undefined,
26581     /**
26582      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26583      */
26584     fieldClass: 'x-form-field',
26585     
26586      /**
26587      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26588      */
26589     valueRenderer: undefined,
26590     
26591     width: 100,
26592     /**
26593      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26594      * {tag: "input", type: "checkbox", autocomplete: "off"})
26595      */
26596      
26597  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26598  
26599     closable : false,
26600     
26601     onResize : function(){
26602         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26603         
26604     },
26605
26606     initEvents : function(){
26607         // Roo.form.Checkbox.superclass.initEvents.call(this);
26608         // has no events...
26609         
26610         if(this.closable){
26611             this.closeEl.on('click', this.onClose, this);
26612         }
26613        
26614     },
26615
26616
26617     getResizeEl : function(){
26618         return this.wrap;
26619     },
26620
26621     getPositionEl : function(){
26622         return this.wrap;
26623     },
26624
26625     // private
26626     onRender : function(ct, position){
26627         
26628         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26629         //if(this.inputValue !== undefined){
26630         this.wrap = this.el.wrap();
26631         
26632         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26633         
26634         if(this.closable){
26635             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26636         }
26637         
26638         if (this.bodyStyle) {
26639             this.viewEl.applyStyles(this.bodyStyle);
26640         }
26641         //this.viewEl.setStyle('padding', '2px');
26642         
26643         this.setValue(this.value);
26644         
26645     },
26646 /*
26647     // private
26648     initValue : Roo.emptyFn,
26649
26650   */
26651
26652         // private
26653     onClick : function(){
26654         
26655     },
26656
26657     /**
26658      * Sets the checked state of the checkbox.
26659      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26660      */
26661     setValue : function(v){
26662         this.value = v;
26663         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26664         // this might be called before we have a dom element..
26665         if (!this.viewEl) {
26666             return;
26667         }
26668         this.viewEl.dom.innerHTML = html;
26669         Roo.form.DisplayField.superclass.setValue.call(this, v);
26670
26671     },
26672     
26673     onClose : function(e)
26674     {
26675         e.preventDefault();
26676         
26677         this.fireEvent('close', this);
26678     }
26679 });/*
26680  * 
26681  * Licence- LGPL
26682  * 
26683  */
26684
26685 /**
26686  * @class Roo.form.DayPicker
26687  * @extends Roo.form.Field
26688  * A Day picker show [M] [T] [W] ....
26689  * @constructor
26690  * Creates a new Day Picker
26691  * @param {Object} config Configuration options
26692  */
26693 Roo.form.DayPicker= function(config){
26694     Roo.form.DayPicker.superclass.constructor.call(this, config);
26695      
26696 };
26697
26698 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26699     /**
26700      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26701      */
26702     focusClass : undefined,
26703     /**
26704      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26705      */
26706     fieldClass: "x-form-field",
26707    
26708     /**
26709      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26710      * {tag: "input", type: "checkbox", autocomplete: "off"})
26711      */
26712     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26713     
26714    
26715     actionMode : 'viewEl', 
26716     //
26717     // private
26718  
26719     inputType : 'hidden',
26720     
26721      
26722     inputElement: false, // real input element?
26723     basedOn: false, // ????
26724     
26725     isFormField: true, // not sure where this is needed!!!!
26726
26727     onResize : function(){
26728         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26729         if(!this.boxLabel){
26730             this.el.alignTo(this.wrap, 'c-c');
26731         }
26732     },
26733
26734     initEvents : function(){
26735         Roo.form.Checkbox.superclass.initEvents.call(this);
26736         this.el.on("click", this.onClick,  this);
26737         this.el.on("change", this.onClick,  this);
26738     },
26739
26740
26741     getResizeEl : function(){
26742         return this.wrap;
26743     },
26744
26745     getPositionEl : function(){
26746         return this.wrap;
26747     },
26748
26749     
26750     // private
26751     onRender : function(ct, position){
26752         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26753        
26754         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26755         
26756         var r1 = '<table><tr>';
26757         var r2 = '<tr class="x-form-daypick-icons">';
26758         for (var i=0; i < 7; i++) {
26759             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26760             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26761         }
26762         
26763         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26764         viewEl.select('img').on('click', this.onClick, this);
26765         this.viewEl = viewEl;   
26766         
26767         
26768         // this will not work on Chrome!!!
26769         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26770         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26771         
26772         
26773           
26774
26775     },
26776
26777     // private
26778     initValue : Roo.emptyFn,
26779
26780     /**
26781      * Returns the checked state of the checkbox.
26782      * @return {Boolean} True if checked, else false
26783      */
26784     getValue : function(){
26785         return this.el.dom.value;
26786         
26787     },
26788
26789         // private
26790     onClick : function(e){ 
26791         //this.setChecked(!this.checked);
26792         Roo.get(e.target).toggleClass('x-menu-item-checked');
26793         this.refreshValue();
26794         //if(this.el.dom.checked != this.checked){
26795         //    this.setValue(this.el.dom.checked);
26796        // }
26797     },
26798     
26799     // private
26800     refreshValue : function()
26801     {
26802         var val = '';
26803         this.viewEl.select('img',true).each(function(e,i,n)  {
26804             val += e.is(".x-menu-item-checked") ? String(n) : '';
26805         });
26806         this.setValue(val, true);
26807     },
26808
26809     /**
26810      * Sets the checked state of the checkbox.
26811      * On is always based on a string comparison between inputValue and the param.
26812      * @param {Boolean/String} value - the value to set 
26813      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26814      */
26815     setValue : function(v,suppressEvent){
26816         if (!this.el.dom) {
26817             return;
26818         }
26819         var old = this.el.dom.value ;
26820         this.el.dom.value = v;
26821         if (suppressEvent) {
26822             return ;
26823         }
26824          
26825         // update display..
26826         this.viewEl.select('img',true).each(function(e,i,n)  {
26827             
26828             var on = e.is(".x-menu-item-checked");
26829             var newv = v.indexOf(String(n)) > -1;
26830             if (on != newv) {
26831                 e.toggleClass('x-menu-item-checked');
26832             }
26833             
26834         });
26835         
26836         
26837         this.fireEvent('change', this, v, old);
26838         
26839         
26840     },
26841    
26842     // handle setting of hidden value by some other method!!?!?
26843     setFromHidden: function()
26844     {
26845         if(!this.el){
26846             return;
26847         }
26848         //console.log("SET FROM HIDDEN");
26849         //alert('setFrom hidden');
26850         this.setValue(this.el.dom.value);
26851     },
26852     
26853     onDestroy : function()
26854     {
26855         if(this.viewEl){
26856             Roo.get(this.viewEl).remove();
26857         }
26858          
26859         Roo.form.DayPicker.superclass.onDestroy.call(this);
26860     }
26861
26862 });/*
26863  * RooJS Library 1.1.1
26864  * Copyright(c) 2008-2011  Alan Knowles
26865  *
26866  * License - LGPL
26867  */
26868  
26869
26870 /**
26871  * @class Roo.form.ComboCheck
26872  * @extends Roo.form.ComboBox
26873  * A combobox for multiple select items.
26874  *
26875  * FIXME - could do with a reset button..
26876  * 
26877  * @constructor
26878  * Create a new ComboCheck
26879  * @param {Object} config Configuration options
26880  */
26881 Roo.form.ComboCheck = function(config){
26882     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26883     // should verify some data...
26884     // like
26885     // hiddenName = required..
26886     // displayField = required
26887     // valudField == required
26888     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26889     var _t = this;
26890     Roo.each(req, function(e) {
26891         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26892             throw "Roo.form.ComboCheck : missing value for: " + e;
26893         }
26894     });
26895     
26896     
26897 };
26898
26899 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26900      
26901      
26902     editable : false,
26903      
26904     selectedClass: 'x-menu-item-checked', 
26905     
26906     // private
26907     onRender : function(ct, position){
26908         var _t = this;
26909         
26910         
26911         
26912         if(!this.tpl){
26913             var cls = 'x-combo-list';
26914
26915             
26916             this.tpl =  new Roo.Template({
26917                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26918                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26919                    '<span>{' + this.displayField + '}</span>' +
26920                     '</div>' 
26921                 
26922             });
26923         }
26924  
26925         
26926         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26927         this.view.singleSelect = false;
26928         this.view.multiSelect = true;
26929         this.view.toggleSelect = true;
26930         this.pageTb.add(new Roo.Toolbar.Fill(), {
26931             
26932             text: 'Done',
26933             handler: function()
26934             {
26935                 _t.collapse();
26936             }
26937         });
26938     },
26939     
26940     onViewOver : function(e, t){
26941         // do nothing...
26942         return;
26943         
26944     },
26945     
26946     onViewClick : function(doFocus,index){
26947         return;
26948         
26949     },
26950     select: function () {
26951         //Roo.log("SELECT CALLED");
26952     },
26953      
26954     selectByValue : function(xv, scrollIntoView){
26955         var ar = this.getValueArray();
26956         var sels = [];
26957         
26958         Roo.each(ar, function(v) {
26959             if(v === undefined || v === null){
26960                 return;
26961             }
26962             var r = this.findRecord(this.valueField, v);
26963             if(r){
26964                 sels.push(this.store.indexOf(r))
26965                 
26966             }
26967         },this);
26968         this.view.select(sels);
26969         return false;
26970     },
26971     
26972     
26973     
26974     onSelect : function(record, index){
26975        // Roo.log("onselect Called");
26976        // this is only called by the clear button now..
26977         this.view.clearSelections();
26978         this.setValue('[]');
26979         if (this.value != this.valueBefore) {
26980             this.fireEvent('change', this, this.value, this.valueBefore);
26981             this.valueBefore = this.value;
26982         }
26983     },
26984     getValueArray : function()
26985     {
26986         var ar = [] ;
26987         
26988         try {
26989             //Roo.log(this.value);
26990             if (typeof(this.value) == 'undefined') {
26991                 return [];
26992             }
26993             var ar = Roo.decode(this.value);
26994             return  ar instanceof Array ? ar : []; //?? valid?
26995             
26996         } catch(e) {
26997             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
26998             return [];
26999         }
27000          
27001     },
27002     expand : function ()
27003     {
27004         
27005         Roo.form.ComboCheck.superclass.expand.call(this);
27006         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27007         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27008         
27009
27010     },
27011     
27012     collapse : function(){
27013         Roo.form.ComboCheck.superclass.collapse.call(this);
27014         var sl = this.view.getSelectedIndexes();
27015         var st = this.store;
27016         var nv = [];
27017         var tv = [];
27018         var r;
27019         Roo.each(sl, function(i) {
27020             r = st.getAt(i);
27021             nv.push(r.get(this.valueField));
27022         },this);
27023         this.setValue(Roo.encode(nv));
27024         if (this.value != this.valueBefore) {
27025
27026             this.fireEvent('change', this, this.value, this.valueBefore);
27027             this.valueBefore = this.value;
27028         }
27029         
27030     },
27031     
27032     setValue : function(v){
27033         // Roo.log(v);
27034         this.value = v;
27035         
27036         var vals = this.getValueArray();
27037         var tv = [];
27038         Roo.each(vals, function(k) {
27039             var r = this.findRecord(this.valueField, k);
27040             if(r){
27041                 tv.push(r.data[this.displayField]);
27042             }else if(this.valueNotFoundText !== undefined){
27043                 tv.push( this.valueNotFoundText );
27044             }
27045         },this);
27046        // Roo.log(tv);
27047         
27048         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27049         this.hiddenField.value = v;
27050         this.value = v;
27051     }
27052     
27053 });/*
27054  * Based on:
27055  * Ext JS Library 1.1.1
27056  * Copyright(c) 2006-2007, Ext JS, LLC.
27057  *
27058  * Originally Released Under LGPL - original licence link has changed is not relivant.
27059  *
27060  * Fork - LGPL
27061  * <script type="text/javascript">
27062  */
27063  
27064 /**
27065  * @class Roo.form.Signature
27066  * @extends Roo.form.Field
27067  * Signature field.  
27068  * @constructor
27069  * 
27070  * @param {Object} config Configuration options
27071  */
27072
27073 Roo.form.Signature = function(config){
27074     Roo.form.Signature.superclass.constructor.call(this, config);
27075     
27076     this.addEvents({// not in used??
27077          /**
27078          * @event confirm
27079          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27080              * @param {Roo.form.Signature} combo This combo box
27081              */
27082         'confirm' : true,
27083         /**
27084          * @event reset
27085          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27086              * @param {Roo.form.ComboBox} combo This combo box
27087              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27088              */
27089         'reset' : true
27090     });
27091 };
27092
27093 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27094     /**
27095      * @cfg {Object} labels Label to use when rendering a form.
27096      * defaults to 
27097      * labels : { 
27098      *      clear : "Clear",
27099      *      confirm : "Confirm"
27100      *  }
27101      */
27102     labels : { 
27103         clear : "Clear",
27104         confirm : "Confirm"
27105     },
27106     /**
27107      * @cfg {Number} width The signature panel width (defaults to 300)
27108      */
27109     width: 300,
27110     /**
27111      * @cfg {Number} height The signature panel height (defaults to 100)
27112      */
27113     height : 100,
27114     /**
27115      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27116      */
27117     allowBlank : false,
27118     
27119     //private
27120     // {Object} signPanel The signature SVG panel element (defaults to {})
27121     signPanel : {},
27122     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27123     isMouseDown : false,
27124     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27125     isConfirmed : false,
27126     // {String} signatureTmp SVG mapping string (defaults to empty string)
27127     signatureTmp : '',
27128     
27129     
27130     defaultAutoCreate : { // modified by initCompnoent..
27131         tag: "input",
27132         type:"hidden"
27133     },
27134
27135     // private
27136     onRender : function(ct, position){
27137         
27138         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27139         
27140         this.wrap = this.el.wrap({
27141             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27142         });
27143         
27144         this.createToolbar(this);
27145         this.signPanel = this.wrap.createChild({
27146                 tag: 'div',
27147                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27148             }, this.el
27149         );
27150             
27151         this.svgID = Roo.id();
27152         this.svgEl = this.signPanel.createChild({
27153               xmlns : 'http://www.w3.org/2000/svg',
27154               tag : 'svg',
27155               id : this.svgID + "-svg",
27156               width: this.width,
27157               height: this.height,
27158               viewBox: '0 0 '+this.width+' '+this.height,
27159               cn : [
27160                 {
27161                     tag: "rect",
27162                     id: this.svgID + "-svg-r",
27163                     width: this.width,
27164                     height: this.height,
27165                     fill: "#ffa"
27166                 },
27167                 {
27168                     tag: "line",
27169                     id: this.svgID + "-svg-l",
27170                     x1: "0", // start
27171                     y1: (this.height*0.8), // start set the line in 80% of height
27172                     x2: this.width, // end
27173                     y2: (this.height*0.8), // end set the line in 80% of height
27174                     'stroke': "#666",
27175                     'stroke-width': "1",
27176                     'stroke-dasharray': "3",
27177                     'shape-rendering': "crispEdges",
27178                     'pointer-events': "none"
27179                 },
27180                 {
27181                     tag: "path",
27182                     id: this.svgID + "-svg-p",
27183                     'stroke': "navy",
27184                     'stroke-width': "3",
27185                     'fill': "none",
27186                     'pointer-events': 'none'
27187                 }
27188               ]
27189         });
27190         this.createSVG();
27191         this.svgBox = this.svgEl.dom.getScreenCTM();
27192     },
27193     createSVG : function(){ 
27194         var svg = this.signPanel;
27195         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27196         var t = this;
27197
27198         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27199         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27200         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27201         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27202         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27203         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27204         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27205         
27206     },
27207     isTouchEvent : function(e){
27208         return e.type.match(/^touch/);
27209     },
27210     getCoords : function (e) {
27211         var pt    = this.svgEl.dom.createSVGPoint();
27212         pt.x = e.clientX; 
27213         pt.y = e.clientY;
27214         if (this.isTouchEvent(e)) {
27215             pt.x =  e.targetTouches[0].clientX;
27216             pt.y = e.targetTouches[0].clientY;
27217         }
27218         var a = this.svgEl.dom.getScreenCTM();
27219         var b = a.inverse();
27220         var mx = pt.matrixTransform(b);
27221         return mx.x + ',' + mx.y;
27222     },
27223     //mouse event headler 
27224     down : function (e) {
27225         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27226         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27227         
27228         this.isMouseDown = true;
27229         
27230         e.preventDefault();
27231     },
27232     move : function (e) {
27233         if (this.isMouseDown) {
27234             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27235             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27236         }
27237         
27238         e.preventDefault();
27239     },
27240     up : function (e) {
27241         this.isMouseDown = false;
27242         var sp = this.signatureTmp.split(' ');
27243         
27244         if(sp.length > 1){
27245             if(!sp[sp.length-2].match(/^L/)){
27246                 sp.pop();
27247                 sp.pop();
27248                 sp.push("");
27249                 this.signatureTmp = sp.join(" ");
27250             }
27251         }
27252         if(this.getValue() != this.signatureTmp){
27253             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27254             this.isConfirmed = false;
27255         }
27256         e.preventDefault();
27257     },
27258     
27259     /**
27260      * Protected method that will not generally be called directly. It
27261      * is called when the editor creates its toolbar. Override this method if you need to
27262      * add custom toolbar buttons.
27263      * @param {HtmlEditor} editor
27264      */
27265     createToolbar : function(editor){
27266          function btn(id, toggle, handler){
27267             var xid = fid + '-'+ id ;
27268             return {
27269                 id : xid,
27270                 cmd : id,
27271                 cls : 'x-btn-icon x-edit-'+id,
27272                 enableToggle:toggle !== false,
27273                 scope: editor, // was editor...
27274                 handler:handler||editor.relayBtnCmd,
27275                 clickEvent:'mousedown',
27276                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27277                 tabIndex:-1
27278             };
27279         }
27280         
27281         
27282         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27283         this.tb = tb;
27284         this.tb.add(
27285            {
27286                 cls : ' x-signature-btn x-signature-'+id,
27287                 scope: editor, // was editor...
27288                 handler: this.reset,
27289                 clickEvent:'mousedown',
27290                 text: this.labels.clear
27291             },
27292             {
27293                  xtype : 'Fill',
27294                  xns: Roo.Toolbar
27295             }, 
27296             {
27297                 cls : '  x-signature-btn x-signature-'+id,
27298                 scope: editor, // was editor...
27299                 handler: this.confirmHandler,
27300                 clickEvent:'mousedown',
27301                 text: this.labels.confirm
27302             }
27303         );
27304     
27305     },
27306     //public
27307     /**
27308      * when user is clicked confirm then show this image.....
27309      * 
27310      * @return {String} Image Data URI
27311      */
27312     getImageDataURI : function(){
27313         var svg = this.svgEl.dom.parentNode.innerHTML;
27314         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27315         return src; 
27316     },
27317     /**
27318      * 
27319      * @return {Boolean} this.isConfirmed
27320      */
27321     getConfirmed : function(){
27322         return this.isConfirmed;
27323     },
27324     /**
27325      * 
27326      * @return {Number} this.width
27327      */
27328     getWidth : function(){
27329         return this.width;
27330     },
27331     /**
27332      * 
27333      * @return {Number} this.height
27334      */
27335     getHeight : function(){
27336         return this.height;
27337     },
27338     // private
27339     getSignature : function(){
27340         return this.signatureTmp;
27341     },
27342     // private
27343     reset : function(){
27344         this.signatureTmp = '';
27345         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27346         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27347         this.isConfirmed = false;
27348         Roo.form.Signature.superclass.reset.call(this);
27349     },
27350     setSignature : function(s){
27351         this.signatureTmp = s;
27352         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27353         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27354         this.setValue(s);
27355         this.isConfirmed = false;
27356         Roo.form.Signature.superclass.reset.call(this);
27357     }, 
27358     test : function(){
27359 //        Roo.log(this.signPanel.dom.contentWindow.up())
27360     },
27361     //private
27362     setConfirmed : function(){
27363         
27364         
27365         
27366 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27367     },
27368     // private
27369     confirmHandler : function(){
27370         if(!this.getSignature()){
27371             return;
27372         }
27373         
27374         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27375         this.setValue(this.getSignature());
27376         this.isConfirmed = true;
27377         
27378         this.fireEvent('confirm', this);
27379     },
27380     // private
27381     // Subclasses should provide the validation implementation by overriding this
27382     validateValue : function(value){
27383         if(this.allowBlank){
27384             return true;
27385         }
27386         
27387         if(this.isConfirmed){
27388             return true;
27389         }
27390         return false;
27391     }
27392 });/*
27393  * Based on:
27394  * Ext JS Library 1.1.1
27395  * Copyright(c) 2006-2007, Ext JS, LLC.
27396  *
27397  * Originally Released Under LGPL - original licence link has changed is not relivant.
27398  *
27399  * Fork - LGPL
27400  * <script type="text/javascript">
27401  */
27402  
27403
27404 /**
27405  * @class Roo.form.ComboBox
27406  * @extends Roo.form.TriggerField
27407  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27408  * @constructor
27409  * Create a new ComboBox.
27410  * @param {Object} config Configuration options
27411  */
27412 Roo.form.Select = function(config){
27413     Roo.form.Select.superclass.constructor.call(this, config);
27414      
27415 };
27416
27417 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27418     /**
27419      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27420      */
27421     /**
27422      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27423      * rendering into an Roo.Editor, defaults to false)
27424      */
27425     /**
27426      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27427      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27428      */
27429     /**
27430      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27431      */
27432     /**
27433      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27434      * the dropdown list (defaults to undefined, with no header element)
27435      */
27436
27437      /**
27438      * @cfg {String/Roo.Template} tpl The template to use to render the output
27439      */
27440      
27441     // private
27442     defaultAutoCreate : {tag: "select"  },
27443     /**
27444      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27445      */
27446     listWidth: undefined,
27447     /**
27448      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27449      * mode = 'remote' or 'text' if mode = 'local')
27450      */
27451     displayField: undefined,
27452     /**
27453      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27454      * mode = 'remote' or 'value' if mode = 'local'). 
27455      * Note: use of a valueField requires the user make a selection
27456      * in order for a value to be mapped.
27457      */
27458     valueField: undefined,
27459     
27460     
27461     /**
27462      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27463      * field's data value (defaults to the underlying DOM element's name)
27464      */
27465     hiddenName: undefined,
27466     /**
27467      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27468      */
27469     listClass: '',
27470     /**
27471      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27472      */
27473     selectedClass: 'x-combo-selected',
27474     /**
27475      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27476      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27477      * which displays a downward arrow icon).
27478      */
27479     triggerClass : 'x-form-arrow-trigger',
27480     /**
27481      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27482      */
27483     shadow:'sides',
27484     /**
27485      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27486      * anchor positions (defaults to 'tl-bl')
27487      */
27488     listAlign: 'tl-bl?',
27489     /**
27490      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27491      */
27492     maxHeight: 300,
27493     /**
27494      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27495      * query specified by the allQuery config option (defaults to 'query')
27496      */
27497     triggerAction: 'query',
27498     /**
27499      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27500      * (defaults to 4, does not apply if editable = false)
27501      */
27502     minChars : 4,
27503     /**
27504      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27505      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27506      */
27507     typeAhead: false,
27508     /**
27509      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27510      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27511      */
27512     queryDelay: 500,
27513     /**
27514      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27515      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27516      */
27517     pageSize: 0,
27518     /**
27519      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27520      * when editable = true (defaults to false)
27521      */
27522     selectOnFocus:false,
27523     /**
27524      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27525      */
27526     queryParam: 'query',
27527     /**
27528      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27529      * when mode = 'remote' (defaults to 'Loading...')
27530      */
27531     loadingText: 'Loading...',
27532     /**
27533      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27534      */
27535     resizable: false,
27536     /**
27537      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27538      */
27539     handleHeight : 8,
27540     /**
27541      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27542      * traditional select (defaults to true)
27543      */
27544     editable: true,
27545     /**
27546      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27547      */
27548     allQuery: '',
27549     /**
27550      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27551      */
27552     mode: 'remote',
27553     /**
27554      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27555      * listWidth has a higher value)
27556      */
27557     minListWidth : 70,
27558     /**
27559      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27560      * allow the user to set arbitrary text into the field (defaults to false)
27561      */
27562     forceSelection:false,
27563     /**
27564      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27565      * if typeAhead = true (defaults to 250)
27566      */
27567     typeAheadDelay : 250,
27568     /**
27569      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27570      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27571      */
27572     valueNotFoundText : undefined,
27573     
27574     /**
27575      * @cfg {String} defaultValue The value displayed after loading the store.
27576      */
27577     defaultValue: '',
27578     
27579     /**
27580      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27581      */
27582     blockFocus : false,
27583     
27584     /**
27585      * @cfg {Boolean} disableClear Disable showing of clear button.
27586      */
27587     disableClear : false,
27588     /**
27589      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27590      */
27591     alwaysQuery : false,
27592     
27593     //private
27594     addicon : false,
27595     editicon: false,
27596     
27597     // element that contains real text value.. (when hidden is used..)
27598      
27599     // private
27600     onRender : function(ct, position){
27601         Roo.form.Field.prototype.onRender.call(this, ct, position);
27602         
27603         if(this.store){
27604             this.store.on('beforeload', this.onBeforeLoad, this);
27605             this.store.on('load', this.onLoad, this);
27606             this.store.on('loadexception', this.onLoadException, this);
27607             this.store.load({});
27608         }
27609         
27610         
27611         
27612     },
27613
27614     // private
27615     initEvents : function(){
27616         //Roo.form.ComboBox.superclass.initEvents.call(this);
27617  
27618     },
27619
27620     onDestroy : function(){
27621        
27622         if(this.store){
27623             this.store.un('beforeload', this.onBeforeLoad, this);
27624             this.store.un('load', this.onLoad, this);
27625             this.store.un('loadexception', this.onLoadException, this);
27626         }
27627         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27628     },
27629
27630     // private
27631     fireKey : function(e){
27632         if(e.isNavKeyPress() && !this.list.isVisible()){
27633             this.fireEvent("specialkey", this, e);
27634         }
27635     },
27636
27637     // private
27638     onResize: function(w, h){
27639         
27640         return; 
27641     
27642         
27643     },
27644
27645     /**
27646      * Allow or prevent the user from directly editing the field text.  If false is passed,
27647      * the user will only be able to select from the items defined in the dropdown list.  This method
27648      * is the runtime equivalent of setting the 'editable' config option at config time.
27649      * @param {Boolean} value True to allow the user to directly edit the field text
27650      */
27651     setEditable : function(value){
27652          
27653     },
27654
27655     // private
27656     onBeforeLoad : function(){
27657         
27658         Roo.log("Select before load");
27659         return;
27660     
27661         this.innerList.update(this.loadingText ?
27662                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27663         //this.restrictHeight();
27664         this.selectedIndex = -1;
27665     },
27666
27667     // private
27668     onLoad : function(){
27669
27670     
27671         var dom = this.el.dom;
27672         dom.innerHTML = '';
27673          var od = dom.ownerDocument;
27674          
27675         if (this.emptyText) {
27676             var op = od.createElement('option');
27677             op.setAttribute('value', '');
27678             op.innerHTML = String.format('{0}', this.emptyText);
27679             dom.appendChild(op);
27680         }
27681         if(this.store.getCount() > 0){
27682            
27683             var vf = this.valueField;
27684             var df = this.displayField;
27685             this.store.data.each(function(r) {
27686                 // which colmsn to use... testing - cdoe / title..
27687                 var op = od.createElement('option');
27688                 op.setAttribute('value', r.data[vf]);
27689                 op.innerHTML = String.format('{0}', r.data[df]);
27690                 dom.appendChild(op);
27691             });
27692             if (typeof(this.defaultValue != 'undefined')) {
27693                 this.setValue(this.defaultValue);
27694             }
27695             
27696              
27697         }else{
27698             //this.onEmptyResults();
27699         }
27700         //this.el.focus();
27701     },
27702     // private
27703     onLoadException : function()
27704     {
27705         dom.innerHTML = '';
27706             
27707         Roo.log("Select on load exception");
27708         return;
27709     
27710         this.collapse();
27711         Roo.log(this.store.reader.jsonData);
27712         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27713             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27714         }
27715         
27716         
27717     },
27718     // private
27719     onTypeAhead : function(){
27720          
27721     },
27722
27723     // private
27724     onSelect : function(record, index){
27725         Roo.log('on select?');
27726         return;
27727         if(this.fireEvent('beforeselect', this, record, index) !== false){
27728             this.setFromData(index > -1 ? record.data : false);
27729             this.collapse();
27730             this.fireEvent('select', this, record, index);
27731         }
27732     },
27733
27734     /**
27735      * Returns the currently selected field value or empty string if no value is set.
27736      * @return {String} value The selected value
27737      */
27738     getValue : function(){
27739         var dom = this.el.dom;
27740         this.value = dom.options[dom.selectedIndex].value;
27741         return this.value;
27742         
27743     },
27744
27745     /**
27746      * Clears any text/value currently set in the field
27747      */
27748     clearValue : function(){
27749         this.value = '';
27750         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27751         
27752     },
27753
27754     /**
27755      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27756      * will be displayed in the field.  If the value does not match the data value of an existing item,
27757      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27758      * Otherwise the field will be blank (although the value will still be set).
27759      * @param {String} value The value to match
27760      */
27761     setValue : function(v){
27762         var d = this.el.dom;
27763         for (var i =0; i < d.options.length;i++) {
27764             if (v == d.options[i].value) {
27765                 d.selectedIndex = i;
27766                 this.value = v;
27767                 return;
27768             }
27769         }
27770         this.clearValue();
27771     },
27772     /**
27773      * @property {Object} the last set data for the element
27774      */
27775     
27776     lastData : false,
27777     /**
27778      * Sets the value of the field based on a object which is related to the record format for the store.
27779      * @param {Object} value the value to set as. or false on reset?
27780      */
27781     setFromData : function(o){
27782         Roo.log('setfrom data?');
27783          
27784         
27785         
27786     },
27787     // private
27788     reset : function(){
27789         this.clearValue();
27790     },
27791     // private
27792     findRecord : function(prop, value){
27793         
27794         return false;
27795     
27796         var record;
27797         if(this.store.getCount() > 0){
27798             this.store.each(function(r){
27799                 if(r.data[prop] == value){
27800                     record = r;
27801                     return false;
27802                 }
27803                 return true;
27804             });
27805         }
27806         return record;
27807     },
27808     
27809     getName: function()
27810     {
27811         // returns hidden if it's set..
27812         if (!this.rendered) {return ''};
27813         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27814         
27815     },
27816      
27817
27818     
27819
27820     // private
27821     onEmptyResults : function(){
27822         Roo.log('empty results');
27823         //this.collapse();
27824     },
27825
27826     /**
27827      * Returns true if the dropdown list is expanded, else false.
27828      */
27829     isExpanded : function(){
27830         return false;
27831     },
27832
27833     /**
27834      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27835      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27836      * @param {String} value The data value of the item to select
27837      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27838      * selected item if it is not currently in view (defaults to true)
27839      * @return {Boolean} True if the value matched an item in the list, else false
27840      */
27841     selectByValue : function(v, scrollIntoView){
27842         Roo.log('select By Value');
27843         return false;
27844     
27845         if(v !== undefined && v !== null){
27846             var r = this.findRecord(this.valueField || this.displayField, v);
27847             if(r){
27848                 this.select(this.store.indexOf(r), scrollIntoView);
27849                 return true;
27850             }
27851         }
27852         return false;
27853     },
27854
27855     /**
27856      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27857      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27858      * @param {Number} index The zero-based index of the list item to select
27859      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27860      * selected item if it is not currently in view (defaults to true)
27861      */
27862     select : function(index, scrollIntoView){
27863         Roo.log('select ');
27864         return  ;
27865         
27866         this.selectedIndex = index;
27867         this.view.select(index);
27868         if(scrollIntoView !== false){
27869             var el = this.view.getNode(index);
27870             if(el){
27871                 this.innerList.scrollChildIntoView(el, false);
27872             }
27873         }
27874     },
27875
27876       
27877
27878     // private
27879     validateBlur : function(){
27880         
27881         return;
27882         
27883     },
27884
27885     // private
27886     initQuery : function(){
27887         this.doQuery(this.getRawValue());
27888     },
27889
27890     // private
27891     doForce : function(){
27892         if(this.el.dom.value.length > 0){
27893             this.el.dom.value =
27894                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27895              
27896         }
27897     },
27898
27899     /**
27900      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27901      * query allowing the query action to be canceled if needed.
27902      * @param {String} query The SQL query to execute
27903      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27904      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27905      * saved in the current store (defaults to false)
27906      */
27907     doQuery : function(q, forceAll){
27908         
27909         Roo.log('doQuery?');
27910         if(q === undefined || q === null){
27911             q = '';
27912         }
27913         var qe = {
27914             query: q,
27915             forceAll: forceAll,
27916             combo: this,
27917             cancel:false
27918         };
27919         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27920             return false;
27921         }
27922         q = qe.query;
27923         forceAll = qe.forceAll;
27924         if(forceAll === true || (q.length >= this.minChars)){
27925             if(this.lastQuery != q || this.alwaysQuery){
27926                 this.lastQuery = q;
27927                 if(this.mode == 'local'){
27928                     this.selectedIndex = -1;
27929                     if(forceAll){
27930                         this.store.clearFilter();
27931                     }else{
27932                         this.store.filter(this.displayField, q);
27933                     }
27934                     this.onLoad();
27935                 }else{
27936                     this.store.baseParams[this.queryParam] = q;
27937                     this.store.load({
27938                         params: this.getParams(q)
27939                     });
27940                     this.expand();
27941                 }
27942             }else{
27943                 this.selectedIndex = -1;
27944                 this.onLoad();   
27945             }
27946         }
27947     },
27948
27949     // private
27950     getParams : function(q){
27951         var p = {};
27952         //p[this.queryParam] = q;
27953         if(this.pageSize){
27954             p.start = 0;
27955             p.limit = this.pageSize;
27956         }
27957         return p;
27958     },
27959
27960     /**
27961      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27962      */
27963     collapse : function(){
27964         
27965     },
27966
27967     // private
27968     collapseIf : function(e){
27969         
27970     },
27971
27972     /**
27973      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27974      */
27975     expand : function(){
27976         
27977     } ,
27978
27979     // private
27980      
27981
27982     /** 
27983     * @cfg {Boolean} grow 
27984     * @hide 
27985     */
27986     /** 
27987     * @cfg {Number} growMin 
27988     * @hide 
27989     */
27990     /** 
27991     * @cfg {Number} growMax 
27992     * @hide 
27993     */
27994     /**
27995      * @hide
27996      * @method autoSize
27997      */
27998     
27999     setWidth : function()
28000     {
28001         
28002     },
28003     getResizeEl : function(){
28004         return this.el;
28005     }
28006 });//<script type="text/javasscript">
28007  
28008
28009 /**
28010  * @class Roo.DDView
28011  * A DnD enabled version of Roo.View.
28012  * @param {Element/String} container The Element in which to create the View.
28013  * @param {String} tpl The template string used to create the markup for each element of the View
28014  * @param {Object} config The configuration properties. These include all the config options of
28015  * {@link Roo.View} plus some specific to this class.<br>
28016  * <p>
28017  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28018  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28019  * <p>
28020  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28021 .x-view-drag-insert-above {
28022         border-top:1px dotted #3366cc;
28023 }
28024 .x-view-drag-insert-below {
28025         border-bottom:1px dotted #3366cc;
28026 }
28027 </code></pre>
28028  * 
28029  */
28030  
28031 Roo.DDView = function(container, tpl, config) {
28032     Roo.DDView.superclass.constructor.apply(this, arguments);
28033     this.getEl().setStyle("outline", "0px none");
28034     this.getEl().unselectable();
28035     if (this.dragGroup) {
28036                 this.setDraggable(this.dragGroup.split(","));
28037     }
28038     if (this.dropGroup) {
28039                 this.setDroppable(this.dropGroup.split(","));
28040     }
28041     if (this.deletable) {
28042         this.setDeletable();
28043     }
28044     this.isDirtyFlag = false;
28045         this.addEvents({
28046                 "drop" : true
28047         });
28048 };
28049
28050 Roo.extend(Roo.DDView, Roo.View, {
28051 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28052 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28053 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28054 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28055
28056         isFormField: true,
28057
28058         reset: Roo.emptyFn,
28059         
28060         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28061
28062         validate: function() {
28063                 return true;
28064         },
28065         
28066         destroy: function() {
28067                 this.purgeListeners();
28068                 this.getEl.removeAllListeners();
28069                 this.getEl().remove();
28070                 if (this.dragZone) {
28071                         if (this.dragZone.destroy) {
28072                                 this.dragZone.destroy();
28073                         }
28074                 }
28075                 if (this.dropZone) {
28076                         if (this.dropZone.destroy) {
28077                                 this.dropZone.destroy();
28078                         }
28079                 }
28080         },
28081
28082 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28083         getName: function() {
28084                 return this.name;
28085         },
28086
28087 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28088         setValue: function(v) {
28089                 if (!this.store) {
28090                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28091                 }
28092                 var data = {};
28093                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28094                 this.store.proxy = new Roo.data.MemoryProxy(data);
28095                 this.store.load();
28096         },
28097
28098 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28099         getValue: function() {
28100                 var result = '(';
28101                 this.store.each(function(rec) {
28102                         result += rec.id + ',';
28103                 });
28104                 return result.substr(0, result.length - 1) + ')';
28105         },
28106         
28107         getIds: function() {
28108                 var i = 0, result = new Array(this.store.getCount());
28109                 this.store.each(function(rec) {
28110                         result[i++] = rec.id;
28111                 });
28112                 return result;
28113         },
28114         
28115         isDirty: function() {
28116                 return this.isDirtyFlag;
28117         },
28118
28119 /**
28120  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28121  *      whole Element becomes the target, and this causes the drop gesture to append.
28122  */
28123     getTargetFromEvent : function(e) {
28124                 var target = e.getTarget();
28125                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28126                 target = target.parentNode;
28127                 }
28128                 if (!target) {
28129                         target = this.el.dom.lastChild || this.el.dom;
28130                 }
28131                 return target;
28132     },
28133
28134 /**
28135  *      Create the drag data which consists of an object which has the property "ddel" as
28136  *      the drag proxy element. 
28137  */
28138     getDragData : function(e) {
28139         var target = this.findItemFromChild(e.getTarget());
28140                 if(target) {
28141                         this.handleSelection(e);
28142                         var selNodes = this.getSelectedNodes();
28143             var dragData = {
28144                 source: this,
28145                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28146                 nodes: selNodes,
28147                 records: []
28148                         };
28149                         var selectedIndices = this.getSelectedIndexes();
28150                         for (var i = 0; i < selectedIndices.length; i++) {
28151                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28152                         }
28153                         if (selNodes.length == 1) {
28154                                 dragData.ddel = target.cloneNode(true); // the div element
28155                         } else {
28156                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28157                                 div.className = 'multi-proxy';
28158                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28159                                         div.appendChild(selNodes[i].cloneNode(true));
28160                                 }
28161                                 dragData.ddel = div;
28162                         }
28163             //console.log(dragData)
28164             //console.log(dragData.ddel.innerHTML)
28165                         return dragData;
28166                 }
28167         //console.log('nodragData')
28168                 return false;
28169     },
28170     
28171 /**     Specify to which ddGroup items in this DDView may be dragged. */
28172     setDraggable: function(ddGroup) {
28173         if (ddGroup instanceof Array) {
28174                 Roo.each(ddGroup, this.setDraggable, this);
28175                 return;
28176         }
28177         if (this.dragZone) {
28178                 this.dragZone.addToGroup(ddGroup);
28179         } else {
28180                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28181                                 containerScroll: true,
28182                                 ddGroup: ddGroup 
28183
28184                         });
28185 //                      Draggability implies selection. DragZone's mousedown selects the element.
28186                         if (!this.multiSelect) { this.singleSelect = true; }
28187
28188 //                      Wire the DragZone's handlers up to methods in *this*
28189                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28190                 }
28191     },
28192
28193 /**     Specify from which ddGroup this DDView accepts drops. */
28194     setDroppable: function(ddGroup) {
28195         if (ddGroup instanceof Array) {
28196                 Roo.each(ddGroup, this.setDroppable, this);
28197                 return;
28198         }
28199         if (this.dropZone) {
28200                 this.dropZone.addToGroup(ddGroup);
28201         } else {
28202                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28203                                 containerScroll: true,
28204                                 ddGroup: ddGroup
28205                         });
28206
28207 //                      Wire the DropZone's handlers up to methods in *this*
28208                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28209                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28210                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28211                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28212                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28213                 }
28214     },
28215
28216 /**     Decide whether to drop above or below a View node. */
28217     getDropPoint : function(e, n, dd){
28218         if (n == this.el.dom) { return "above"; }
28219                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28220                 var c = t + (b - t) / 2;
28221                 var y = Roo.lib.Event.getPageY(e);
28222                 if(y <= c) {
28223                         return "above";
28224                 }else{
28225                         return "below";
28226                 }
28227     },
28228
28229     onNodeEnter : function(n, dd, e, data){
28230                 return false;
28231     },
28232     
28233     onNodeOver : function(n, dd, e, data){
28234                 var pt = this.getDropPoint(e, n, dd);
28235                 // set the insert point style on the target node
28236                 var dragElClass = this.dropNotAllowed;
28237                 if (pt) {
28238                         var targetElClass;
28239                         if (pt == "above"){
28240                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28241                                 targetElClass = "x-view-drag-insert-above";
28242                         } else {
28243                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28244                                 targetElClass = "x-view-drag-insert-below";
28245                         }
28246                         if (this.lastInsertClass != targetElClass){
28247                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28248                                 this.lastInsertClass = targetElClass;
28249                         }
28250                 }
28251                 return dragElClass;
28252         },
28253
28254     onNodeOut : function(n, dd, e, data){
28255                 this.removeDropIndicators(n);
28256     },
28257
28258     onNodeDrop : function(n, dd, e, data){
28259         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28260                 return false;
28261         }
28262         var pt = this.getDropPoint(e, n, dd);
28263                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28264                 if (pt == "below") { insertAt++; }
28265                 for (var i = 0; i < data.records.length; i++) {
28266                         var r = data.records[i];
28267                         var dup = this.store.getById(r.id);
28268                         if (dup && (dd != this.dragZone)) {
28269                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28270                         } else {
28271                                 if (data.copy) {
28272                                         this.store.insert(insertAt++, r.copy());
28273                                 } else {
28274                                         data.source.isDirtyFlag = true;
28275                                         r.store.remove(r);
28276                                         this.store.insert(insertAt++, r);
28277                                 }
28278                                 this.isDirtyFlag = true;
28279                         }
28280                 }
28281                 this.dragZone.cachedTarget = null;
28282                 return true;
28283     },
28284
28285     removeDropIndicators : function(n){
28286                 if(n){
28287                         Roo.fly(n).removeClass([
28288                                 "x-view-drag-insert-above",
28289                                 "x-view-drag-insert-below"]);
28290                         this.lastInsertClass = "_noclass";
28291                 }
28292     },
28293
28294 /**
28295  *      Utility method. Add a delete option to the DDView's context menu.
28296  *      @param {String} imageUrl The URL of the "delete" icon image.
28297  */
28298         setDeletable: function(imageUrl) {
28299                 if (!this.singleSelect && !this.multiSelect) {
28300                         this.singleSelect = true;
28301                 }
28302                 var c = this.getContextMenu();
28303                 this.contextMenu.on("itemclick", function(item) {
28304                         switch (item.id) {
28305                                 case "delete":
28306                                         this.remove(this.getSelectedIndexes());
28307                                         break;
28308                         }
28309                 }, this);
28310                 this.contextMenu.add({
28311                         icon: imageUrl,
28312                         id: "delete",
28313                         text: 'Delete'
28314                 });
28315         },
28316         
28317 /**     Return the context menu for this DDView. */
28318         getContextMenu: function() {
28319                 if (!this.contextMenu) {
28320 //                      Create the View's context menu
28321                         this.contextMenu = new Roo.menu.Menu({
28322                                 id: this.id + "-contextmenu"
28323                         });
28324                         this.el.on("contextmenu", this.showContextMenu, this);
28325                 }
28326                 return this.contextMenu;
28327         },
28328         
28329         disableContextMenu: function() {
28330                 if (this.contextMenu) {
28331                         this.el.un("contextmenu", this.showContextMenu, this);
28332                 }
28333         },
28334
28335         showContextMenu: function(e, item) {
28336         item = this.findItemFromChild(e.getTarget());
28337                 if (item) {
28338                         e.stopEvent();
28339                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28340                         this.contextMenu.showAt(e.getXY());
28341             }
28342     },
28343
28344 /**
28345  *      Remove {@link Roo.data.Record}s at the specified indices.
28346  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28347  */
28348     remove: function(selectedIndices) {
28349                 selectedIndices = [].concat(selectedIndices);
28350                 for (var i = 0; i < selectedIndices.length; i++) {
28351                         var rec = this.store.getAt(selectedIndices[i]);
28352                         this.store.remove(rec);
28353                 }
28354     },
28355
28356 /**
28357  *      Double click fires the event, but also, if this is draggable, and there is only one other
28358  *      related DropZone, it transfers the selected node.
28359  */
28360     onDblClick : function(e){
28361         var item = this.findItemFromChild(e.getTarget());
28362         if(item){
28363             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28364                 return false;
28365             }
28366             if (this.dragGroup) {
28367                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28368                     while (targets.indexOf(this.dropZone) > -1) {
28369                             targets.remove(this.dropZone);
28370                                 }
28371                     if (targets.length == 1) {
28372                                         this.dragZone.cachedTarget = null;
28373                         var el = Roo.get(targets[0].getEl());
28374                         var box = el.getBox(true);
28375                         targets[0].onNodeDrop(el.dom, {
28376                                 target: el.dom,
28377                                 xy: [box.x, box.y + box.height - 1]
28378                         }, null, this.getDragData(e));
28379                     }
28380                 }
28381         }
28382     },
28383     
28384     handleSelection: function(e) {
28385                 this.dragZone.cachedTarget = null;
28386         var item = this.findItemFromChild(e.getTarget());
28387         if (!item) {
28388                 this.clearSelections(true);
28389                 return;
28390         }
28391                 if (item && (this.multiSelect || this.singleSelect)){
28392                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28393                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28394                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28395                                 this.unselect(item);
28396                         } else {
28397                                 this.select(item, this.multiSelect && e.ctrlKey);
28398                                 this.lastSelection = item;
28399                         }
28400                 }
28401     },
28402
28403     onItemClick : function(item, index, e){
28404                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28405                         return false;
28406                 }
28407                 return true;
28408     },
28409
28410     unselect : function(nodeInfo, suppressEvent){
28411                 var node = this.getNode(nodeInfo);
28412                 if(node && this.isSelected(node)){
28413                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28414                                 Roo.fly(node).removeClass(this.selectedClass);
28415                                 this.selections.remove(node);
28416                                 if(!suppressEvent){
28417                                         this.fireEvent("selectionchange", this, this.selections);
28418                                 }
28419                         }
28420                 }
28421     }
28422 });
28423 /*
28424  * Based on:
28425  * Ext JS Library 1.1.1
28426  * Copyright(c) 2006-2007, Ext JS, LLC.
28427  *
28428  * Originally Released Under LGPL - original licence link has changed is not relivant.
28429  *
28430  * Fork - LGPL
28431  * <script type="text/javascript">
28432  */
28433  
28434 /**
28435  * @class Roo.LayoutManager
28436  * @extends Roo.util.Observable
28437  * Base class for layout managers.
28438  */
28439 Roo.LayoutManager = function(container, config){
28440     Roo.LayoutManager.superclass.constructor.call(this);
28441     this.el = Roo.get(container);
28442     // ie scrollbar fix
28443     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28444         document.body.scroll = "no";
28445     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28446         this.el.position('relative');
28447     }
28448     this.id = this.el.id;
28449     this.el.addClass("x-layout-container");
28450     /** false to disable window resize monitoring @type Boolean */
28451     this.monitorWindowResize = true;
28452     this.regions = {};
28453     this.addEvents({
28454         /**
28455          * @event layout
28456          * Fires when a layout is performed. 
28457          * @param {Roo.LayoutManager} this
28458          */
28459         "layout" : true,
28460         /**
28461          * @event regionresized
28462          * Fires when the user resizes a region. 
28463          * @param {Roo.LayoutRegion} region The resized region
28464          * @param {Number} newSize The new size (width for east/west, height for north/south)
28465          */
28466         "regionresized" : true,
28467         /**
28468          * @event regioncollapsed
28469          * Fires when a region is collapsed. 
28470          * @param {Roo.LayoutRegion} region The collapsed region
28471          */
28472         "regioncollapsed" : true,
28473         /**
28474          * @event regionexpanded
28475          * Fires when a region is expanded.  
28476          * @param {Roo.LayoutRegion} region The expanded region
28477          */
28478         "regionexpanded" : true
28479     });
28480     this.updating = false;
28481     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28482 };
28483
28484 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28485     /**
28486      * Returns true if this layout is currently being updated
28487      * @return {Boolean}
28488      */
28489     isUpdating : function(){
28490         return this.updating; 
28491     },
28492     
28493     /**
28494      * Suspend the LayoutManager from doing auto-layouts while
28495      * making multiple add or remove calls
28496      */
28497     beginUpdate : function(){
28498         this.updating = true;    
28499     },
28500     
28501     /**
28502      * Restore auto-layouts and optionally disable the manager from performing a layout
28503      * @param {Boolean} noLayout true to disable a layout update 
28504      */
28505     endUpdate : function(noLayout){
28506         this.updating = false;
28507         if(!noLayout){
28508             this.layout();
28509         }    
28510     },
28511     
28512     layout: function(){
28513         
28514     },
28515     
28516     onRegionResized : function(region, newSize){
28517         this.fireEvent("regionresized", region, newSize);
28518         this.layout();
28519     },
28520     
28521     onRegionCollapsed : function(region){
28522         this.fireEvent("regioncollapsed", region);
28523     },
28524     
28525     onRegionExpanded : function(region){
28526         this.fireEvent("regionexpanded", region);
28527     },
28528         
28529     /**
28530      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28531      * performs box-model adjustments.
28532      * @return {Object} The size as an object {width: (the width), height: (the height)}
28533      */
28534     getViewSize : function(){
28535         var size;
28536         if(this.el.dom != document.body){
28537             size = this.el.getSize();
28538         }else{
28539             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28540         }
28541         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28542         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28543         return size;
28544     },
28545     
28546     /**
28547      * Returns the Element this layout is bound to.
28548      * @return {Roo.Element}
28549      */
28550     getEl : function(){
28551         return this.el;
28552     },
28553     
28554     /**
28555      * Returns the specified region.
28556      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28557      * @return {Roo.LayoutRegion}
28558      */
28559     getRegion : function(target){
28560         return this.regions[target.toLowerCase()];
28561     },
28562     
28563     onWindowResize : function(){
28564         if(this.monitorWindowResize){
28565             this.layout();
28566         }
28567     }
28568 });/*
28569  * Based on:
28570  * Ext JS Library 1.1.1
28571  * Copyright(c) 2006-2007, Ext JS, LLC.
28572  *
28573  * Originally Released Under LGPL - original licence link has changed is not relivant.
28574  *
28575  * Fork - LGPL
28576  * <script type="text/javascript">
28577  */
28578 /**
28579  * @class Roo.BorderLayout
28580  * @extends Roo.LayoutManager
28581  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28582  * please see: <br><br>
28583  * <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>
28584  * <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>
28585  * Example:
28586  <pre><code>
28587  var layout = new Roo.BorderLayout(document.body, {
28588     north: {
28589         initialSize: 25,
28590         titlebar: false
28591     },
28592     west: {
28593         split:true,
28594         initialSize: 200,
28595         minSize: 175,
28596         maxSize: 400,
28597         titlebar: true,
28598         collapsible: true
28599     },
28600     east: {
28601         split:true,
28602         initialSize: 202,
28603         minSize: 175,
28604         maxSize: 400,
28605         titlebar: true,
28606         collapsible: true
28607     },
28608     south: {
28609         split:true,
28610         initialSize: 100,
28611         minSize: 100,
28612         maxSize: 200,
28613         titlebar: true,
28614         collapsible: true
28615     },
28616     center: {
28617         titlebar: true,
28618         autoScroll:true,
28619         resizeTabs: true,
28620         minTabWidth: 50,
28621         preferredTabWidth: 150
28622     }
28623 });
28624
28625 // shorthand
28626 var CP = Roo.ContentPanel;
28627
28628 layout.beginUpdate();
28629 layout.add("north", new CP("north", "North"));
28630 layout.add("south", new CP("south", {title: "South", closable: true}));
28631 layout.add("west", new CP("west", {title: "West"}));
28632 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28633 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28634 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28635 layout.getRegion("center").showPanel("center1");
28636 layout.endUpdate();
28637 </code></pre>
28638
28639 <b>The container the layout is rendered into can be either the body element or any other element.
28640 If it is not the body element, the container needs to either be an absolute positioned element,
28641 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28642 the container size if it is not the body element.</b>
28643
28644 * @constructor
28645 * Create a new BorderLayout
28646 * @param {String/HTMLElement/Element} container The container this layout is bound to
28647 * @param {Object} config Configuration options
28648  */
28649 Roo.BorderLayout = function(container, config){
28650     config = config || {};
28651     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28652     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28653     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28654         var target = this.factory.validRegions[i];
28655         if(config[target]){
28656             this.addRegion(target, config[target]);
28657         }
28658     }
28659 };
28660
28661 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28662     /**
28663      * Creates and adds a new region if it doesn't already exist.
28664      * @param {String} target The target region key (north, south, east, west or center).
28665      * @param {Object} config The regions config object
28666      * @return {BorderLayoutRegion} The new region
28667      */
28668     addRegion : function(target, config){
28669         if(!this.regions[target]){
28670             var r = this.factory.create(target, this, config);
28671             this.bindRegion(target, r);
28672         }
28673         return this.regions[target];
28674     },
28675
28676     // private (kinda)
28677     bindRegion : function(name, r){
28678         this.regions[name] = r;
28679         r.on("visibilitychange", this.layout, this);
28680         r.on("paneladded", this.layout, this);
28681         r.on("panelremoved", this.layout, this);
28682         r.on("invalidated", this.layout, this);
28683         r.on("resized", this.onRegionResized, this);
28684         r.on("collapsed", this.onRegionCollapsed, this);
28685         r.on("expanded", this.onRegionExpanded, this);
28686     },
28687
28688     /**
28689      * Performs a layout update.
28690      */
28691     layout : function(){
28692         if(this.updating) {
28693             return;
28694         }
28695         var size = this.getViewSize();
28696         var w = size.width;
28697         var h = size.height;
28698         var centerW = w;
28699         var centerH = h;
28700         var centerY = 0;
28701         var centerX = 0;
28702         //var x = 0, y = 0;
28703
28704         var rs = this.regions;
28705         var north = rs["north"];
28706         var south = rs["south"]; 
28707         var west = rs["west"];
28708         var east = rs["east"];
28709         var center = rs["center"];
28710         //if(this.hideOnLayout){ // not supported anymore
28711             //c.el.setStyle("display", "none");
28712         //}
28713         if(north && north.isVisible()){
28714             var b = north.getBox();
28715             var m = north.getMargins();
28716             b.width = w - (m.left+m.right);
28717             b.x = m.left;
28718             b.y = m.top;
28719             centerY = b.height + b.y + m.bottom;
28720             centerH -= centerY;
28721             north.updateBox(this.safeBox(b));
28722         }
28723         if(south && south.isVisible()){
28724             var b = south.getBox();
28725             var m = south.getMargins();
28726             b.width = w - (m.left+m.right);
28727             b.x = m.left;
28728             var totalHeight = (b.height + m.top + m.bottom);
28729             b.y = h - totalHeight + m.top;
28730             centerH -= totalHeight;
28731             south.updateBox(this.safeBox(b));
28732         }
28733         if(west && west.isVisible()){
28734             var b = west.getBox();
28735             var m = west.getMargins();
28736             b.height = centerH - (m.top+m.bottom);
28737             b.x = m.left;
28738             b.y = centerY + m.top;
28739             var totalWidth = (b.width + m.left + m.right);
28740             centerX += totalWidth;
28741             centerW -= totalWidth;
28742             west.updateBox(this.safeBox(b));
28743         }
28744         if(east && east.isVisible()){
28745             var b = east.getBox();
28746             var m = east.getMargins();
28747             b.height = centerH - (m.top+m.bottom);
28748             var totalWidth = (b.width + m.left + m.right);
28749             b.x = w - totalWidth + m.left;
28750             b.y = centerY + m.top;
28751             centerW -= totalWidth;
28752             east.updateBox(this.safeBox(b));
28753         }
28754         if(center){
28755             var m = center.getMargins();
28756             var centerBox = {
28757                 x: centerX + m.left,
28758                 y: centerY + m.top,
28759                 width: centerW - (m.left+m.right),
28760                 height: centerH - (m.top+m.bottom)
28761             };
28762             //if(this.hideOnLayout){
28763                 //center.el.setStyle("display", "block");
28764             //}
28765             center.updateBox(this.safeBox(centerBox));
28766         }
28767         this.el.repaint();
28768         this.fireEvent("layout", this);
28769     },
28770
28771     // private
28772     safeBox : function(box){
28773         box.width = Math.max(0, box.width);
28774         box.height = Math.max(0, box.height);
28775         return box;
28776     },
28777
28778     /**
28779      * Adds a ContentPanel (or subclass) to this layout.
28780      * @param {String} target The target region key (north, south, east, west or center).
28781      * @param {Roo.ContentPanel} panel The panel to add
28782      * @return {Roo.ContentPanel} The added panel
28783      */
28784     add : function(target, panel){
28785          
28786         target = target.toLowerCase();
28787         return this.regions[target].add(panel);
28788     },
28789
28790     /**
28791      * Remove a ContentPanel (or subclass) to this layout.
28792      * @param {String} target The target region key (north, south, east, west or center).
28793      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28794      * @return {Roo.ContentPanel} The removed panel
28795      */
28796     remove : function(target, panel){
28797         target = target.toLowerCase();
28798         return this.regions[target].remove(panel);
28799     },
28800
28801     /**
28802      * Searches all regions for a panel with the specified id
28803      * @param {String} panelId
28804      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28805      */
28806     findPanel : function(panelId){
28807         var rs = this.regions;
28808         for(var target in rs){
28809             if(typeof rs[target] != "function"){
28810                 var p = rs[target].getPanel(panelId);
28811                 if(p){
28812                     return p;
28813                 }
28814             }
28815         }
28816         return null;
28817     },
28818
28819     /**
28820      * Searches all regions for a panel with the specified id and activates (shows) it.
28821      * @param {String/ContentPanel} panelId The panels id or the panel itself
28822      * @return {Roo.ContentPanel} The shown panel or null
28823      */
28824     showPanel : function(panelId) {
28825       var rs = this.regions;
28826       for(var target in rs){
28827          var r = rs[target];
28828          if(typeof r != "function"){
28829             if(r.hasPanel(panelId)){
28830                return r.showPanel(panelId);
28831             }
28832          }
28833       }
28834       return null;
28835    },
28836
28837    /**
28838      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28839      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28840      */
28841     restoreState : function(provider){
28842         if(!provider){
28843             provider = Roo.state.Manager;
28844         }
28845         var sm = new Roo.LayoutStateManager();
28846         sm.init(this, provider);
28847     },
28848
28849     /**
28850      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28851      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28852      * a valid ContentPanel config object.  Example:
28853      * <pre><code>
28854 // Create the main layout
28855 var layout = new Roo.BorderLayout('main-ct', {
28856     west: {
28857         split:true,
28858         minSize: 175,
28859         titlebar: true
28860     },
28861     center: {
28862         title:'Components'
28863     }
28864 }, 'main-ct');
28865
28866 // Create and add multiple ContentPanels at once via configs
28867 layout.batchAdd({
28868    west: {
28869        id: 'source-files',
28870        autoCreate:true,
28871        title:'Ext Source Files',
28872        autoScroll:true,
28873        fitToFrame:true
28874    },
28875    center : {
28876        el: cview,
28877        autoScroll:true,
28878        fitToFrame:true,
28879        toolbar: tb,
28880        resizeEl:'cbody'
28881    }
28882 });
28883 </code></pre>
28884      * @param {Object} regions An object containing ContentPanel configs by region name
28885      */
28886     batchAdd : function(regions){
28887         this.beginUpdate();
28888         for(var rname in regions){
28889             var lr = this.regions[rname];
28890             if(lr){
28891                 this.addTypedPanels(lr, regions[rname]);
28892             }
28893         }
28894         this.endUpdate();
28895     },
28896
28897     // private
28898     addTypedPanels : function(lr, ps){
28899         if(typeof ps == 'string'){
28900             lr.add(new Roo.ContentPanel(ps));
28901         }
28902         else if(ps instanceof Array){
28903             for(var i =0, len = ps.length; i < len; i++){
28904                 this.addTypedPanels(lr, ps[i]);
28905             }
28906         }
28907         else if(!ps.events){ // raw config?
28908             var el = ps.el;
28909             delete ps.el; // prevent conflict
28910             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28911         }
28912         else {  // panel object assumed!
28913             lr.add(ps);
28914         }
28915     },
28916     /**
28917      * Adds a xtype elements to the layout.
28918      * <pre><code>
28919
28920 layout.addxtype({
28921        xtype : 'ContentPanel',
28922        region: 'west',
28923        items: [ .... ]
28924    }
28925 );
28926
28927 layout.addxtype({
28928         xtype : 'NestedLayoutPanel',
28929         region: 'west',
28930         layout: {
28931            center: { },
28932            west: { }   
28933         },
28934         items : [ ... list of content panels or nested layout panels.. ]
28935    }
28936 );
28937 </code></pre>
28938      * @param {Object} cfg Xtype definition of item to add.
28939      */
28940     addxtype : function(cfg)
28941     {
28942         // basically accepts a pannel...
28943         // can accept a layout region..!?!?
28944         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28945         
28946         if (!cfg.xtype.match(/Panel$/)) {
28947             return false;
28948         }
28949         var ret = false;
28950         
28951         if (typeof(cfg.region) == 'undefined') {
28952             Roo.log("Failed to add Panel, region was not set");
28953             Roo.log(cfg);
28954             return false;
28955         }
28956         var region = cfg.region;
28957         delete cfg.region;
28958         
28959           
28960         var xitems = [];
28961         if (cfg.items) {
28962             xitems = cfg.items;
28963             delete cfg.items;
28964         }
28965         var nb = false;
28966         
28967         switch(cfg.xtype) 
28968         {
28969             case 'ContentPanel':  // ContentPanel (el, cfg)
28970             case 'ScrollPanel':  // ContentPanel (el, cfg)
28971             case 'ViewPanel': 
28972                 if(cfg.autoCreate) {
28973                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28974                 } else {
28975                     var el = this.el.createChild();
28976                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28977                 }
28978                 
28979                 this.add(region, ret);
28980                 break;
28981             
28982             
28983             case 'TreePanel': // our new panel!
28984                 cfg.el = this.el.createChild();
28985                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28986                 this.add(region, ret);
28987                 break;
28988             
28989             case 'NestedLayoutPanel': 
28990                 // create a new Layout (which is  a Border Layout...
28991                 var el = this.el.createChild();
28992                 var clayout = cfg.layout;
28993                 delete cfg.layout;
28994                 clayout.items   = clayout.items  || [];
28995                 // replace this exitems with the clayout ones..
28996                 xitems = clayout.items;
28997                  
28998                 
28999                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29000                     cfg.background = false;
29001                 }
29002                 var layout = new Roo.BorderLayout(el, clayout);
29003                 
29004                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29005                 //console.log('adding nested layout panel '  + cfg.toSource());
29006                 this.add(region, ret);
29007                 nb = {}; /// find first...
29008                 break;
29009                 
29010             case 'GridPanel': 
29011             
29012                 // needs grid and region
29013                 
29014                 //var el = this.getRegion(region).el.createChild();
29015                 var el = this.el.createChild();
29016                 // create the grid first...
29017                 
29018                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29019                 delete cfg.grid;
29020                 if (region == 'center' && this.active ) {
29021                     cfg.background = false;
29022                 }
29023                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29024                 
29025                 this.add(region, ret);
29026                 if (cfg.background) {
29027                     ret.on('activate', function(gp) {
29028                         if (!gp.grid.rendered) {
29029                             gp.grid.render();
29030                         }
29031                     });
29032                 } else {
29033                     grid.render();
29034                 }
29035                 break;
29036            
29037            
29038            
29039                 
29040                 
29041                 
29042             default:
29043                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29044                     
29045                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29046                     this.add(region, ret);
29047                 } else {
29048                 
29049                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29050                     return null;
29051                 }
29052                 
29053              // GridPanel (grid, cfg)
29054             
29055         }
29056         this.beginUpdate();
29057         // add children..
29058         var region = '';
29059         var abn = {};
29060         Roo.each(xitems, function(i)  {
29061             region = nb && i.region ? i.region : false;
29062             
29063             var add = ret.addxtype(i);
29064            
29065             if (region) {
29066                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29067                 if (!i.background) {
29068                     abn[region] = nb[region] ;
29069                 }
29070             }
29071             
29072         });
29073         this.endUpdate();
29074
29075         // make the last non-background panel active..
29076         //if (nb) { Roo.log(abn); }
29077         if (nb) {
29078             
29079             for(var r in abn) {
29080                 region = this.getRegion(r);
29081                 if (region) {
29082                     // tried using nb[r], but it does not work..
29083                      
29084                     region.showPanel(abn[r]);
29085                    
29086                 }
29087             }
29088         }
29089         return ret;
29090         
29091     }
29092 });
29093
29094 /**
29095  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29096  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29097  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29098  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29099  * <pre><code>
29100 // shorthand
29101 var CP = Roo.ContentPanel;
29102
29103 var layout = Roo.BorderLayout.create({
29104     north: {
29105         initialSize: 25,
29106         titlebar: false,
29107         panels: [new CP("north", "North")]
29108     },
29109     west: {
29110         split:true,
29111         initialSize: 200,
29112         minSize: 175,
29113         maxSize: 400,
29114         titlebar: true,
29115         collapsible: true,
29116         panels: [new CP("west", {title: "West"})]
29117     },
29118     east: {
29119         split:true,
29120         initialSize: 202,
29121         minSize: 175,
29122         maxSize: 400,
29123         titlebar: true,
29124         collapsible: true,
29125         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29126     },
29127     south: {
29128         split:true,
29129         initialSize: 100,
29130         minSize: 100,
29131         maxSize: 200,
29132         titlebar: true,
29133         collapsible: true,
29134         panels: [new CP("south", {title: "South", closable: true})]
29135     },
29136     center: {
29137         titlebar: true,
29138         autoScroll:true,
29139         resizeTabs: true,
29140         minTabWidth: 50,
29141         preferredTabWidth: 150,
29142         panels: [
29143             new CP("center1", {title: "Close Me", closable: true}),
29144             new CP("center2", {title: "Center Panel", closable: false})
29145         ]
29146     }
29147 }, document.body);
29148
29149 layout.getRegion("center").showPanel("center1");
29150 </code></pre>
29151  * @param config
29152  * @param targetEl
29153  */
29154 Roo.BorderLayout.create = function(config, targetEl){
29155     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29156     layout.beginUpdate();
29157     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29158     for(var j = 0, jlen = regions.length; j < jlen; j++){
29159         var lr = regions[j];
29160         if(layout.regions[lr] && config[lr].panels){
29161             var r = layout.regions[lr];
29162             var ps = config[lr].panels;
29163             layout.addTypedPanels(r, ps);
29164         }
29165     }
29166     layout.endUpdate();
29167     return layout;
29168 };
29169
29170 // private
29171 Roo.BorderLayout.RegionFactory = {
29172     // private
29173     validRegions : ["north","south","east","west","center"],
29174
29175     // private
29176     create : function(target, mgr, config){
29177         target = target.toLowerCase();
29178         if(config.lightweight || config.basic){
29179             return new Roo.BasicLayoutRegion(mgr, config, target);
29180         }
29181         switch(target){
29182             case "north":
29183                 return new Roo.NorthLayoutRegion(mgr, config);
29184             case "south":
29185                 return new Roo.SouthLayoutRegion(mgr, config);
29186             case "east":
29187                 return new Roo.EastLayoutRegion(mgr, config);
29188             case "west":
29189                 return new Roo.WestLayoutRegion(mgr, config);
29190             case "center":
29191                 return new Roo.CenterLayoutRegion(mgr, config);
29192         }
29193         throw 'Layout region "'+target+'" not supported.';
29194     }
29195 };/*
29196  * Based on:
29197  * Ext JS Library 1.1.1
29198  * Copyright(c) 2006-2007, Ext JS, LLC.
29199  *
29200  * Originally Released Under LGPL - original licence link has changed is not relivant.
29201  *
29202  * Fork - LGPL
29203  * <script type="text/javascript">
29204  */
29205  
29206 /**
29207  * @class Roo.BasicLayoutRegion
29208  * @extends Roo.util.Observable
29209  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29210  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29211  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29212  */
29213 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29214     this.mgr = mgr;
29215     this.position  = pos;
29216     this.events = {
29217         /**
29218          * @scope Roo.BasicLayoutRegion
29219          */
29220         
29221         /**
29222          * @event beforeremove
29223          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29224          * @param {Roo.LayoutRegion} this
29225          * @param {Roo.ContentPanel} panel The panel
29226          * @param {Object} e The cancel event object
29227          */
29228         "beforeremove" : true,
29229         /**
29230          * @event invalidated
29231          * Fires when the layout for this region is changed.
29232          * @param {Roo.LayoutRegion} this
29233          */
29234         "invalidated" : true,
29235         /**
29236          * @event visibilitychange
29237          * Fires when this region is shown or hidden 
29238          * @param {Roo.LayoutRegion} this
29239          * @param {Boolean} visibility true or false
29240          */
29241         "visibilitychange" : true,
29242         /**
29243          * @event paneladded
29244          * Fires when a panel is added. 
29245          * @param {Roo.LayoutRegion} this
29246          * @param {Roo.ContentPanel} panel The panel
29247          */
29248         "paneladded" : true,
29249         /**
29250          * @event panelremoved
29251          * Fires when a panel is removed. 
29252          * @param {Roo.LayoutRegion} this
29253          * @param {Roo.ContentPanel} panel The panel
29254          */
29255         "panelremoved" : true,
29256         /**
29257          * @event beforecollapse
29258          * Fires when this region before collapse.
29259          * @param {Roo.LayoutRegion} this
29260          */
29261         "beforecollapse" : true,
29262         /**
29263          * @event collapsed
29264          * Fires when this region is collapsed.
29265          * @param {Roo.LayoutRegion} this
29266          */
29267         "collapsed" : true,
29268         /**
29269          * @event expanded
29270          * Fires when this region is expanded.
29271          * @param {Roo.LayoutRegion} this
29272          */
29273         "expanded" : true,
29274         /**
29275          * @event slideshow
29276          * Fires when this region is slid into view.
29277          * @param {Roo.LayoutRegion} this
29278          */
29279         "slideshow" : true,
29280         /**
29281          * @event slidehide
29282          * Fires when this region slides out of view. 
29283          * @param {Roo.LayoutRegion} this
29284          */
29285         "slidehide" : true,
29286         /**
29287          * @event panelactivated
29288          * Fires when a panel is activated. 
29289          * @param {Roo.LayoutRegion} this
29290          * @param {Roo.ContentPanel} panel The activated panel
29291          */
29292         "panelactivated" : true,
29293         /**
29294          * @event resized
29295          * Fires when the user resizes this region. 
29296          * @param {Roo.LayoutRegion} this
29297          * @param {Number} newSize The new size (width for east/west, height for north/south)
29298          */
29299         "resized" : true
29300     };
29301     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29302     this.panels = new Roo.util.MixedCollection();
29303     this.panels.getKey = this.getPanelId.createDelegate(this);
29304     this.box = null;
29305     this.activePanel = null;
29306     // ensure listeners are added...
29307     
29308     if (config.listeners || config.events) {
29309         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29310             listeners : config.listeners || {},
29311             events : config.events || {}
29312         });
29313     }
29314     
29315     if(skipConfig !== true){
29316         this.applyConfig(config);
29317     }
29318 };
29319
29320 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29321     getPanelId : function(p){
29322         return p.getId();
29323     },
29324     
29325     applyConfig : function(config){
29326         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29327         this.config = config;
29328         
29329     },
29330     
29331     /**
29332      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29333      * the width, for horizontal (north, south) the height.
29334      * @param {Number} newSize The new width or height
29335      */
29336     resizeTo : function(newSize){
29337         var el = this.el ? this.el :
29338                  (this.activePanel ? this.activePanel.getEl() : null);
29339         if(el){
29340             switch(this.position){
29341                 case "east":
29342                 case "west":
29343                     el.setWidth(newSize);
29344                     this.fireEvent("resized", this, newSize);
29345                 break;
29346                 case "north":
29347                 case "south":
29348                     el.setHeight(newSize);
29349                     this.fireEvent("resized", this, newSize);
29350                 break;                
29351             }
29352         }
29353     },
29354     
29355     getBox : function(){
29356         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29357     },
29358     
29359     getMargins : function(){
29360         return this.margins;
29361     },
29362     
29363     updateBox : function(box){
29364         this.box = box;
29365         var el = this.activePanel.getEl();
29366         el.dom.style.left = box.x + "px";
29367         el.dom.style.top = box.y + "px";
29368         this.activePanel.setSize(box.width, box.height);
29369     },
29370     
29371     /**
29372      * Returns the container element for this region.
29373      * @return {Roo.Element}
29374      */
29375     getEl : function(){
29376         return this.activePanel;
29377     },
29378     
29379     /**
29380      * Returns true if this region is currently visible.
29381      * @return {Boolean}
29382      */
29383     isVisible : function(){
29384         return this.activePanel ? true : false;
29385     },
29386     
29387     setActivePanel : function(panel){
29388         panel = this.getPanel(panel);
29389         if(this.activePanel && this.activePanel != panel){
29390             this.activePanel.setActiveState(false);
29391             this.activePanel.getEl().setLeftTop(-10000,-10000);
29392         }
29393         this.activePanel = panel;
29394         panel.setActiveState(true);
29395         if(this.box){
29396             panel.setSize(this.box.width, this.box.height);
29397         }
29398         this.fireEvent("panelactivated", this, panel);
29399         this.fireEvent("invalidated");
29400     },
29401     
29402     /**
29403      * Show the specified panel.
29404      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29405      * @return {Roo.ContentPanel} The shown panel or null
29406      */
29407     showPanel : function(panel){
29408         if(panel = this.getPanel(panel)){
29409             this.setActivePanel(panel);
29410         }
29411         return panel;
29412     },
29413     
29414     /**
29415      * Get the active panel for this region.
29416      * @return {Roo.ContentPanel} The active panel or null
29417      */
29418     getActivePanel : function(){
29419         return this.activePanel;
29420     },
29421     
29422     /**
29423      * Add the passed ContentPanel(s)
29424      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29425      * @return {Roo.ContentPanel} The panel added (if only one was added)
29426      */
29427     add : function(panel){
29428         if(arguments.length > 1){
29429             for(var i = 0, len = arguments.length; i < len; i++) {
29430                 this.add(arguments[i]);
29431             }
29432             return null;
29433         }
29434         if(this.hasPanel(panel)){
29435             this.showPanel(panel);
29436             return panel;
29437         }
29438         var el = panel.getEl();
29439         if(el.dom.parentNode != this.mgr.el.dom){
29440             this.mgr.el.dom.appendChild(el.dom);
29441         }
29442         if(panel.setRegion){
29443             panel.setRegion(this);
29444         }
29445         this.panels.add(panel);
29446         el.setStyle("position", "absolute");
29447         if(!panel.background){
29448             this.setActivePanel(panel);
29449             if(this.config.initialSize && this.panels.getCount()==1){
29450                 this.resizeTo(this.config.initialSize);
29451             }
29452         }
29453         this.fireEvent("paneladded", this, panel);
29454         return panel;
29455     },
29456     
29457     /**
29458      * Returns true if the panel is in this region.
29459      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29460      * @return {Boolean}
29461      */
29462     hasPanel : function(panel){
29463         if(typeof panel == "object"){ // must be panel obj
29464             panel = panel.getId();
29465         }
29466         return this.getPanel(panel) ? true : false;
29467     },
29468     
29469     /**
29470      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29471      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29472      * @param {Boolean} preservePanel Overrides the config preservePanel option
29473      * @return {Roo.ContentPanel} The panel that was removed
29474      */
29475     remove : function(panel, preservePanel){
29476         panel = this.getPanel(panel);
29477         if(!panel){
29478             return null;
29479         }
29480         var e = {};
29481         this.fireEvent("beforeremove", this, panel, e);
29482         if(e.cancel === true){
29483             return null;
29484         }
29485         var panelId = panel.getId();
29486         this.panels.removeKey(panelId);
29487         return panel;
29488     },
29489     
29490     /**
29491      * Returns the panel specified or null if it's not in this region.
29492      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29493      * @return {Roo.ContentPanel}
29494      */
29495     getPanel : function(id){
29496         if(typeof id == "object"){ // must be panel obj
29497             return id;
29498         }
29499         return this.panels.get(id);
29500     },
29501     
29502     /**
29503      * Returns this regions position (north/south/east/west/center).
29504      * @return {String} 
29505      */
29506     getPosition: function(){
29507         return this.position;    
29508     }
29509 });/*
29510  * Based on:
29511  * Ext JS Library 1.1.1
29512  * Copyright(c) 2006-2007, Ext JS, LLC.
29513  *
29514  * Originally Released Under LGPL - original licence link has changed is not relivant.
29515  *
29516  * Fork - LGPL
29517  * <script type="text/javascript">
29518  */
29519  
29520 /**
29521  * @class Roo.LayoutRegion
29522  * @extends Roo.BasicLayoutRegion
29523  * This class represents a region in a layout manager.
29524  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29525  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29526  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29527  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29528  * @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})
29529  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29530  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29531  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29532  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29533  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29534  * @cfg {String}    title           The title for the region (overrides panel titles)
29535  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29536  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29537  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29538  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29539  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29540  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29541  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29542  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29543  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29544  * @cfg {Boolean}   showPin         True to show a pin button
29545  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29546  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29547  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29548  * @cfg {Number}    width           For East/West panels
29549  * @cfg {Number}    height          For North/South panels
29550  * @cfg {Boolean}   split           To show the splitter
29551  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29552  */
29553 Roo.LayoutRegion = function(mgr, config, pos){
29554     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29555     var dh = Roo.DomHelper;
29556     /** This region's container element 
29557     * @type Roo.Element */
29558     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29559     /** This region's title element 
29560     * @type Roo.Element */
29561
29562     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29563         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29564         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29565     ]}, true);
29566     this.titleEl.enableDisplayMode();
29567     /** This region's title text element 
29568     * @type HTMLElement */
29569     this.titleTextEl = this.titleEl.dom.firstChild;
29570     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29571     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29572     this.closeBtn.enableDisplayMode();
29573     this.closeBtn.on("click", this.closeClicked, this);
29574     this.closeBtn.hide();
29575
29576     this.createBody(config);
29577     this.visible = true;
29578     this.collapsed = false;
29579
29580     if(config.hideWhenEmpty){
29581         this.hide();
29582         this.on("paneladded", this.validateVisibility, this);
29583         this.on("panelremoved", this.validateVisibility, this);
29584     }
29585     this.applyConfig(config);
29586 };
29587
29588 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29589
29590     createBody : function(){
29591         /** This region's body element 
29592         * @type Roo.Element */
29593         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29594     },
29595
29596     applyConfig : function(c){
29597         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29598             var dh = Roo.DomHelper;
29599             if(c.titlebar !== false){
29600                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29601                 this.collapseBtn.on("click", this.collapse, this);
29602                 this.collapseBtn.enableDisplayMode();
29603
29604                 if(c.showPin === true || this.showPin){
29605                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29606                     this.stickBtn.enableDisplayMode();
29607                     this.stickBtn.on("click", this.expand, this);
29608                     this.stickBtn.hide();
29609                 }
29610             }
29611             /** This region's collapsed element
29612             * @type Roo.Element */
29613             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29614                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29615             ]}, true);
29616             if(c.floatable !== false){
29617                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29618                this.collapsedEl.on("click", this.collapseClick, this);
29619             }
29620
29621             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29622                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29623                    id: "message", unselectable: "on", style:{"float":"left"}});
29624                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29625              }
29626             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29627             this.expandBtn.on("click", this.expand, this);
29628         }
29629         if(this.collapseBtn){
29630             this.collapseBtn.setVisible(c.collapsible == true);
29631         }
29632         this.cmargins = c.cmargins || this.cmargins ||
29633                          (this.position == "west" || this.position == "east" ?
29634                              {top: 0, left: 2, right:2, bottom: 0} :
29635                              {top: 2, left: 0, right:0, bottom: 2});
29636         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29637         this.bottomTabs = c.tabPosition != "top";
29638         this.autoScroll = c.autoScroll || false;
29639         if(this.autoScroll){
29640             this.bodyEl.setStyle("overflow", "auto");
29641         }else{
29642             this.bodyEl.setStyle("overflow", "hidden");
29643         }
29644         //if(c.titlebar !== false){
29645             if((!c.titlebar && !c.title) || c.titlebar === false){
29646                 this.titleEl.hide();
29647             }else{
29648                 this.titleEl.show();
29649                 if(c.title){
29650                     this.titleTextEl.innerHTML = c.title;
29651                 }
29652             }
29653         //}
29654         this.duration = c.duration || .30;
29655         this.slideDuration = c.slideDuration || .45;
29656         this.config = c;
29657         if(c.collapsed){
29658             this.collapse(true);
29659         }
29660         if(c.hidden){
29661             this.hide();
29662         }
29663     },
29664     /**
29665      * Returns true if this region is currently visible.
29666      * @return {Boolean}
29667      */
29668     isVisible : function(){
29669         return this.visible;
29670     },
29671
29672     /**
29673      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29674      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29675      */
29676     setCollapsedTitle : function(title){
29677         title = title || "&#160;";
29678         if(this.collapsedTitleTextEl){
29679             this.collapsedTitleTextEl.innerHTML = title;
29680         }
29681     },
29682
29683     getBox : function(){
29684         var b;
29685         if(!this.collapsed){
29686             b = this.el.getBox(false, true);
29687         }else{
29688             b = this.collapsedEl.getBox(false, true);
29689         }
29690         return b;
29691     },
29692
29693     getMargins : function(){
29694         return this.collapsed ? this.cmargins : this.margins;
29695     },
29696
29697     highlight : function(){
29698         this.el.addClass("x-layout-panel-dragover");
29699     },
29700
29701     unhighlight : function(){
29702         this.el.removeClass("x-layout-panel-dragover");
29703     },
29704
29705     updateBox : function(box){
29706         this.box = box;
29707         if(!this.collapsed){
29708             this.el.dom.style.left = box.x + "px";
29709             this.el.dom.style.top = box.y + "px";
29710             this.updateBody(box.width, box.height);
29711         }else{
29712             this.collapsedEl.dom.style.left = box.x + "px";
29713             this.collapsedEl.dom.style.top = box.y + "px";
29714             this.collapsedEl.setSize(box.width, box.height);
29715         }
29716         if(this.tabs){
29717             this.tabs.autoSizeTabs();
29718         }
29719     },
29720
29721     updateBody : function(w, h){
29722         if(w !== null){
29723             this.el.setWidth(w);
29724             w -= this.el.getBorderWidth("rl");
29725             if(this.config.adjustments){
29726                 w += this.config.adjustments[0];
29727             }
29728         }
29729         if(h !== null){
29730             this.el.setHeight(h);
29731             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29732             h -= this.el.getBorderWidth("tb");
29733             if(this.config.adjustments){
29734                 h += this.config.adjustments[1];
29735             }
29736             this.bodyEl.setHeight(h);
29737             if(this.tabs){
29738                 h = this.tabs.syncHeight(h);
29739             }
29740         }
29741         if(this.panelSize){
29742             w = w !== null ? w : this.panelSize.width;
29743             h = h !== null ? h : this.panelSize.height;
29744         }
29745         if(this.activePanel){
29746             var el = this.activePanel.getEl();
29747             w = w !== null ? w : el.getWidth();
29748             h = h !== null ? h : el.getHeight();
29749             this.panelSize = {width: w, height: h};
29750             this.activePanel.setSize(w, h);
29751         }
29752         if(Roo.isIE && this.tabs){
29753             this.tabs.el.repaint();
29754         }
29755     },
29756
29757     /**
29758      * Returns the container element for this region.
29759      * @return {Roo.Element}
29760      */
29761     getEl : function(){
29762         return this.el;
29763     },
29764
29765     /**
29766      * Hides this region.
29767      */
29768     hide : function(){
29769         if(!this.collapsed){
29770             this.el.dom.style.left = "-2000px";
29771             this.el.hide();
29772         }else{
29773             this.collapsedEl.dom.style.left = "-2000px";
29774             this.collapsedEl.hide();
29775         }
29776         this.visible = false;
29777         this.fireEvent("visibilitychange", this, false);
29778     },
29779
29780     /**
29781      * Shows this region if it was previously hidden.
29782      */
29783     show : function(){
29784         if(!this.collapsed){
29785             this.el.show();
29786         }else{
29787             this.collapsedEl.show();
29788         }
29789         this.visible = true;
29790         this.fireEvent("visibilitychange", this, true);
29791     },
29792
29793     closeClicked : function(){
29794         if(this.activePanel){
29795             this.remove(this.activePanel);
29796         }
29797     },
29798
29799     collapseClick : function(e){
29800         if(this.isSlid){
29801            e.stopPropagation();
29802            this.slideIn();
29803         }else{
29804            e.stopPropagation();
29805            this.slideOut();
29806         }
29807     },
29808
29809     /**
29810      * Collapses this region.
29811      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29812      */
29813     collapse : function(skipAnim, skipCheck = false){
29814         if(this.collapsed) {
29815             return;
29816         }
29817         
29818         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29819             
29820             this.collapsed = true;
29821             if(this.split){
29822                 this.split.el.hide();
29823             }
29824             if(this.config.animate && skipAnim !== true){
29825                 this.fireEvent("invalidated", this);
29826                 this.animateCollapse();
29827             }else{
29828                 this.el.setLocation(-20000,-20000);
29829                 this.el.hide();
29830                 this.collapsedEl.show();
29831                 this.fireEvent("collapsed", this);
29832                 this.fireEvent("invalidated", this);
29833             }
29834         }
29835         
29836     },
29837
29838     animateCollapse : function(){
29839         // overridden
29840     },
29841
29842     /**
29843      * Expands this region if it was previously collapsed.
29844      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29845      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29846      */
29847     expand : function(e, skipAnim){
29848         if(e) {
29849             e.stopPropagation();
29850         }
29851         if(!this.collapsed || this.el.hasActiveFx()) {
29852             return;
29853         }
29854         if(this.isSlid){
29855             this.afterSlideIn();
29856             skipAnim = true;
29857         }
29858         this.collapsed = false;
29859         if(this.config.animate && skipAnim !== true){
29860             this.animateExpand();
29861         }else{
29862             this.el.show();
29863             if(this.split){
29864                 this.split.el.show();
29865             }
29866             this.collapsedEl.setLocation(-2000,-2000);
29867             this.collapsedEl.hide();
29868             this.fireEvent("invalidated", this);
29869             this.fireEvent("expanded", this);
29870         }
29871     },
29872
29873     animateExpand : function(){
29874         // overridden
29875     },
29876
29877     initTabs : function()
29878     {
29879         this.bodyEl.setStyle("overflow", "hidden");
29880         var ts = new Roo.TabPanel(
29881                 this.bodyEl.dom,
29882                 {
29883                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29884                     disableTooltips: this.config.disableTabTips,
29885                     toolbar : this.config.toolbar
29886                 }
29887         );
29888         if(this.config.hideTabs){
29889             ts.stripWrap.setDisplayed(false);
29890         }
29891         this.tabs = ts;
29892         ts.resizeTabs = this.config.resizeTabs === true;
29893         ts.minTabWidth = this.config.minTabWidth || 40;
29894         ts.maxTabWidth = this.config.maxTabWidth || 250;
29895         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29896         ts.monitorResize = false;
29897         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29898         ts.bodyEl.addClass('x-layout-tabs-body');
29899         this.panels.each(this.initPanelAsTab, this);
29900     },
29901
29902     initPanelAsTab : function(panel){
29903         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29904                     this.config.closeOnTab && panel.isClosable());
29905         if(panel.tabTip !== undefined){
29906             ti.setTooltip(panel.tabTip);
29907         }
29908         ti.on("activate", function(){
29909               this.setActivePanel(panel);
29910         }, this);
29911         if(this.config.closeOnTab){
29912             ti.on("beforeclose", function(t, e){
29913                 e.cancel = true;
29914                 this.remove(panel);
29915             }, this);
29916         }
29917         return ti;
29918     },
29919
29920     updatePanelTitle : function(panel, title){
29921         if(this.activePanel == panel){
29922             this.updateTitle(title);
29923         }
29924         if(this.tabs){
29925             var ti = this.tabs.getTab(panel.getEl().id);
29926             ti.setText(title);
29927             if(panel.tabTip !== undefined){
29928                 ti.setTooltip(panel.tabTip);
29929             }
29930         }
29931     },
29932
29933     updateTitle : function(title){
29934         if(this.titleTextEl && !this.config.title){
29935             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29936         }
29937     },
29938
29939     setActivePanel : function(panel){
29940         panel = this.getPanel(panel);
29941         if(this.activePanel && this.activePanel != panel){
29942             this.activePanel.setActiveState(false);
29943         }
29944         this.activePanel = panel;
29945         panel.setActiveState(true);
29946         if(this.panelSize){
29947             panel.setSize(this.panelSize.width, this.panelSize.height);
29948         }
29949         if(this.closeBtn){
29950             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29951         }
29952         this.updateTitle(panel.getTitle());
29953         if(this.tabs){
29954             this.fireEvent("invalidated", this);
29955         }
29956         this.fireEvent("panelactivated", this, panel);
29957     },
29958
29959     /**
29960      * Shows the specified panel.
29961      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29962      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29963      */
29964     showPanel : function(panel)
29965     {
29966         panel = this.getPanel(panel);
29967         if(panel){
29968             if(this.tabs){
29969                 var tab = this.tabs.getTab(panel.getEl().id);
29970                 if(tab.isHidden()){
29971                     this.tabs.unhideTab(tab.id);
29972                 }
29973                 tab.activate();
29974             }else{
29975                 this.setActivePanel(panel);
29976             }
29977         }
29978         return panel;
29979     },
29980
29981     /**
29982      * Get the active panel for this region.
29983      * @return {Roo.ContentPanel} The active panel or null
29984      */
29985     getActivePanel : function(){
29986         return this.activePanel;
29987     },
29988
29989     validateVisibility : function(){
29990         if(this.panels.getCount() < 1){
29991             this.updateTitle("&#160;");
29992             this.closeBtn.hide();
29993             this.hide();
29994         }else{
29995             if(!this.isVisible()){
29996                 this.show();
29997             }
29998         }
29999     },
30000
30001     /**
30002      * Adds the passed ContentPanel(s) to this region.
30003      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30004      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30005      */
30006     add : function(panel){
30007         if(arguments.length > 1){
30008             for(var i = 0, len = arguments.length; i < len; i++) {
30009                 this.add(arguments[i]);
30010             }
30011             return null;
30012         }
30013         if(this.hasPanel(panel)){
30014             this.showPanel(panel);
30015             return panel;
30016         }
30017         panel.setRegion(this);
30018         this.panels.add(panel);
30019         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30020             this.bodyEl.dom.appendChild(panel.getEl().dom);
30021             if(panel.background !== true){
30022                 this.setActivePanel(panel);
30023             }
30024             this.fireEvent("paneladded", this, panel);
30025             return panel;
30026         }
30027         if(!this.tabs){
30028             this.initTabs();
30029         }else{
30030             this.initPanelAsTab(panel);
30031         }
30032         if(panel.background !== true){
30033             this.tabs.activate(panel.getEl().id);
30034         }
30035         this.fireEvent("paneladded", this, panel);
30036         return panel;
30037     },
30038
30039     /**
30040      * Hides the tab for the specified panel.
30041      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30042      */
30043     hidePanel : function(panel){
30044         if(this.tabs && (panel = this.getPanel(panel))){
30045             this.tabs.hideTab(panel.getEl().id);
30046         }
30047     },
30048
30049     /**
30050      * Unhides the tab for a previously hidden panel.
30051      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30052      */
30053     unhidePanel : function(panel){
30054         if(this.tabs && (panel = this.getPanel(panel))){
30055             this.tabs.unhideTab(panel.getEl().id);
30056         }
30057     },
30058
30059     clearPanels : function(){
30060         while(this.panels.getCount() > 0){
30061              this.remove(this.panels.first());
30062         }
30063     },
30064
30065     /**
30066      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30067      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30068      * @param {Boolean} preservePanel Overrides the config preservePanel option
30069      * @return {Roo.ContentPanel} The panel that was removed
30070      */
30071     remove : function(panel, preservePanel){
30072         panel = this.getPanel(panel);
30073         if(!panel){
30074             return null;
30075         }
30076         var e = {};
30077         this.fireEvent("beforeremove", this, panel, e);
30078         if(e.cancel === true){
30079             return null;
30080         }
30081         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30082         var panelId = panel.getId();
30083         this.panels.removeKey(panelId);
30084         if(preservePanel){
30085             document.body.appendChild(panel.getEl().dom);
30086         }
30087         if(this.tabs){
30088             this.tabs.removeTab(panel.getEl().id);
30089         }else if (!preservePanel){
30090             this.bodyEl.dom.removeChild(panel.getEl().dom);
30091         }
30092         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30093             var p = this.panels.first();
30094             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30095             tempEl.appendChild(p.getEl().dom);
30096             this.bodyEl.update("");
30097             this.bodyEl.dom.appendChild(p.getEl().dom);
30098             tempEl = null;
30099             this.updateTitle(p.getTitle());
30100             this.tabs = null;
30101             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30102             this.setActivePanel(p);
30103         }
30104         panel.setRegion(null);
30105         if(this.activePanel == panel){
30106             this.activePanel = null;
30107         }
30108         if(this.config.autoDestroy !== false && preservePanel !== true){
30109             try{panel.destroy();}catch(e){}
30110         }
30111         this.fireEvent("panelremoved", this, panel);
30112         return panel;
30113     },
30114
30115     /**
30116      * Returns the TabPanel component used by this region
30117      * @return {Roo.TabPanel}
30118      */
30119     getTabs : function(){
30120         return this.tabs;
30121     },
30122
30123     createTool : function(parentEl, className){
30124         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30125             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30126         btn.addClassOnOver("x-layout-tools-button-over");
30127         return btn;
30128     }
30129 });/*
30130  * Based on:
30131  * Ext JS Library 1.1.1
30132  * Copyright(c) 2006-2007, Ext JS, LLC.
30133  *
30134  * Originally Released Under LGPL - original licence link has changed is not relivant.
30135  *
30136  * Fork - LGPL
30137  * <script type="text/javascript">
30138  */
30139  
30140
30141
30142 /**
30143  * @class Roo.SplitLayoutRegion
30144  * @extends Roo.LayoutRegion
30145  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30146  */
30147 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30148     this.cursor = cursor;
30149     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30150 };
30151
30152 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30153     splitTip : "Drag to resize.",
30154     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30155     useSplitTips : false,
30156
30157     applyConfig : function(config){
30158         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30159         if(config.split){
30160             if(!this.split){
30161                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30162                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30163                 /** The SplitBar for this region 
30164                 * @type Roo.SplitBar */
30165                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30166                 this.split.on("moved", this.onSplitMove, this);
30167                 this.split.useShim = config.useShim === true;
30168                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30169                 if(this.useSplitTips){
30170                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30171                 }
30172                 if(config.collapsible){
30173                     this.split.el.on("dblclick", this.collapse,  this);
30174                 }
30175             }
30176             if(typeof config.minSize != "undefined"){
30177                 this.split.minSize = config.minSize;
30178             }
30179             if(typeof config.maxSize != "undefined"){
30180                 this.split.maxSize = config.maxSize;
30181             }
30182             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30183                 this.hideSplitter();
30184             }
30185         }
30186     },
30187
30188     getHMaxSize : function(){
30189          var cmax = this.config.maxSize || 10000;
30190          var center = this.mgr.getRegion("center");
30191          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30192     },
30193
30194     getVMaxSize : function(){
30195          var cmax = this.config.maxSize || 10000;
30196          var center = this.mgr.getRegion("center");
30197          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30198     },
30199
30200     onSplitMove : function(split, newSize){
30201         this.fireEvent("resized", this, newSize);
30202     },
30203     
30204     /** 
30205      * Returns the {@link Roo.SplitBar} for this region.
30206      * @return {Roo.SplitBar}
30207      */
30208     getSplitBar : function(){
30209         return this.split;
30210     },
30211     
30212     hide : function(){
30213         this.hideSplitter();
30214         Roo.SplitLayoutRegion.superclass.hide.call(this);
30215     },
30216
30217     hideSplitter : function(){
30218         if(this.split){
30219             this.split.el.setLocation(-2000,-2000);
30220             this.split.el.hide();
30221         }
30222     },
30223
30224     show : function(){
30225         if(this.split){
30226             this.split.el.show();
30227         }
30228         Roo.SplitLayoutRegion.superclass.show.call(this);
30229     },
30230     
30231     beforeSlide: function(){
30232         if(Roo.isGecko){// firefox overflow auto bug workaround
30233             this.bodyEl.clip();
30234             if(this.tabs) {
30235                 this.tabs.bodyEl.clip();
30236             }
30237             if(this.activePanel){
30238                 this.activePanel.getEl().clip();
30239                 
30240                 if(this.activePanel.beforeSlide){
30241                     this.activePanel.beforeSlide();
30242                 }
30243             }
30244         }
30245     },
30246     
30247     afterSlide : function(){
30248         if(Roo.isGecko){// firefox overflow auto bug workaround
30249             this.bodyEl.unclip();
30250             if(this.tabs) {
30251                 this.tabs.bodyEl.unclip();
30252             }
30253             if(this.activePanel){
30254                 this.activePanel.getEl().unclip();
30255                 if(this.activePanel.afterSlide){
30256                     this.activePanel.afterSlide();
30257                 }
30258             }
30259         }
30260     },
30261
30262     initAutoHide : function(){
30263         if(this.autoHide !== false){
30264             if(!this.autoHideHd){
30265                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30266                 this.autoHideHd = {
30267                     "mouseout": function(e){
30268                         if(!e.within(this.el, true)){
30269                             st.delay(500);
30270                         }
30271                     },
30272                     "mouseover" : function(e){
30273                         st.cancel();
30274                     },
30275                     scope : this
30276                 };
30277             }
30278             this.el.on(this.autoHideHd);
30279         }
30280     },
30281
30282     clearAutoHide : function(){
30283         if(this.autoHide !== false){
30284             this.el.un("mouseout", this.autoHideHd.mouseout);
30285             this.el.un("mouseover", this.autoHideHd.mouseover);
30286         }
30287     },
30288
30289     clearMonitor : function(){
30290         Roo.get(document).un("click", this.slideInIf, this);
30291     },
30292
30293     // these names are backwards but not changed for compat
30294     slideOut : function(){
30295         if(this.isSlid || this.el.hasActiveFx()){
30296             return;
30297         }
30298         this.isSlid = true;
30299         if(this.collapseBtn){
30300             this.collapseBtn.hide();
30301         }
30302         this.closeBtnState = this.closeBtn.getStyle('display');
30303         this.closeBtn.hide();
30304         if(this.stickBtn){
30305             this.stickBtn.show();
30306         }
30307         this.el.show();
30308         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30309         this.beforeSlide();
30310         this.el.setStyle("z-index", 10001);
30311         this.el.slideIn(this.getSlideAnchor(), {
30312             callback: function(){
30313                 this.afterSlide();
30314                 this.initAutoHide();
30315                 Roo.get(document).on("click", this.slideInIf, this);
30316                 this.fireEvent("slideshow", this);
30317             },
30318             scope: this,
30319             block: true
30320         });
30321     },
30322
30323     afterSlideIn : function(){
30324         this.clearAutoHide();
30325         this.isSlid = false;
30326         this.clearMonitor();
30327         this.el.setStyle("z-index", "");
30328         if(this.collapseBtn){
30329             this.collapseBtn.show();
30330         }
30331         this.closeBtn.setStyle('display', this.closeBtnState);
30332         if(this.stickBtn){
30333             this.stickBtn.hide();
30334         }
30335         this.fireEvent("slidehide", this);
30336     },
30337
30338     slideIn : function(cb){
30339         if(!this.isSlid || this.el.hasActiveFx()){
30340             Roo.callback(cb);
30341             return;
30342         }
30343         this.isSlid = false;
30344         this.beforeSlide();
30345         this.el.slideOut(this.getSlideAnchor(), {
30346             callback: function(){
30347                 this.el.setLeftTop(-10000, -10000);
30348                 this.afterSlide();
30349                 this.afterSlideIn();
30350                 Roo.callback(cb);
30351             },
30352             scope: this,
30353             block: true
30354         });
30355     },
30356     
30357     slideInIf : function(e){
30358         if(!e.within(this.el)){
30359             this.slideIn();
30360         }
30361     },
30362
30363     animateCollapse : function(){
30364         this.beforeSlide();
30365         this.el.setStyle("z-index", 20000);
30366         var anchor = this.getSlideAnchor();
30367         this.el.slideOut(anchor, {
30368             callback : function(){
30369                 this.el.setStyle("z-index", "");
30370                 this.collapsedEl.slideIn(anchor, {duration:.3});
30371                 this.afterSlide();
30372                 this.el.setLocation(-10000,-10000);
30373                 this.el.hide();
30374                 this.fireEvent("collapsed", this);
30375             },
30376             scope: this,
30377             block: true
30378         });
30379     },
30380
30381     animateExpand : function(){
30382         this.beforeSlide();
30383         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30384         this.el.setStyle("z-index", 20000);
30385         this.collapsedEl.hide({
30386             duration:.1
30387         });
30388         this.el.slideIn(this.getSlideAnchor(), {
30389             callback : function(){
30390                 this.el.setStyle("z-index", "");
30391                 this.afterSlide();
30392                 if(this.split){
30393                     this.split.el.show();
30394                 }
30395                 this.fireEvent("invalidated", this);
30396                 this.fireEvent("expanded", this);
30397             },
30398             scope: this,
30399             block: true
30400         });
30401     },
30402
30403     anchors : {
30404         "west" : "left",
30405         "east" : "right",
30406         "north" : "top",
30407         "south" : "bottom"
30408     },
30409
30410     sanchors : {
30411         "west" : "l",
30412         "east" : "r",
30413         "north" : "t",
30414         "south" : "b"
30415     },
30416
30417     canchors : {
30418         "west" : "tl-tr",
30419         "east" : "tr-tl",
30420         "north" : "tl-bl",
30421         "south" : "bl-tl"
30422     },
30423
30424     getAnchor : function(){
30425         return this.anchors[this.position];
30426     },
30427
30428     getCollapseAnchor : function(){
30429         return this.canchors[this.position];
30430     },
30431
30432     getSlideAnchor : function(){
30433         return this.sanchors[this.position];
30434     },
30435
30436     getAlignAdj : function(){
30437         var cm = this.cmargins;
30438         switch(this.position){
30439             case "west":
30440                 return [0, 0];
30441             break;
30442             case "east":
30443                 return [0, 0];
30444             break;
30445             case "north":
30446                 return [0, 0];
30447             break;
30448             case "south":
30449                 return [0, 0];
30450             break;
30451         }
30452     },
30453
30454     getExpandAdj : function(){
30455         var c = this.collapsedEl, cm = this.cmargins;
30456         switch(this.position){
30457             case "west":
30458                 return [-(cm.right+c.getWidth()+cm.left), 0];
30459             break;
30460             case "east":
30461                 return [cm.right+c.getWidth()+cm.left, 0];
30462             break;
30463             case "north":
30464                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30465             break;
30466             case "south":
30467                 return [0, cm.top+cm.bottom+c.getHeight()];
30468             break;
30469         }
30470     }
30471 });/*
30472  * Based on:
30473  * Ext JS Library 1.1.1
30474  * Copyright(c) 2006-2007, Ext JS, LLC.
30475  *
30476  * Originally Released Under LGPL - original licence link has changed is not relivant.
30477  *
30478  * Fork - LGPL
30479  * <script type="text/javascript">
30480  */
30481 /*
30482  * These classes are private internal classes
30483  */
30484 Roo.CenterLayoutRegion = function(mgr, config){
30485     Roo.LayoutRegion.call(this, mgr, config, "center");
30486     this.visible = true;
30487     this.minWidth = config.minWidth || 20;
30488     this.minHeight = config.minHeight || 20;
30489 };
30490
30491 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30492     hide : function(){
30493         // center panel can't be hidden
30494     },
30495     
30496     show : function(){
30497         // center panel can't be hidden
30498     },
30499     
30500     getMinWidth: function(){
30501         return this.minWidth;
30502     },
30503     
30504     getMinHeight: function(){
30505         return this.minHeight;
30506     }
30507 });
30508
30509
30510 Roo.NorthLayoutRegion = function(mgr, config){
30511     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30512     if(this.split){
30513         this.split.placement = Roo.SplitBar.TOP;
30514         this.split.orientation = Roo.SplitBar.VERTICAL;
30515         this.split.el.addClass("x-layout-split-v");
30516     }
30517     var size = config.initialSize || config.height;
30518     if(typeof size != "undefined"){
30519         this.el.setHeight(size);
30520     }
30521 };
30522 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30523     orientation: Roo.SplitBar.VERTICAL,
30524     getBox : function(){
30525         if(this.collapsed){
30526             return this.collapsedEl.getBox();
30527         }
30528         var box = this.el.getBox();
30529         if(this.split){
30530             box.height += this.split.el.getHeight();
30531         }
30532         return box;
30533     },
30534     
30535     updateBox : function(box){
30536         if(this.split && !this.collapsed){
30537             box.height -= this.split.el.getHeight();
30538             this.split.el.setLeft(box.x);
30539             this.split.el.setTop(box.y+box.height);
30540             this.split.el.setWidth(box.width);
30541         }
30542         if(this.collapsed){
30543             this.updateBody(box.width, null);
30544         }
30545         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30546     }
30547 });
30548
30549 Roo.SouthLayoutRegion = function(mgr, config){
30550     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30551     if(this.split){
30552         this.split.placement = Roo.SplitBar.BOTTOM;
30553         this.split.orientation = Roo.SplitBar.VERTICAL;
30554         this.split.el.addClass("x-layout-split-v");
30555     }
30556     var size = config.initialSize || config.height;
30557     if(typeof size != "undefined"){
30558         this.el.setHeight(size);
30559     }
30560 };
30561 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30562     orientation: Roo.SplitBar.VERTICAL,
30563     getBox : function(){
30564         if(this.collapsed){
30565             return this.collapsedEl.getBox();
30566         }
30567         var box = this.el.getBox();
30568         if(this.split){
30569             var sh = this.split.el.getHeight();
30570             box.height += sh;
30571             box.y -= sh;
30572         }
30573         return box;
30574     },
30575     
30576     updateBox : function(box){
30577         if(this.split && !this.collapsed){
30578             var sh = this.split.el.getHeight();
30579             box.height -= sh;
30580             box.y += sh;
30581             this.split.el.setLeft(box.x);
30582             this.split.el.setTop(box.y-sh);
30583             this.split.el.setWidth(box.width);
30584         }
30585         if(this.collapsed){
30586             this.updateBody(box.width, null);
30587         }
30588         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30589     }
30590 });
30591
30592 Roo.EastLayoutRegion = function(mgr, config){
30593     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30594     if(this.split){
30595         this.split.placement = Roo.SplitBar.RIGHT;
30596         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30597         this.split.el.addClass("x-layout-split-h");
30598     }
30599     var size = config.initialSize || config.width;
30600     if(typeof size != "undefined"){
30601         this.el.setWidth(size);
30602     }
30603 };
30604 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30605     orientation: Roo.SplitBar.HORIZONTAL,
30606     getBox : function(){
30607         if(this.collapsed){
30608             return this.collapsedEl.getBox();
30609         }
30610         var box = this.el.getBox();
30611         if(this.split){
30612             var sw = this.split.el.getWidth();
30613             box.width += sw;
30614             box.x -= sw;
30615         }
30616         return box;
30617     },
30618
30619     updateBox : function(box){
30620         if(this.split && !this.collapsed){
30621             var sw = this.split.el.getWidth();
30622             box.width -= sw;
30623             this.split.el.setLeft(box.x);
30624             this.split.el.setTop(box.y);
30625             this.split.el.setHeight(box.height);
30626             box.x += sw;
30627         }
30628         if(this.collapsed){
30629             this.updateBody(null, box.height);
30630         }
30631         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30632     }
30633 });
30634
30635 Roo.WestLayoutRegion = function(mgr, config){
30636     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30637     if(this.split){
30638         this.split.placement = Roo.SplitBar.LEFT;
30639         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30640         this.split.el.addClass("x-layout-split-h");
30641     }
30642     var size = config.initialSize || config.width;
30643     if(typeof size != "undefined"){
30644         this.el.setWidth(size);
30645     }
30646 };
30647 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30648     orientation: Roo.SplitBar.HORIZONTAL,
30649     getBox : function(){
30650         if(this.collapsed){
30651             return this.collapsedEl.getBox();
30652         }
30653         var box = this.el.getBox();
30654         if(this.split){
30655             box.width += this.split.el.getWidth();
30656         }
30657         return box;
30658     },
30659     
30660     updateBox : function(box){
30661         if(this.split && !this.collapsed){
30662             var sw = this.split.el.getWidth();
30663             box.width -= sw;
30664             this.split.el.setLeft(box.x+box.width);
30665             this.split.el.setTop(box.y);
30666             this.split.el.setHeight(box.height);
30667         }
30668         if(this.collapsed){
30669             this.updateBody(null, box.height);
30670         }
30671         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30672     }
30673 });
30674 /*
30675  * Based on:
30676  * Ext JS Library 1.1.1
30677  * Copyright(c) 2006-2007, Ext JS, LLC.
30678  *
30679  * Originally Released Under LGPL - original licence link has changed is not relivant.
30680  *
30681  * Fork - LGPL
30682  * <script type="text/javascript">
30683  */
30684  
30685  
30686 /*
30687  * Private internal class for reading and applying state
30688  */
30689 Roo.LayoutStateManager = function(layout){
30690      // default empty state
30691      this.state = {
30692         north: {},
30693         south: {},
30694         east: {},
30695         west: {}       
30696     };
30697 };
30698
30699 Roo.LayoutStateManager.prototype = {
30700     init : function(layout, provider){
30701         this.provider = provider;
30702         var state = provider.get(layout.id+"-layout-state");
30703         if(state){
30704             var wasUpdating = layout.isUpdating();
30705             if(!wasUpdating){
30706                 layout.beginUpdate();
30707             }
30708             for(var key in state){
30709                 if(typeof state[key] != "function"){
30710                     var rstate = state[key];
30711                     var r = layout.getRegion(key);
30712                     if(r && rstate){
30713                         if(rstate.size){
30714                             r.resizeTo(rstate.size);
30715                         }
30716                         if(rstate.collapsed == true){
30717                             r.collapse(true);
30718                         }else{
30719                             r.expand(null, true);
30720                         }
30721                     }
30722                 }
30723             }
30724             if(!wasUpdating){
30725                 layout.endUpdate();
30726             }
30727             this.state = state; 
30728         }
30729         this.layout = layout;
30730         layout.on("regionresized", this.onRegionResized, this);
30731         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30732         layout.on("regionexpanded", this.onRegionExpanded, this);
30733     },
30734     
30735     storeState : function(){
30736         this.provider.set(this.layout.id+"-layout-state", this.state);
30737     },
30738     
30739     onRegionResized : function(region, newSize){
30740         this.state[region.getPosition()].size = newSize;
30741         this.storeState();
30742     },
30743     
30744     onRegionCollapsed : function(region){
30745         this.state[region.getPosition()].collapsed = true;
30746         this.storeState();
30747     },
30748     
30749     onRegionExpanded : function(region){
30750         this.state[region.getPosition()].collapsed = false;
30751         this.storeState();
30752     }
30753 };/*
30754  * Based on:
30755  * Ext JS Library 1.1.1
30756  * Copyright(c) 2006-2007, Ext JS, LLC.
30757  *
30758  * Originally Released Under LGPL - original licence link has changed is not relivant.
30759  *
30760  * Fork - LGPL
30761  * <script type="text/javascript">
30762  */
30763 /**
30764  * @class Roo.ContentPanel
30765  * @extends Roo.util.Observable
30766  * A basic ContentPanel element.
30767  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30768  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30769  * @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
30770  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30771  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30772  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30773  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30774  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30775  * @cfg {String} title          The title for this panel
30776  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30777  * @cfg {String} url            Calls {@link #setUrl} with this value
30778  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30779  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30780  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30781  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30782
30783  * @constructor
30784  * Create a new ContentPanel.
30785  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30786  * @param {String/Object} config A string to set only the title or a config object
30787  * @param {String} content (optional) Set the HTML content for this panel
30788  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30789  */
30790 Roo.ContentPanel = function(el, config, content){
30791     
30792      
30793     /*
30794     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30795         config = el;
30796         el = Roo.id();
30797     }
30798     if (config && config.parentLayout) { 
30799         el = config.parentLayout.el.createChild(); 
30800     }
30801     */
30802     if(el.autoCreate){ // xtype is available if this is called from factory
30803         config = el;
30804         el = Roo.id();
30805     }
30806     this.el = Roo.get(el);
30807     if(!this.el && config && config.autoCreate){
30808         if(typeof config.autoCreate == "object"){
30809             if(!config.autoCreate.id){
30810                 config.autoCreate.id = config.id||el;
30811             }
30812             this.el = Roo.DomHelper.append(document.body,
30813                         config.autoCreate, true);
30814         }else{
30815             this.el = Roo.DomHelper.append(document.body,
30816                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30817         }
30818     }
30819     this.closable = false;
30820     this.loaded = false;
30821     this.active = false;
30822     if(typeof config == "string"){
30823         this.title = config;
30824     }else{
30825         Roo.apply(this, config);
30826     }
30827     
30828     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30829         this.wrapEl = this.el.wrap();
30830         this.toolbar.container = this.el.insertSibling(false, 'before');
30831         this.toolbar = new Roo.Toolbar(this.toolbar);
30832     }
30833     
30834     // xtype created footer. - not sure if will work as we normally have to render first..
30835     if (this.footer && !this.footer.el && this.footer.xtype) {
30836         if (!this.wrapEl) {
30837             this.wrapEl = this.el.wrap();
30838         }
30839     
30840         this.footer.container = this.wrapEl.createChild();
30841          
30842         this.footer = Roo.factory(this.footer, Roo);
30843         
30844     }
30845     
30846     if(this.resizeEl){
30847         this.resizeEl = Roo.get(this.resizeEl, true);
30848     }else{
30849         this.resizeEl = this.el;
30850     }
30851     // handle view.xtype
30852     
30853  
30854     
30855     
30856     this.addEvents({
30857         /**
30858          * @event activate
30859          * Fires when this panel is activated. 
30860          * @param {Roo.ContentPanel} this
30861          */
30862         "activate" : true,
30863         /**
30864          * @event deactivate
30865          * Fires when this panel is activated. 
30866          * @param {Roo.ContentPanel} this
30867          */
30868         "deactivate" : true,
30869
30870         /**
30871          * @event resize
30872          * Fires when this panel is resized if fitToFrame is true.
30873          * @param {Roo.ContentPanel} this
30874          * @param {Number} width The width after any component adjustments
30875          * @param {Number} height The height after any component adjustments
30876          */
30877         "resize" : true,
30878         
30879          /**
30880          * @event render
30881          * Fires when this tab is created
30882          * @param {Roo.ContentPanel} this
30883          */
30884         "render" : true
30885         
30886         
30887         
30888     });
30889     
30890
30891     
30892     
30893     if(this.autoScroll){
30894         this.resizeEl.setStyle("overflow", "auto");
30895     } else {
30896         // fix randome scrolling
30897         this.el.on('scroll', function() {
30898             Roo.log('fix random scolling');
30899             this.scrollTo('top',0); 
30900         });
30901     }
30902     content = content || this.content;
30903     if(content){
30904         this.setContent(content);
30905     }
30906     if(config && config.url){
30907         this.setUrl(this.url, this.params, this.loadOnce);
30908     }
30909     
30910     
30911     
30912     Roo.ContentPanel.superclass.constructor.call(this);
30913     
30914     if (this.view && typeof(this.view.xtype) != 'undefined') {
30915         this.view.el = this.el.appendChild(document.createElement("div"));
30916         this.view = Roo.factory(this.view); 
30917         this.view.render  &&  this.view.render(false, '');  
30918     }
30919     
30920     
30921     this.fireEvent('render', this);
30922 };
30923
30924 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30925     tabTip:'',
30926     setRegion : function(region){
30927         this.region = region;
30928         if(region){
30929            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30930         }else{
30931            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30932         } 
30933     },
30934     
30935     /**
30936      * Returns the toolbar for this Panel if one was configured. 
30937      * @return {Roo.Toolbar} 
30938      */
30939     getToolbar : function(){
30940         return this.toolbar;
30941     },
30942     
30943     setActiveState : function(active){
30944         this.active = active;
30945         if(!active){
30946             this.fireEvent("deactivate", this);
30947         }else{
30948             this.fireEvent("activate", this);
30949         }
30950     },
30951     /**
30952      * Updates this panel's element
30953      * @param {String} content The new content
30954      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30955     */
30956     setContent : function(content, loadScripts){
30957         this.el.update(content, loadScripts);
30958     },
30959
30960     ignoreResize : function(w, h){
30961         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30962             return true;
30963         }else{
30964             this.lastSize = {width: w, height: h};
30965             return false;
30966         }
30967     },
30968     /**
30969      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30970      * @return {Roo.UpdateManager} The UpdateManager
30971      */
30972     getUpdateManager : function(){
30973         return this.el.getUpdateManager();
30974     },
30975      /**
30976      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30977      * @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:
30978 <pre><code>
30979 panel.load({
30980     url: "your-url.php",
30981     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30982     callback: yourFunction,
30983     scope: yourObject, //(optional scope)
30984     discardUrl: false,
30985     nocache: false,
30986     text: "Loading...",
30987     timeout: 30,
30988     scripts: false
30989 });
30990 </code></pre>
30991      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30992      * 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.
30993      * @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}
30994      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30995      * @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.
30996      * @return {Roo.ContentPanel} this
30997      */
30998     load : function(){
30999         var um = this.el.getUpdateManager();
31000         um.update.apply(um, arguments);
31001         return this;
31002     },
31003
31004
31005     /**
31006      * 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.
31007      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31008      * @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)
31009      * @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)
31010      * @return {Roo.UpdateManager} The UpdateManager
31011      */
31012     setUrl : function(url, params, loadOnce){
31013         if(this.refreshDelegate){
31014             this.removeListener("activate", this.refreshDelegate);
31015         }
31016         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31017         this.on("activate", this.refreshDelegate);
31018         return this.el.getUpdateManager();
31019     },
31020     
31021     _handleRefresh : function(url, params, loadOnce){
31022         if(!loadOnce || !this.loaded){
31023             var updater = this.el.getUpdateManager();
31024             updater.update(url, params, this._setLoaded.createDelegate(this));
31025         }
31026     },
31027     
31028     _setLoaded : function(){
31029         this.loaded = true;
31030     }, 
31031     
31032     /**
31033      * Returns this panel's id
31034      * @return {String} 
31035      */
31036     getId : function(){
31037         return this.el.id;
31038     },
31039     
31040     /** 
31041      * Returns this panel's element - used by regiosn to add.
31042      * @return {Roo.Element} 
31043      */
31044     getEl : function(){
31045         return this.wrapEl || this.el;
31046     },
31047     
31048     adjustForComponents : function(width, height)
31049     {
31050         //Roo.log('adjustForComponents ');
31051         if(this.resizeEl != this.el){
31052             width -= this.el.getFrameWidth('lr');
31053             height -= this.el.getFrameWidth('tb');
31054         }
31055         if(this.toolbar){
31056             var te = this.toolbar.getEl();
31057             height -= te.getHeight();
31058             te.setWidth(width);
31059         }
31060         if(this.footer){
31061             var te = this.footer.getEl();
31062             Roo.log("footer:" + te.getHeight());
31063             
31064             height -= te.getHeight();
31065             te.setWidth(width);
31066         }
31067         
31068         
31069         if(this.adjustments){
31070             width += this.adjustments[0];
31071             height += this.adjustments[1];
31072         }
31073         return {"width": width, "height": height};
31074     },
31075     
31076     setSize : function(width, height){
31077         if(this.fitToFrame && !this.ignoreResize(width, height)){
31078             if(this.fitContainer && this.resizeEl != this.el){
31079                 this.el.setSize(width, height);
31080             }
31081             var size = this.adjustForComponents(width, height);
31082             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31083             this.fireEvent('resize', this, size.width, size.height);
31084         }
31085     },
31086     
31087     /**
31088      * Returns this panel's title
31089      * @return {String} 
31090      */
31091     getTitle : function(){
31092         return this.title;
31093     },
31094     
31095     /**
31096      * Set this panel's title
31097      * @param {String} title
31098      */
31099     setTitle : function(title){
31100         this.title = title;
31101         if(this.region){
31102             this.region.updatePanelTitle(this, title);
31103         }
31104     },
31105     
31106     /**
31107      * Returns true is this panel was configured to be closable
31108      * @return {Boolean} 
31109      */
31110     isClosable : function(){
31111         return this.closable;
31112     },
31113     
31114     beforeSlide : function(){
31115         this.el.clip();
31116         this.resizeEl.clip();
31117     },
31118     
31119     afterSlide : function(){
31120         this.el.unclip();
31121         this.resizeEl.unclip();
31122     },
31123     
31124     /**
31125      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31126      *   Will fail silently if the {@link #setUrl} method has not been called.
31127      *   This does not activate the panel, just updates its content.
31128      */
31129     refresh : function(){
31130         if(this.refreshDelegate){
31131            this.loaded = false;
31132            this.refreshDelegate();
31133         }
31134     },
31135     
31136     /**
31137      * Destroys this panel
31138      */
31139     destroy : function(){
31140         this.el.removeAllListeners();
31141         var tempEl = document.createElement("span");
31142         tempEl.appendChild(this.el.dom);
31143         tempEl.innerHTML = "";
31144         this.el.remove();
31145         this.el = null;
31146     },
31147     
31148     /**
31149      * form - if the content panel contains a form - this is a reference to it.
31150      * @type {Roo.form.Form}
31151      */
31152     form : false,
31153     /**
31154      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31155      *    This contains a reference to it.
31156      * @type {Roo.View}
31157      */
31158     view : false,
31159     
31160       /**
31161      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31162      * <pre><code>
31163
31164 layout.addxtype({
31165        xtype : 'Form',
31166        items: [ .... ]
31167    }
31168 );
31169
31170 </code></pre>
31171      * @param {Object} cfg Xtype definition of item to add.
31172      */
31173     
31174     addxtype : function(cfg) {
31175         // add form..
31176         if (cfg.xtype.match(/^Form$/)) {
31177             
31178             var el;
31179             //if (this.footer) {
31180             //    el = this.footer.container.insertSibling(false, 'before');
31181             //} else {
31182                 el = this.el.createChild();
31183             //}
31184
31185             this.form = new  Roo.form.Form(cfg);
31186             
31187             
31188             if ( this.form.allItems.length) {
31189                 this.form.render(el.dom);
31190             }
31191             return this.form;
31192         }
31193         // should only have one of theses..
31194         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31195             // views.. should not be just added - used named prop 'view''
31196             
31197             cfg.el = this.el.appendChild(document.createElement("div"));
31198             // factory?
31199             
31200             var ret = new Roo.factory(cfg);
31201              
31202              ret.render && ret.render(false, ''); // render blank..
31203             this.view = ret;
31204             return ret;
31205         }
31206         return false;
31207     }
31208 });
31209
31210 /**
31211  * @class Roo.GridPanel
31212  * @extends Roo.ContentPanel
31213  * @constructor
31214  * Create a new GridPanel.
31215  * @param {Roo.grid.Grid} grid The grid for this panel
31216  * @param {String/Object} config A string to set only the panel's title, or a config object
31217  */
31218 Roo.GridPanel = function(grid, config){
31219     
31220   
31221     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31222         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31223         
31224     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31225     
31226     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31227     
31228     if(this.toolbar){
31229         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31230     }
31231     // xtype created footer. - not sure if will work as we normally have to render first..
31232     if (this.footer && !this.footer.el && this.footer.xtype) {
31233         
31234         this.footer.container = this.grid.getView().getFooterPanel(true);
31235         this.footer.dataSource = this.grid.dataSource;
31236         this.footer = Roo.factory(this.footer, Roo);
31237         
31238     }
31239     
31240     grid.monitorWindowResize = false; // turn off autosizing
31241     grid.autoHeight = false;
31242     grid.autoWidth = false;
31243     this.grid = grid;
31244     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31245 };
31246
31247 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31248     getId : function(){
31249         return this.grid.id;
31250     },
31251     
31252     /**
31253      * Returns the grid for this panel
31254      * @return {Roo.grid.Grid} 
31255      */
31256     getGrid : function(){
31257         return this.grid;    
31258     },
31259     
31260     setSize : function(width, height){
31261         if(!this.ignoreResize(width, height)){
31262             var grid = this.grid;
31263             var size = this.adjustForComponents(width, height);
31264             grid.getGridEl().setSize(size.width, size.height);
31265             grid.autoSize();
31266         }
31267     },
31268     
31269     beforeSlide : function(){
31270         this.grid.getView().scroller.clip();
31271     },
31272     
31273     afterSlide : function(){
31274         this.grid.getView().scroller.unclip();
31275     },
31276     
31277     destroy : function(){
31278         this.grid.destroy();
31279         delete this.grid;
31280         Roo.GridPanel.superclass.destroy.call(this); 
31281     }
31282 });
31283
31284
31285 /**
31286  * @class Roo.NestedLayoutPanel
31287  * @extends Roo.ContentPanel
31288  * @constructor
31289  * Create a new NestedLayoutPanel.
31290  * 
31291  * 
31292  * @param {Roo.BorderLayout} layout The layout for this panel
31293  * @param {String/Object} config A string to set only the title or a config object
31294  */
31295 Roo.NestedLayoutPanel = function(layout, config)
31296 {
31297     // construct with only one argument..
31298     /* FIXME - implement nicer consturctors
31299     if (layout.layout) {
31300         config = layout;
31301         layout = config.layout;
31302         delete config.layout;
31303     }
31304     if (layout.xtype && !layout.getEl) {
31305         // then layout needs constructing..
31306         layout = Roo.factory(layout, Roo);
31307     }
31308     */
31309     
31310     
31311     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31312     
31313     layout.monitorWindowResize = false; // turn off autosizing
31314     this.layout = layout;
31315     this.layout.getEl().addClass("x-layout-nested-layout");
31316     
31317     
31318     
31319     
31320 };
31321
31322 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31323
31324     setSize : function(width, height){
31325         if(!this.ignoreResize(width, height)){
31326             var size = this.adjustForComponents(width, height);
31327             var el = this.layout.getEl();
31328             el.setSize(size.width, size.height);
31329             var touch = el.dom.offsetWidth;
31330             this.layout.layout();
31331             // ie requires a double layout on the first pass
31332             if(Roo.isIE && !this.initialized){
31333                 this.initialized = true;
31334                 this.layout.layout();
31335             }
31336         }
31337     },
31338     
31339     // activate all subpanels if not currently active..
31340     
31341     setActiveState : function(active){
31342         this.active = active;
31343         if(!active){
31344             this.fireEvent("deactivate", this);
31345             return;
31346         }
31347         
31348         this.fireEvent("activate", this);
31349         // not sure if this should happen before or after..
31350         if (!this.layout) {
31351             return; // should not happen..
31352         }
31353         var reg = false;
31354         for (var r in this.layout.regions) {
31355             reg = this.layout.getRegion(r);
31356             if (reg.getActivePanel()) {
31357                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31358                 reg.setActivePanel(reg.getActivePanel());
31359                 continue;
31360             }
31361             if (!reg.panels.length) {
31362                 continue;
31363             }
31364             reg.showPanel(reg.getPanel(0));
31365         }
31366         
31367         
31368         
31369         
31370     },
31371     
31372     /**
31373      * Returns the nested BorderLayout for this panel
31374      * @return {Roo.BorderLayout} 
31375      */
31376     getLayout : function(){
31377         return this.layout;
31378     },
31379     
31380      /**
31381      * Adds a xtype elements to the layout of the nested panel
31382      * <pre><code>
31383
31384 panel.addxtype({
31385        xtype : 'ContentPanel',
31386        region: 'west',
31387        items: [ .... ]
31388    }
31389 );
31390
31391 panel.addxtype({
31392         xtype : 'NestedLayoutPanel',
31393         region: 'west',
31394         layout: {
31395            center: { },
31396            west: { }   
31397         },
31398         items : [ ... list of content panels or nested layout panels.. ]
31399    }
31400 );
31401 </code></pre>
31402      * @param {Object} cfg Xtype definition of item to add.
31403      */
31404     addxtype : function(cfg) {
31405         return this.layout.addxtype(cfg);
31406     
31407     }
31408 });
31409
31410 Roo.ScrollPanel = function(el, config, content){
31411     config = config || {};
31412     config.fitToFrame = true;
31413     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31414     
31415     this.el.dom.style.overflow = "hidden";
31416     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31417     this.el.removeClass("x-layout-inactive-content");
31418     this.el.on("mousewheel", this.onWheel, this);
31419
31420     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31421     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31422     up.unselectable(); down.unselectable();
31423     up.on("click", this.scrollUp, this);
31424     down.on("click", this.scrollDown, this);
31425     up.addClassOnOver("x-scroller-btn-over");
31426     down.addClassOnOver("x-scroller-btn-over");
31427     up.addClassOnClick("x-scroller-btn-click");
31428     down.addClassOnClick("x-scroller-btn-click");
31429     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31430
31431     this.resizeEl = this.el;
31432     this.el = wrap; this.up = up; this.down = down;
31433 };
31434
31435 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31436     increment : 100,
31437     wheelIncrement : 5,
31438     scrollUp : function(){
31439         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31440     },
31441
31442     scrollDown : function(){
31443         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31444     },
31445
31446     afterScroll : function(){
31447         var el = this.resizeEl;
31448         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31449         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31450         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31451     },
31452
31453     setSize : function(){
31454         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31455         this.afterScroll();
31456     },
31457
31458     onWheel : function(e){
31459         var d = e.getWheelDelta();
31460         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31461         this.afterScroll();
31462         e.stopEvent();
31463     },
31464
31465     setContent : function(content, loadScripts){
31466         this.resizeEl.update(content, loadScripts);
31467     }
31468
31469 });
31470
31471
31472
31473
31474
31475
31476
31477
31478
31479 /**
31480  * @class Roo.TreePanel
31481  * @extends Roo.ContentPanel
31482  * @constructor
31483  * Create a new TreePanel. - defaults to fit/scoll contents.
31484  * @param {String/Object} config A string to set only the panel's title, or a config object
31485  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31486  */
31487 Roo.TreePanel = function(config){
31488     var el = config.el;
31489     var tree = config.tree;
31490     delete config.tree; 
31491     delete config.el; // hopefull!
31492     
31493     // wrapper for IE7 strict & safari scroll issue
31494     
31495     var treeEl = el.createChild();
31496     config.resizeEl = treeEl;
31497     
31498     
31499     
31500     Roo.TreePanel.superclass.constructor.call(this, el, config);
31501  
31502  
31503     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31504     //console.log(tree);
31505     this.on('activate', function()
31506     {
31507         if (this.tree.rendered) {
31508             return;
31509         }
31510         //console.log('render tree');
31511         this.tree.render();
31512     });
31513     // this should not be needed.. - it's actually the 'el' that resizes?
31514     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31515     
31516     //this.on('resize',  function (cp, w, h) {
31517     //        this.tree.innerCt.setWidth(w);
31518     //        this.tree.innerCt.setHeight(h);
31519     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31520     //});
31521
31522         
31523     
31524 };
31525
31526 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31527     fitToFrame : true,
31528     autoScroll : true
31529 });
31530
31531
31532
31533
31534
31535
31536
31537
31538
31539
31540
31541 /*
31542  * Based on:
31543  * Ext JS Library 1.1.1
31544  * Copyright(c) 2006-2007, Ext JS, LLC.
31545  *
31546  * Originally Released Under LGPL - original licence link has changed is not relivant.
31547  *
31548  * Fork - LGPL
31549  * <script type="text/javascript">
31550  */
31551  
31552
31553 /**
31554  * @class Roo.ReaderLayout
31555  * @extends Roo.BorderLayout
31556  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31557  * center region containing two nested regions (a top one for a list view and one for item preview below),
31558  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31559  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31560  * expedites the setup of the overall layout and regions for this common application style.
31561  * Example:
31562  <pre><code>
31563 var reader = new Roo.ReaderLayout();
31564 var CP = Roo.ContentPanel;  // shortcut for adding
31565
31566 reader.beginUpdate();
31567 reader.add("north", new CP("north", "North"));
31568 reader.add("west", new CP("west", {title: "West"}));
31569 reader.add("east", new CP("east", {title: "East"}));
31570
31571 reader.regions.listView.add(new CP("listView", "List"));
31572 reader.regions.preview.add(new CP("preview", "Preview"));
31573 reader.endUpdate();
31574 </code></pre>
31575 * @constructor
31576 * Create a new ReaderLayout
31577 * @param {Object} config Configuration options
31578 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31579 * document.body if omitted)
31580 */
31581 Roo.ReaderLayout = function(config, renderTo){
31582     var c = config || {size:{}};
31583     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31584         north: c.north !== false ? Roo.apply({
31585             split:false,
31586             initialSize: 32,
31587             titlebar: false
31588         }, c.north) : false,
31589         west: c.west !== false ? Roo.apply({
31590             split:true,
31591             initialSize: 200,
31592             minSize: 175,
31593             maxSize: 400,
31594             titlebar: true,
31595             collapsible: true,
31596             animate: true,
31597             margins:{left:5,right:0,bottom:5,top:5},
31598             cmargins:{left:5,right:5,bottom:5,top:5}
31599         }, c.west) : false,
31600         east: c.east !== false ? Roo.apply({
31601             split:true,
31602             initialSize: 200,
31603             minSize: 175,
31604             maxSize: 400,
31605             titlebar: true,
31606             collapsible: true,
31607             animate: true,
31608             margins:{left:0,right:5,bottom:5,top:5},
31609             cmargins:{left:5,right:5,bottom:5,top:5}
31610         }, c.east) : false,
31611         center: Roo.apply({
31612             tabPosition: 'top',
31613             autoScroll:false,
31614             closeOnTab: true,
31615             titlebar:false,
31616             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31617         }, c.center)
31618     });
31619
31620     this.el.addClass('x-reader');
31621
31622     this.beginUpdate();
31623
31624     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31625         south: c.preview !== false ? Roo.apply({
31626             split:true,
31627             initialSize: 200,
31628             minSize: 100,
31629             autoScroll:true,
31630             collapsible:true,
31631             titlebar: true,
31632             cmargins:{top:5,left:0, right:0, bottom:0}
31633         }, c.preview) : false,
31634         center: Roo.apply({
31635             autoScroll:false,
31636             titlebar:false,
31637             minHeight:200
31638         }, c.listView)
31639     });
31640     this.add('center', new Roo.NestedLayoutPanel(inner,
31641             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31642
31643     this.endUpdate();
31644
31645     this.regions.preview = inner.getRegion('south');
31646     this.regions.listView = inner.getRegion('center');
31647 };
31648
31649 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31650  * Based on:
31651  * Ext JS Library 1.1.1
31652  * Copyright(c) 2006-2007, Ext JS, LLC.
31653  *
31654  * Originally Released Under LGPL - original licence link has changed is not relivant.
31655  *
31656  * Fork - LGPL
31657  * <script type="text/javascript">
31658  */
31659  
31660 /**
31661  * @class Roo.grid.Grid
31662  * @extends Roo.util.Observable
31663  * This class represents the primary interface of a component based grid control.
31664  * <br><br>Usage:<pre><code>
31665  var grid = new Roo.grid.Grid("my-container-id", {
31666      ds: myDataStore,
31667      cm: myColModel,
31668      selModel: mySelectionModel,
31669      autoSizeColumns: true,
31670      monitorWindowResize: false,
31671      trackMouseOver: true
31672  });
31673  // set any options
31674  grid.render();
31675  * </code></pre>
31676  * <b>Common Problems:</b><br/>
31677  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31678  * element will correct this<br/>
31679  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31680  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31681  * are unpredictable.<br/>
31682  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31683  * grid to calculate dimensions/offsets.<br/>
31684   * @constructor
31685  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31686  * The container MUST have some type of size defined for the grid to fill. The container will be
31687  * automatically set to position relative if it isn't already.
31688  * @param {Object} config A config object that sets properties on this grid.
31689  */
31690 Roo.grid.Grid = function(container, config){
31691         // initialize the container
31692         this.container = Roo.get(container);
31693         this.container.update("");
31694         this.container.setStyle("overflow", "hidden");
31695     this.container.addClass('x-grid-container');
31696
31697     this.id = this.container.id;
31698
31699     Roo.apply(this, config);
31700     // check and correct shorthanded configs
31701     if(this.ds){
31702         this.dataSource = this.ds;
31703         delete this.ds;
31704     }
31705     if(this.cm){
31706         this.colModel = this.cm;
31707         delete this.cm;
31708     }
31709     if(this.sm){
31710         this.selModel = this.sm;
31711         delete this.sm;
31712     }
31713
31714     if (this.selModel) {
31715         this.selModel = Roo.factory(this.selModel, Roo.grid);
31716         this.sm = this.selModel;
31717         this.sm.xmodule = this.xmodule || false;
31718     }
31719     if (typeof(this.colModel.config) == 'undefined') {
31720         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31721         this.cm = this.colModel;
31722         this.cm.xmodule = this.xmodule || false;
31723     }
31724     if (this.dataSource) {
31725         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31726         this.ds = this.dataSource;
31727         this.ds.xmodule = this.xmodule || false;
31728          
31729     }
31730     
31731     
31732     
31733     if(this.width){
31734         this.container.setWidth(this.width);
31735     }
31736
31737     if(this.height){
31738         this.container.setHeight(this.height);
31739     }
31740     /** @private */
31741         this.addEvents({
31742         // raw events
31743         /**
31744          * @event click
31745          * The raw click event for the entire grid.
31746          * @param {Roo.EventObject} e
31747          */
31748         "click" : true,
31749         /**
31750          * @event dblclick
31751          * The raw dblclick event for the entire grid.
31752          * @param {Roo.EventObject} e
31753          */
31754         "dblclick" : true,
31755         /**
31756          * @event contextmenu
31757          * The raw contextmenu event for the entire grid.
31758          * @param {Roo.EventObject} e
31759          */
31760         "contextmenu" : true,
31761         /**
31762          * @event mousedown
31763          * The raw mousedown event for the entire grid.
31764          * @param {Roo.EventObject} e
31765          */
31766         "mousedown" : true,
31767         /**
31768          * @event mouseup
31769          * The raw mouseup event for the entire grid.
31770          * @param {Roo.EventObject} e
31771          */
31772         "mouseup" : true,
31773         /**
31774          * @event mouseover
31775          * The raw mouseover event for the entire grid.
31776          * @param {Roo.EventObject} e
31777          */
31778         "mouseover" : true,
31779         /**
31780          * @event mouseout
31781          * The raw mouseout event for the entire grid.
31782          * @param {Roo.EventObject} e
31783          */
31784         "mouseout" : true,
31785         /**
31786          * @event keypress
31787          * The raw keypress event for the entire grid.
31788          * @param {Roo.EventObject} e
31789          */
31790         "keypress" : true,
31791         /**
31792          * @event keydown
31793          * The raw keydown event for the entire grid.
31794          * @param {Roo.EventObject} e
31795          */
31796         "keydown" : true,
31797
31798         // custom events
31799
31800         /**
31801          * @event cellclick
31802          * Fires when a cell is clicked
31803          * @param {Grid} this
31804          * @param {Number} rowIndex
31805          * @param {Number} columnIndex
31806          * @param {Roo.EventObject} e
31807          */
31808         "cellclick" : true,
31809         /**
31810          * @event celldblclick
31811          * Fires when a cell is double clicked
31812          * @param {Grid} this
31813          * @param {Number} rowIndex
31814          * @param {Number} columnIndex
31815          * @param {Roo.EventObject} e
31816          */
31817         "celldblclick" : true,
31818         /**
31819          * @event rowclick
31820          * Fires when a row is clicked
31821          * @param {Grid} this
31822          * @param {Number} rowIndex
31823          * @param {Roo.EventObject} e
31824          */
31825         "rowclick" : true,
31826         /**
31827          * @event rowdblclick
31828          * Fires when a row is double clicked
31829          * @param {Grid} this
31830          * @param {Number} rowIndex
31831          * @param {Roo.EventObject} e
31832          */
31833         "rowdblclick" : true,
31834         /**
31835          * @event headerclick
31836          * Fires when a header is clicked
31837          * @param {Grid} this
31838          * @param {Number} columnIndex
31839          * @param {Roo.EventObject} e
31840          */
31841         "headerclick" : true,
31842         /**
31843          * @event headerdblclick
31844          * Fires when a header cell is double clicked
31845          * @param {Grid} this
31846          * @param {Number} columnIndex
31847          * @param {Roo.EventObject} e
31848          */
31849         "headerdblclick" : true,
31850         /**
31851          * @event rowcontextmenu
31852          * Fires when a row is right clicked
31853          * @param {Grid} this
31854          * @param {Number} rowIndex
31855          * @param {Roo.EventObject} e
31856          */
31857         "rowcontextmenu" : true,
31858         /**
31859          * @event cellcontextmenu
31860          * Fires when a cell is right clicked
31861          * @param {Grid} this
31862          * @param {Number} rowIndex
31863          * @param {Number} cellIndex
31864          * @param {Roo.EventObject} e
31865          */
31866          "cellcontextmenu" : true,
31867         /**
31868          * @event headercontextmenu
31869          * Fires when a header is right clicked
31870          * @param {Grid} this
31871          * @param {Number} columnIndex
31872          * @param {Roo.EventObject} e
31873          */
31874         "headercontextmenu" : true,
31875         /**
31876          * @event bodyscroll
31877          * Fires when the body element is scrolled
31878          * @param {Number} scrollLeft
31879          * @param {Number} scrollTop
31880          */
31881         "bodyscroll" : true,
31882         /**
31883          * @event columnresize
31884          * Fires when the user resizes a column
31885          * @param {Number} columnIndex
31886          * @param {Number} newSize
31887          */
31888         "columnresize" : true,
31889         /**
31890          * @event columnmove
31891          * Fires when the user moves a column
31892          * @param {Number} oldIndex
31893          * @param {Number} newIndex
31894          */
31895         "columnmove" : true,
31896         /**
31897          * @event startdrag
31898          * Fires when row(s) start being dragged
31899          * @param {Grid} this
31900          * @param {Roo.GridDD} dd The drag drop object
31901          * @param {event} e The raw browser event
31902          */
31903         "startdrag" : true,
31904         /**
31905          * @event enddrag
31906          * Fires when a drag operation is complete
31907          * @param {Grid} this
31908          * @param {Roo.GridDD} dd The drag drop object
31909          * @param {event} e The raw browser event
31910          */
31911         "enddrag" : true,
31912         /**
31913          * @event dragdrop
31914          * Fires when dragged row(s) are dropped on a valid DD target
31915          * @param {Grid} this
31916          * @param {Roo.GridDD} dd The drag drop object
31917          * @param {String} targetId The target drag drop object
31918          * @param {event} e The raw browser event
31919          */
31920         "dragdrop" : true,
31921         /**
31922          * @event dragover
31923          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31924          * @param {Grid} this
31925          * @param {Roo.GridDD} dd The drag drop object
31926          * @param {String} targetId The target drag drop object
31927          * @param {event} e The raw browser event
31928          */
31929         "dragover" : true,
31930         /**
31931          * @event dragenter
31932          *  Fires when the dragged row(s) first cross another DD target while being dragged
31933          * @param {Grid} this
31934          * @param {Roo.GridDD} dd The drag drop object
31935          * @param {String} targetId The target drag drop object
31936          * @param {event} e The raw browser event
31937          */
31938         "dragenter" : true,
31939         /**
31940          * @event dragout
31941          * Fires when the dragged row(s) leave another DD target while being dragged
31942          * @param {Grid} this
31943          * @param {Roo.GridDD} dd The drag drop object
31944          * @param {String} targetId The target drag drop object
31945          * @param {event} e The raw browser event
31946          */
31947         "dragout" : true,
31948         /**
31949          * @event rowclass
31950          * Fires when a row is rendered, so you can change add a style to it.
31951          * @param {GridView} gridview   The grid view
31952          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31953          */
31954         'rowclass' : true,
31955
31956         /**
31957          * @event render
31958          * Fires when the grid is rendered
31959          * @param {Grid} grid
31960          */
31961         'render' : true
31962     });
31963
31964     Roo.grid.Grid.superclass.constructor.call(this);
31965 };
31966 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31967     
31968     /**
31969      * @cfg {String} ddGroup - drag drop group.
31970      */
31971
31972     /**
31973      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31974      */
31975     minColumnWidth : 25,
31976
31977     /**
31978      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31979      * <b>on initial render.</b> It is more efficient to explicitly size the columns
31980      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31981      */
31982     autoSizeColumns : false,
31983
31984     /**
31985      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31986      */
31987     autoSizeHeaders : true,
31988
31989     /**
31990      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31991      */
31992     monitorWindowResize : true,
31993
31994     /**
31995      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31996      * rows measured to get a columns size. Default is 0 (all rows).
31997      */
31998     maxRowsToMeasure : 0,
31999
32000     /**
32001      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32002      */
32003     trackMouseOver : true,
32004
32005     /**
32006     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32007     */
32008     
32009     /**
32010     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32011     */
32012     enableDragDrop : false,
32013     
32014     /**
32015     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32016     */
32017     enableColumnMove : true,
32018     
32019     /**
32020     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32021     */
32022     enableColumnHide : true,
32023     
32024     /**
32025     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32026     */
32027     enableRowHeightSync : false,
32028     
32029     /**
32030     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32031     */
32032     stripeRows : true,
32033     
32034     /**
32035     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32036     */
32037     autoHeight : false,
32038
32039     /**
32040      * @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.
32041      */
32042     autoExpandColumn : false,
32043
32044     /**
32045     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32046     * Default is 50.
32047     */
32048     autoExpandMin : 50,
32049
32050     /**
32051     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32052     */
32053     autoExpandMax : 1000,
32054
32055     /**
32056     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32057     */
32058     view : null,
32059
32060     /**
32061     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32062     */
32063     loadMask : false,
32064     /**
32065     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32066     */
32067     dropTarget: false,
32068     
32069    
32070     
32071     // private
32072     rendered : false,
32073
32074     /**
32075     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32076     * of a fixed width. Default is false.
32077     */
32078     /**
32079     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32080     */
32081     /**
32082      * Called once after all setup has been completed and the grid is ready to be rendered.
32083      * @return {Roo.grid.Grid} this
32084      */
32085     render : function()
32086     {
32087         var c = this.container;
32088         // try to detect autoHeight/width mode
32089         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32090             this.autoHeight = true;
32091         }
32092         var view = this.getView();
32093         view.init(this);
32094
32095         c.on("click", this.onClick, this);
32096         c.on("dblclick", this.onDblClick, this);
32097         c.on("contextmenu", this.onContextMenu, this);
32098         c.on("keydown", this.onKeyDown, this);
32099         if (Roo.isTouch) {
32100             c.on("touchstart", this.onTouchStart, this);
32101         }
32102
32103         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32104
32105         this.getSelectionModel().init(this);
32106
32107         view.render();
32108
32109         if(this.loadMask){
32110             this.loadMask = new Roo.LoadMask(this.container,
32111                     Roo.apply({store:this.dataSource}, this.loadMask));
32112         }
32113         
32114         
32115         if (this.toolbar && this.toolbar.xtype) {
32116             this.toolbar.container = this.getView().getHeaderPanel(true);
32117             this.toolbar = new Roo.Toolbar(this.toolbar);
32118         }
32119         if (this.footer && this.footer.xtype) {
32120             this.footer.dataSource = this.getDataSource();
32121             this.footer.container = this.getView().getFooterPanel(true);
32122             this.footer = Roo.factory(this.footer, Roo);
32123         }
32124         if (this.dropTarget && this.dropTarget.xtype) {
32125             delete this.dropTarget.xtype;
32126             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32127         }
32128         
32129         
32130         this.rendered = true;
32131         this.fireEvent('render', this);
32132         return this;
32133     },
32134
32135         /**
32136          * Reconfigures the grid to use a different Store and Column Model.
32137          * The View will be bound to the new objects and refreshed.
32138          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32139          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32140          */
32141     reconfigure : function(dataSource, colModel){
32142         if(this.loadMask){
32143             this.loadMask.destroy();
32144             this.loadMask = new Roo.LoadMask(this.container,
32145                     Roo.apply({store:dataSource}, this.loadMask));
32146         }
32147         this.view.bind(dataSource, colModel);
32148         this.dataSource = dataSource;
32149         this.colModel = colModel;
32150         this.view.refresh(true);
32151     },
32152
32153     // private
32154     onKeyDown : function(e){
32155         this.fireEvent("keydown", e);
32156     },
32157
32158     /**
32159      * Destroy this grid.
32160      * @param {Boolean} removeEl True to remove the element
32161      */
32162     destroy : function(removeEl, keepListeners){
32163         if(this.loadMask){
32164             this.loadMask.destroy();
32165         }
32166         var c = this.container;
32167         c.removeAllListeners();
32168         this.view.destroy();
32169         this.colModel.purgeListeners();
32170         if(!keepListeners){
32171             this.purgeListeners();
32172         }
32173         c.update("");
32174         if(removeEl === true){
32175             c.remove();
32176         }
32177     },
32178
32179     // private
32180     processEvent : function(name, e){
32181         // does this fire select???
32182         //Roo.log('grid:processEvent '  + name);
32183         
32184         if (name != 'touchstart' ) {
32185             this.fireEvent(name, e);    
32186         }
32187         
32188         var t = e.getTarget();
32189         var v = this.view;
32190         var header = v.findHeaderIndex(t);
32191         if(header !== false){
32192             var ename = name == 'touchstart' ? 'click' : name;
32193              
32194             this.fireEvent("header" + ename, this, header, e);
32195         }else{
32196             var row = v.findRowIndex(t);
32197             var cell = v.findCellIndex(t);
32198             if (name == 'touchstart') {
32199                 // first touch is always a click.
32200                 // hopefull this happens after selection is updated.?
32201                 name = false;
32202                 
32203                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32204                     var cs = this.selModel.getSelectedCell();
32205                     if (row == cs[0] && cell == cs[1]){
32206                         name = 'dblclick';
32207                     }
32208                 }
32209                 if (typeof(this.selModel.getSelections) != 'undefined') {
32210                     var cs = this.selModel.getSelections();
32211                     var ds = this.dataSource;
32212                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32213                         name = 'dblclick';
32214                     }
32215                 }
32216                 if (!name) {
32217                     return;
32218                 }
32219             }
32220             
32221             
32222             if(row !== false){
32223                 this.fireEvent("row" + name, this, row, e);
32224                 if(cell !== false){
32225                     this.fireEvent("cell" + name, this, row, cell, e);
32226                 }
32227             }
32228         }
32229     },
32230
32231     // private
32232     onClick : function(e){
32233         this.processEvent("click", e);
32234     },
32235    // private
32236     onTouchStart : function(e){
32237         this.processEvent("touchstart", e);
32238     },
32239
32240     // private
32241     onContextMenu : function(e, t){
32242         this.processEvent("contextmenu", e);
32243     },
32244
32245     // private
32246     onDblClick : function(e){
32247         this.processEvent("dblclick", e);
32248     },
32249
32250     // private
32251     walkCells : function(row, col, step, fn, scope){
32252         var cm = this.colModel, clen = cm.getColumnCount();
32253         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32254         if(step < 0){
32255             if(col < 0){
32256                 row--;
32257                 first = false;
32258             }
32259             while(row >= 0){
32260                 if(!first){
32261                     col = clen-1;
32262                 }
32263                 first = false;
32264                 while(col >= 0){
32265                     if(fn.call(scope || this, row, col, cm) === true){
32266                         return [row, col];
32267                     }
32268                     col--;
32269                 }
32270                 row--;
32271             }
32272         } else {
32273             if(col >= clen){
32274                 row++;
32275                 first = false;
32276             }
32277             while(row < rlen){
32278                 if(!first){
32279                     col = 0;
32280                 }
32281                 first = false;
32282                 while(col < clen){
32283                     if(fn.call(scope || this, row, col, cm) === true){
32284                         return [row, col];
32285                     }
32286                     col++;
32287                 }
32288                 row++;
32289             }
32290         }
32291         return null;
32292     },
32293
32294     // private
32295     getSelections : function(){
32296         return this.selModel.getSelections();
32297     },
32298
32299     /**
32300      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32301      * but if manual update is required this method will initiate it.
32302      */
32303     autoSize : function(){
32304         if(this.rendered){
32305             this.view.layout();
32306             if(this.view.adjustForScroll){
32307                 this.view.adjustForScroll();
32308             }
32309         }
32310     },
32311
32312     /**
32313      * Returns the grid's underlying element.
32314      * @return {Element} The element
32315      */
32316     getGridEl : function(){
32317         return this.container;
32318     },
32319
32320     // private for compatibility, overridden by editor grid
32321     stopEditing : function(){},
32322
32323     /**
32324      * Returns the grid's SelectionModel.
32325      * @return {SelectionModel}
32326      */
32327     getSelectionModel : function(){
32328         if(!this.selModel){
32329             this.selModel = new Roo.grid.RowSelectionModel();
32330         }
32331         return this.selModel;
32332     },
32333
32334     /**
32335      * Returns the grid's DataSource.
32336      * @return {DataSource}
32337      */
32338     getDataSource : function(){
32339         return this.dataSource;
32340     },
32341
32342     /**
32343      * Returns the grid's ColumnModel.
32344      * @return {ColumnModel}
32345      */
32346     getColumnModel : function(){
32347         return this.colModel;
32348     },
32349
32350     /**
32351      * Returns the grid's GridView object.
32352      * @return {GridView}
32353      */
32354     getView : function(){
32355         if(!this.view){
32356             this.view = new Roo.grid.GridView(this.viewConfig);
32357         }
32358         return this.view;
32359     },
32360     /**
32361      * Called to get grid's drag proxy text, by default returns this.ddText.
32362      * @return {String}
32363      */
32364     getDragDropText : function(){
32365         var count = this.selModel.getCount();
32366         return String.format(this.ddText, count, count == 1 ? '' : 's');
32367     }
32368 });
32369 /**
32370  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32371  * %0 is replaced with the number of selected rows.
32372  * @type String
32373  */
32374 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32375  * Based on:
32376  * Ext JS Library 1.1.1
32377  * Copyright(c) 2006-2007, Ext JS, LLC.
32378  *
32379  * Originally Released Under LGPL - original licence link has changed is not relivant.
32380  *
32381  * Fork - LGPL
32382  * <script type="text/javascript">
32383  */
32384  
32385 Roo.grid.AbstractGridView = function(){
32386         this.grid = null;
32387         
32388         this.events = {
32389             "beforerowremoved" : true,
32390             "beforerowsinserted" : true,
32391             "beforerefresh" : true,
32392             "rowremoved" : true,
32393             "rowsinserted" : true,
32394             "rowupdated" : true,
32395             "refresh" : true
32396         };
32397     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32398 };
32399
32400 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32401     rowClass : "x-grid-row",
32402     cellClass : "x-grid-cell",
32403     tdClass : "x-grid-td",
32404     hdClass : "x-grid-hd",
32405     splitClass : "x-grid-hd-split",
32406     
32407     init: function(grid){
32408         this.grid = grid;
32409                 var cid = this.grid.getGridEl().id;
32410         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32411         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32412         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32413         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32414         },
32415         
32416     getColumnRenderers : function(){
32417         var renderers = [];
32418         var cm = this.grid.colModel;
32419         var colCount = cm.getColumnCount();
32420         for(var i = 0; i < colCount; i++){
32421             renderers[i] = cm.getRenderer(i);
32422         }
32423         return renderers;
32424     },
32425     
32426     getColumnIds : function(){
32427         var ids = [];
32428         var cm = this.grid.colModel;
32429         var colCount = cm.getColumnCount();
32430         for(var i = 0; i < colCount; i++){
32431             ids[i] = cm.getColumnId(i);
32432         }
32433         return ids;
32434     },
32435     
32436     getDataIndexes : function(){
32437         if(!this.indexMap){
32438             this.indexMap = this.buildIndexMap();
32439         }
32440         return this.indexMap.colToData;
32441     },
32442     
32443     getColumnIndexByDataIndex : function(dataIndex){
32444         if(!this.indexMap){
32445             this.indexMap = this.buildIndexMap();
32446         }
32447         return this.indexMap.dataToCol[dataIndex];
32448     },
32449     
32450     /**
32451      * Set a css style for a column dynamically. 
32452      * @param {Number} colIndex The index of the column
32453      * @param {String} name The css property name
32454      * @param {String} value The css value
32455      */
32456     setCSSStyle : function(colIndex, name, value){
32457         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32458         Roo.util.CSS.updateRule(selector, name, value);
32459     },
32460     
32461     generateRules : function(cm){
32462         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32463         Roo.util.CSS.removeStyleSheet(rulesId);
32464         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32465             var cid = cm.getColumnId(i);
32466             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32467                          this.tdSelector, cid, " {\n}\n",
32468                          this.hdSelector, cid, " {\n}\n",
32469                          this.splitSelector, cid, " {\n}\n");
32470         }
32471         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32472     }
32473 });/*
32474  * Based on:
32475  * Ext JS Library 1.1.1
32476  * Copyright(c) 2006-2007, Ext JS, LLC.
32477  *
32478  * Originally Released Under LGPL - original licence link has changed is not relivant.
32479  *
32480  * Fork - LGPL
32481  * <script type="text/javascript">
32482  */
32483
32484 // private
32485 // This is a support class used internally by the Grid components
32486 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32487     this.grid = grid;
32488     this.view = grid.getView();
32489     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32490     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32491     if(hd2){
32492         this.setHandleElId(Roo.id(hd));
32493         this.setOuterHandleElId(Roo.id(hd2));
32494     }
32495     this.scroll = false;
32496 };
32497 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32498     maxDragWidth: 120,
32499     getDragData : function(e){
32500         var t = Roo.lib.Event.getTarget(e);
32501         var h = this.view.findHeaderCell(t);
32502         if(h){
32503             return {ddel: h.firstChild, header:h};
32504         }
32505         return false;
32506     },
32507
32508     onInitDrag : function(e){
32509         this.view.headersDisabled = true;
32510         var clone = this.dragData.ddel.cloneNode(true);
32511         clone.id = Roo.id();
32512         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32513         this.proxy.update(clone);
32514         return true;
32515     },
32516
32517     afterValidDrop : function(){
32518         var v = this.view;
32519         setTimeout(function(){
32520             v.headersDisabled = false;
32521         }, 50);
32522     },
32523
32524     afterInvalidDrop : function(){
32525         var v = this.view;
32526         setTimeout(function(){
32527             v.headersDisabled = false;
32528         }, 50);
32529     }
32530 });
32531 /*
32532  * Based on:
32533  * Ext JS Library 1.1.1
32534  * Copyright(c) 2006-2007, Ext JS, LLC.
32535  *
32536  * Originally Released Under LGPL - original licence link has changed is not relivant.
32537  *
32538  * Fork - LGPL
32539  * <script type="text/javascript">
32540  */
32541 // private
32542 // This is a support class used internally by the Grid components
32543 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32544     this.grid = grid;
32545     this.view = grid.getView();
32546     // split the proxies so they don't interfere with mouse events
32547     this.proxyTop = Roo.DomHelper.append(document.body, {
32548         cls:"col-move-top", html:"&#160;"
32549     }, true);
32550     this.proxyBottom = Roo.DomHelper.append(document.body, {
32551         cls:"col-move-bottom", html:"&#160;"
32552     }, true);
32553     this.proxyTop.hide = this.proxyBottom.hide = function(){
32554         this.setLeftTop(-100,-100);
32555         this.setStyle("visibility", "hidden");
32556     };
32557     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32558     // temporarily disabled
32559     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32560     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32561 };
32562 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32563     proxyOffsets : [-4, -9],
32564     fly: Roo.Element.fly,
32565
32566     getTargetFromEvent : function(e){
32567         var t = Roo.lib.Event.getTarget(e);
32568         var cindex = this.view.findCellIndex(t);
32569         if(cindex !== false){
32570             return this.view.getHeaderCell(cindex);
32571         }
32572         return null;
32573     },
32574
32575     nextVisible : function(h){
32576         var v = this.view, cm = this.grid.colModel;
32577         h = h.nextSibling;
32578         while(h){
32579             if(!cm.isHidden(v.getCellIndex(h))){
32580                 return h;
32581             }
32582             h = h.nextSibling;
32583         }
32584         return null;
32585     },
32586
32587     prevVisible : function(h){
32588         var v = this.view, cm = this.grid.colModel;
32589         h = h.prevSibling;
32590         while(h){
32591             if(!cm.isHidden(v.getCellIndex(h))){
32592                 return h;
32593             }
32594             h = h.prevSibling;
32595         }
32596         return null;
32597     },
32598
32599     positionIndicator : function(h, n, e){
32600         var x = Roo.lib.Event.getPageX(e);
32601         var r = Roo.lib.Dom.getRegion(n.firstChild);
32602         var px, pt, py = r.top + this.proxyOffsets[1];
32603         if((r.right - x) <= (r.right-r.left)/2){
32604             px = r.right+this.view.borderWidth;
32605             pt = "after";
32606         }else{
32607             px = r.left;
32608             pt = "before";
32609         }
32610         var oldIndex = this.view.getCellIndex(h);
32611         var newIndex = this.view.getCellIndex(n);
32612
32613         if(this.grid.colModel.isFixed(newIndex)){
32614             return false;
32615         }
32616
32617         var locked = this.grid.colModel.isLocked(newIndex);
32618
32619         if(pt == "after"){
32620             newIndex++;
32621         }
32622         if(oldIndex < newIndex){
32623             newIndex--;
32624         }
32625         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32626             return false;
32627         }
32628         px +=  this.proxyOffsets[0];
32629         this.proxyTop.setLeftTop(px, py);
32630         this.proxyTop.show();
32631         if(!this.bottomOffset){
32632             this.bottomOffset = this.view.mainHd.getHeight();
32633         }
32634         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32635         this.proxyBottom.show();
32636         return pt;
32637     },
32638
32639     onNodeEnter : function(n, dd, e, data){
32640         if(data.header != n){
32641             this.positionIndicator(data.header, n, e);
32642         }
32643     },
32644
32645     onNodeOver : function(n, dd, e, data){
32646         var result = false;
32647         if(data.header != n){
32648             result = this.positionIndicator(data.header, n, e);
32649         }
32650         if(!result){
32651             this.proxyTop.hide();
32652             this.proxyBottom.hide();
32653         }
32654         return result ? this.dropAllowed : this.dropNotAllowed;
32655     },
32656
32657     onNodeOut : function(n, dd, e, data){
32658         this.proxyTop.hide();
32659         this.proxyBottom.hide();
32660     },
32661
32662     onNodeDrop : function(n, dd, e, data){
32663         var h = data.header;
32664         if(h != n){
32665             var cm = this.grid.colModel;
32666             var x = Roo.lib.Event.getPageX(e);
32667             var r = Roo.lib.Dom.getRegion(n.firstChild);
32668             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32669             var oldIndex = this.view.getCellIndex(h);
32670             var newIndex = this.view.getCellIndex(n);
32671             var locked = cm.isLocked(newIndex);
32672             if(pt == "after"){
32673                 newIndex++;
32674             }
32675             if(oldIndex < newIndex){
32676                 newIndex--;
32677             }
32678             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32679                 return false;
32680             }
32681             cm.setLocked(oldIndex, locked, true);
32682             cm.moveColumn(oldIndex, newIndex);
32683             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32684             return true;
32685         }
32686         return false;
32687     }
32688 });
32689 /*
32690  * Based on:
32691  * Ext JS Library 1.1.1
32692  * Copyright(c) 2006-2007, Ext JS, LLC.
32693  *
32694  * Originally Released Under LGPL - original licence link has changed is not relivant.
32695  *
32696  * Fork - LGPL
32697  * <script type="text/javascript">
32698  */
32699   
32700 /**
32701  * @class Roo.grid.GridView
32702  * @extends Roo.util.Observable
32703  *
32704  * @constructor
32705  * @param {Object} config
32706  */
32707 Roo.grid.GridView = function(config){
32708     Roo.grid.GridView.superclass.constructor.call(this);
32709     this.el = null;
32710
32711     Roo.apply(this, config);
32712 };
32713
32714 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32715
32716     unselectable :  'unselectable="on"',
32717     unselectableCls :  'x-unselectable',
32718     
32719     
32720     rowClass : "x-grid-row",
32721
32722     cellClass : "x-grid-col",
32723
32724     tdClass : "x-grid-td",
32725
32726     hdClass : "x-grid-hd",
32727
32728     splitClass : "x-grid-split",
32729
32730     sortClasses : ["sort-asc", "sort-desc"],
32731
32732     enableMoveAnim : false,
32733
32734     hlColor: "C3DAF9",
32735
32736     dh : Roo.DomHelper,
32737
32738     fly : Roo.Element.fly,
32739
32740     css : Roo.util.CSS,
32741
32742     borderWidth: 1,
32743
32744     splitOffset: 3,
32745
32746     scrollIncrement : 22,
32747
32748     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32749
32750     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32751
32752     bind : function(ds, cm){
32753         if(this.ds){
32754             this.ds.un("load", this.onLoad, this);
32755             this.ds.un("datachanged", this.onDataChange, this);
32756             this.ds.un("add", this.onAdd, this);
32757             this.ds.un("remove", this.onRemove, this);
32758             this.ds.un("update", this.onUpdate, this);
32759             this.ds.un("clear", this.onClear, this);
32760         }
32761         if(ds){
32762             ds.on("load", this.onLoad, this);
32763             ds.on("datachanged", this.onDataChange, this);
32764             ds.on("add", this.onAdd, this);
32765             ds.on("remove", this.onRemove, this);
32766             ds.on("update", this.onUpdate, this);
32767             ds.on("clear", this.onClear, this);
32768         }
32769         this.ds = ds;
32770
32771         if(this.cm){
32772             this.cm.un("widthchange", this.onColWidthChange, this);
32773             this.cm.un("headerchange", this.onHeaderChange, this);
32774             this.cm.un("hiddenchange", this.onHiddenChange, this);
32775             this.cm.un("columnmoved", this.onColumnMove, this);
32776             this.cm.un("columnlockchange", this.onColumnLock, this);
32777         }
32778         if(cm){
32779             this.generateRules(cm);
32780             cm.on("widthchange", this.onColWidthChange, this);
32781             cm.on("headerchange", this.onHeaderChange, this);
32782             cm.on("hiddenchange", this.onHiddenChange, this);
32783             cm.on("columnmoved", this.onColumnMove, this);
32784             cm.on("columnlockchange", this.onColumnLock, this);
32785         }
32786         this.cm = cm;
32787     },
32788
32789     init: function(grid){
32790         Roo.grid.GridView.superclass.init.call(this, grid);
32791
32792         this.bind(grid.dataSource, grid.colModel);
32793
32794         grid.on("headerclick", this.handleHeaderClick, this);
32795
32796         if(grid.trackMouseOver){
32797             grid.on("mouseover", this.onRowOver, this);
32798             grid.on("mouseout", this.onRowOut, this);
32799         }
32800         grid.cancelTextSelection = function(){};
32801         this.gridId = grid.id;
32802
32803         var tpls = this.templates || {};
32804
32805         if(!tpls.master){
32806             tpls.master = new Roo.Template(
32807                '<div class="x-grid" hidefocus="true">',
32808                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32809                   '<div class="x-grid-topbar"></div>',
32810                   '<div class="x-grid-scroller"><div></div></div>',
32811                   '<div class="x-grid-locked">',
32812                       '<div class="x-grid-header">{lockedHeader}</div>',
32813                       '<div class="x-grid-body">{lockedBody}</div>',
32814                   "</div>",
32815                   '<div class="x-grid-viewport">',
32816                       '<div class="x-grid-header">{header}</div>',
32817                       '<div class="x-grid-body">{body}</div>',
32818                   "</div>",
32819                   '<div class="x-grid-bottombar"></div>',
32820                  
32821                   '<div class="x-grid-resize-proxy">&#160;</div>',
32822                "</div>"
32823             );
32824             tpls.master.disableformats = true;
32825         }
32826
32827         if(!tpls.header){
32828             tpls.header = new Roo.Template(
32829                '<table border="0" cellspacing="0" cellpadding="0">',
32830                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32831                "</table>{splits}"
32832             );
32833             tpls.header.disableformats = true;
32834         }
32835         tpls.header.compile();
32836
32837         if(!tpls.hcell){
32838             tpls.hcell = new Roo.Template(
32839                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32840                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32841                 "</div></td>"
32842              );
32843              tpls.hcell.disableFormats = true;
32844         }
32845         tpls.hcell.compile();
32846
32847         if(!tpls.hsplit){
32848             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32849                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32850             tpls.hsplit.disableFormats = true;
32851         }
32852         tpls.hsplit.compile();
32853
32854         if(!tpls.body){
32855             tpls.body = new Roo.Template(
32856                '<table border="0" cellspacing="0" cellpadding="0">',
32857                "<tbody>{rows}</tbody>",
32858                "</table>"
32859             );
32860             tpls.body.disableFormats = true;
32861         }
32862         tpls.body.compile();
32863
32864         if(!tpls.row){
32865             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32866             tpls.row.disableFormats = true;
32867         }
32868         tpls.row.compile();
32869
32870         if(!tpls.cell){
32871             tpls.cell = new Roo.Template(
32872                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32873                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32874                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32875                 "</td>"
32876             );
32877             tpls.cell.disableFormats = true;
32878         }
32879         tpls.cell.compile();
32880
32881         this.templates = tpls;
32882     },
32883
32884     // remap these for backwards compat
32885     onColWidthChange : function(){
32886         this.updateColumns.apply(this, arguments);
32887     },
32888     onHeaderChange : function(){
32889         this.updateHeaders.apply(this, arguments);
32890     }, 
32891     onHiddenChange : function(){
32892         this.handleHiddenChange.apply(this, arguments);
32893     },
32894     onColumnMove : function(){
32895         this.handleColumnMove.apply(this, arguments);
32896     },
32897     onColumnLock : function(){
32898         this.handleLockChange.apply(this, arguments);
32899     },
32900
32901     onDataChange : function(){
32902         this.refresh();
32903         this.updateHeaderSortState();
32904     },
32905
32906     onClear : function(){
32907         this.refresh();
32908     },
32909
32910     onUpdate : function(ds, record){
32911         this.refreshRow(record);
32912     },
32913
32914     refreshRow : function(record){
32915         var ds = this.ds, index;
32916         if(typeof record == 'number'){
32917             index = record;
32918             record = ds.getAt(index);
32919         }else{
32920             index = ds.indexOf(record);
32921         }
32922         this.insertRows(ds, index, index, true);
32923         this.onRemove(ds, record, index+1, true);
32924         this.syncRowHeights(index, index);
32925         this.layout();
32926         this.fireEvent("rowupdated", this, index, record);
32927     },
32928
32929     onAdd : function(ds, records, index){
32930         this.insertRows(ds, index, index + (records.length-1));
32931     },
32932
32933     onRemove : function(ds, record, index, isUpdate){
32934         if(isUpdate !== true){
32935             this.fireEvent("beforerowremoved", this, index, record);
32936         }
32937         var bt = this.getBodyTable(), lt = this.getLockedTable();
32938         if(bt.rows[index]){
32939             bt.firstChild.removeChild(bt.rows[index]);
32940         }
32941         if(lt.rows[index]){
32942             lt.firstChild.removeChild(lt.rows[index]);
32943         }
32944         if(isUpdate !== true){
32945             this.stripeRows(index);
32946             this.syncRowHeights(index, index);
32947             this.layout();
32948             this.fireEvent("rowremoved", this, index, record);
32949         }
32950     },
32951
32952     onLoad : function(){
32953         this.scrollToTop();
32954     },
32955
32956     /**
32957      * Scrolls the grid to the top
32958      */
32959     scrollToTop : function(){
32960         if(this.scroller){
32961             this.scroller.dom.scrollTop = 0;
32962             this.syncScroll();
32963         }
32964     },
32965
32966     /**
32967      * Gets a panel in the header of the grid that can be used for toolbars etc.
32968      * After modifying the contents of this panel a call to grid.autoSize() may be
32969      * required to register any changes in size.
32970      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32971      * @return Roo.Element
32972      */
32973     getHeaderPanel : function(doShow){
32974         if(doShow){
32975             this.headerPanel.show();
32976         }
32977         return this.headerPanel;
32978     },
32979
32980     /**
32981      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32982      * After modifying the contents of this panel a call to grid.autoSize() may be
32983      * required to register any changes in size.
32984      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32985      * @return Roo.Element
32986      */
32987     getFooterPanel : function(doShow){
32988         if(doShow){
32989             this.footerPanel.show();
32990         }
32991         return this.footerPanel;
32992     },
32993
32994     initElements : function(){
32995         var E = Roo.Element;
32996         var el = this.grid.getGridEl().dom.firstChild;
32997         var cs = el.childNodes;
32998
32999         this.el = new E(el);
33000         
33001          this.focusEl = new E(el.firstChild);
33002         this.focusEl.swallowEvent("click", true);
33003         
33004         this.headerPanel = new E(cs[1]);
33005         this.headerPanel.enableDisplayMode("block");
33006
33007         this.scroller = new E(cs[2]);
33008         this.scrollSizer = new E(this.scroller.dom.firstChild);
33009
33010         this.lockedWrap = new E(cs[3]);
33011         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33012         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33013
33014         this.mainWrap = new E(cs[4]);
33015         this.mainHd = new E(this.mainWrap.dom.firstChild);
33016         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33017
33018         this.footerPanel = new E(cs[5]);
33019         this.footerPanel.enableDisplayMode("block");
33020
33021         this.resizeProxy = new E(cs[6]);
33022
33023         this.headerSelector = String.format(
33024            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33025            this.lockedHd.id, this.mainHd.id
33026         );
33027
33028         this.splitterSelector = String.format(
33029            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33030            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33031         );
33032     },
33033     idToCssName : function(s)
33034     {
33035         return s.replace(/[^a-z0-9]+/ig, '-');
33036     },
33037
33038     getHeaderCell : function(index){
33039         return Roo.DomQuery.select(this.headerSelector)[index];
33040     },
33041
33042     getHeaderCellMeasure : function(index){
33043         return this.getHeaderCell(index).firstChild;
33044     },
33045
33046     getHeaderCellText : function(index){
33047         return this.getHeaderCell(index).firstChild.firstChild;
33048     },
33049
33050     getLockedTable : function(){
33051         return this.lockedBody.dom.firstChild;
33052     },
33053
33054     getBodyTable : function(){
33055         return this.mainBody.dom.firstChild;
33056     },
33057
33058     getLockedRow : function(index){
33059         return this.getLockedTable().rows[index];
33060     },
33061
33062     getRow : function(index){
33063         return this.getBodyTable().rows[index];
33064     },
33065
33066     getRowComposite : function(index){
33067         if(!this.rowEl){
33068             this.rowEl = new Roo.CompositeElementLite();
33069         }
33070         var els = [], lrow, mrow;
33071         if(lrow = this.getLockedRow(index)){
33072             els.push(lrow);
33073         }
33074         if(mrow = this.getRow(index)){
33075             els.push(mrow);
33076         }
33077         this.rowEl.elements = els;
33078         return this.rowEl;
33079     },
33080     /**
33081      * Gets the 'td' of the cell
33082      * 
33083      * @param {Integer} rowIndex row to select
33084      * @param {Integer} colIndex column to select
33085      * 
33086      * @return {Object} 
33087      */
33088     getCell : function(rowIndex, colIndex){
33089         var locked = this.cm.getLockedCount();
33090         var source;
33091         if(colIndex < locked){
33092             source = this.lockedBody.dom.firstChild;
33093         }else{
33094             source = this.mainBody.dom.firstChild;
33095             colIndex -= locked;
33096         }
33097         return source.rows[rowIndex].childNodes[colIndex];
33098     },
33099
33100     getCellText : function(rowIndex, colIndex){
33101         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33102     },
33103
33104     getCellBox : function(cell){
33105         var b = this.fly(cell).getBox();
33106         if(Roo.isOpera){ // opera fails to report the Y
33107             b.y = cell.offsetTop + this.mainBody.getY();
33108         }
33109         return b;
33110     },
33111
33112     getCellIndex : function(cell){
33113         var id = String(cell.className).match(this.cellRE);
33114         if(id){
33115             return parseInt(id[1], 10);
33116         }
33117         return 0;
33118     },
33119
33120     findHeaderIndex : function(n){
33121         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33122         return r ? this.getCellIndex(r) : false;
33123     },
33124
33125     findHeaderCell : function(n){
33126         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33127         return r ? r : false;
33128     },
33129
33130     findRowIndex : function(n){
33131         if(!n){
33132             return false;
33133         }
33134         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33135         return r ? r.rowIndex : false;
33136     },
33137
33138     findCellIndex : function(node){
33139         var stop = this.el.dom;
33140         while(node && node != stop){
33141             if(this.findRE.test(node.className)){
33142                 return this.getCellIndex(node);
33143             }
33144             node = node.parentNode;
33145         }
33146         return false;
33147     },
33148
33149     getColumnId : function(index){
33150         return this.cm.getColumnId(index);
33151     },
33152
33153     getSplitters : function()
33154     {
33155         if(this.splitterSelector){
33156            return Roo.DomQuery.select(this.splitterSelector);
33157         }else{
33158             return null;
33159       }
33160     },
33161
33162     getSplitter : function(index){
33163         return this.getSplitters()[index];
33164     },
33165
33166     onRowOver : function(e, t){
33167         var row;
33168         if((row = this.findRowIndex(t)) !== false){
33169             this.getRowComposite(row).addClass("x-grid-row-over");
33170         }
33171     },
33172
33173     onRowOut : function(e, t){
33174         var row;
33175         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33176             this.getRowComposite(row).removeClass("x-grid-row-over");
33177         }
33178     },
33179
33180     renderHeaders : function(){
33181         var cm = this.cm;
33182         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33183         var cb = [], lb = [], sb = [], lsb = [], p = {};
33184         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33185             p.cellId = "x-grid-hd-0-" + i;
33186             p.splitId = "x-grid-csplit-0-" + i;
33187             p.id = cm.getColumnId(i);
33188             p.value = cm.getColumnHeader(i) || "";
33189             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33190             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33191             if(!cm.isLocked(i)){
33192                 cb[cb.length] = ct.apply(p);
33193                 sb[sb.length] = st.apply(p);
33194             }else{
33195                 lb[lb.length] = ct.apply(p);
33196                 lsb[lsb.length] = st.apply(p);
33197             }
33198         }
33199         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33200                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33201     },
33202
33203     updateHeaders : function(){
33204         var html = this.renderHeaders();
33205         this.lockedHd.update(html[0]);
33206         this.mainHd.update(html[1]);
33207     },
33208
33209     /**
33210      * Focuses the specified row.
33211      * @param {Number} row The row index
33212      */
33213     focusRow : function(row)
33214     {
33215         //Roo.log('GridView.focusRow');
33216         var x = this.scroller.dom.scrollLeft;
33217         this.focusCell(row, 0, false);
33218         this.scroller.dom.scrollLeft = x;
33219     },
33220
33221     /**
33222      * Focuses the specified cell.
33223      * @param {Number} row The row index
33224      * @param {Number} col The column index
33225      * @param {Boolean} hscroll false to disable horizontal scrolling
33226      */
33227     focusCell : function(row, col, hscroll)
33228     {
33229         //Roo.log('GridView.focusCell');
33230         var el = this.ensureVisible(row, col, hscroll);
33231         this.focusEl.alignTo(el, "tl-tl");
33232         if(Roo.isGecko){
33233             this.focusEl.focus();
33234         }else{
33235             this.focusEl.focus.defer(1, this.focusEl);
33236         }
33237     },
33238
33239     /**
33240      * Scrolls the specified cell into view
33241      * @param {Number} row The row index
33242      * @param {Number} col The column index
33243      * @param {Boolean} hscroll false to disable horizontal scrolling
33244      */
33245     ensureVisible : function(row, col, hscroll)
33246     {
33247         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33248         //return null; //disable for testing.
33249         if(typeof row != "number"){
33250             row = row.rowIndex;
33251         }
33252         if(row < 0 && row >= this.ds.getCount()){
33253             return  null;
33254         }
33255         col = (col !== undefined ? col : 0);
33256         var cm = this.grid.colModel;
33257         while(cm.isHidden(col)){
33258             col++;
33259         }
33260
33261         var el = this.getCell(row, col);
33262         if(!el){
33263             return null;
33264         }
33265         var c = this.scroller.dom;
33266
33267         var ctop = parseInt(el.offsetTop, 10);
33268         var cleft = parseInt(el.offsetLeft, 10);
33269         var cbot = ctop + el.offsetHeight;
33270         var cright = cleft + el.offsetWidth;
33271         
33272         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33273         var stop = parseInt(c.scrollTop, 10);
33274         var sleft = parseInt(c.scrollLeft, 10);
33275         var sbot = stop + ch;
33276         var sright = sleft + c.clientWidth;
33277         /*
33278         Roo.log('GridView.ensureVisible:' +
33279                 ' ctop:' + ctop +
33280                 ' c.clientHeight:' + c.clientHeight +
33281                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33282                 ' stop:' + stop +
33283                 ' cbot:' + cbot +
33284                 ' sbot:' + sbot +
33285                 ' ch:' + ch  
33286                 );
33287         */
33288         if(ctop < stop){
33289              c.scrollTop = ctop;
33290             //Roo.log("set scrolltop to ctop DISABLE?");
33291         }else if(cbot > sbot){
33292             //Roo.log("set scrolltop to cbot-ch");
33293             c.scrollTop = cbot-ch;
33294         }
33295         
33296         if(hscroll !== false){
33297             if(cleft < sleft){
33298                 c.scrollLeft = cleft;
33299             }else if(cright > sright){
33300                 c.scrollLeft = cright-c.clientWidth;
33301             }
33302         }
33303          
33304         return el;
33305     },
33306
33307     updateColumns : function(){
33308         this.grid.stopEditing();
33309         var cm = this.grid.colModel, colIds = this.getColumnIds();
33310         //var totalWidth = cm.getTotalWidth();
33311         var pos = 0;
33312         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33313             //if(cm.isHidden(i)) continue;
33314             var w = cm.getColumnWidth(i);
33315             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33316             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33317         }
33318         this.updateSplitters();
33319     },
33320
33321     generateRules : function(cm){
33322         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33323         Roo.util.CSS.removeStyleSheet(rulesId);
33324         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33325             var cid = cm.getColumnId(i);
33326             var align = '';
33327             if(cm.config[i].align){
33328                 align = 'text-align:'+cm.config[i].align+';';
33329             }
33330             var hidden = '';
33331             if(cm.isHidden(i)){
33332                 hidden = 'display:none;';
33333             }
33334             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33335             ruleBuf.push(
33336                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33337                     this.hdSelector, cid, " {\n", align, width, "}\n",
33338                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33339                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33340         }
33341         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33342     },
33343
33344     updateSplitters : function(){
33345         var cm = this.cm, s = this.getSplitters();
33346         if(s){ // splitters not created yet
33347             var pos = 0, locked = true;
33348             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33349                 if(cm.isHidden(i)) {
33350                     continue;
33351                 }
33352                 var w = cm.getColumnWidth(i); // make sure it's a number
33353                 if(!cm.isLocked(i) && locked){
33354                     pos = 0;
33355                     locked = false;
33356                 }
33357                 pos += w;
33358                 s[i].style.left = (pos-this.splitOffset) + "px";
33359             }
33360         }
33361     },
33362
33363     handleHiddenChange : function(colModel, colIndex, hidden){
33364         if(hidden){
33365             this.hideColumn(colIndex);
33366         }else{
33367             this.unhideColumn(colIndex);
33368         }
33369     },
33370
33371     hideColumn : function(colIndex){
33372         var cid = this.getColumnId(colIndex);
33373         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33374         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33375         if(Roo.isSafari){
33376             this.updateHeaders();
33377         }
33378         this.updateSplitters();
33379         this.layout();
33380     },
33381
33382     unhideColumn : function(colIndex){
33383         var cid = this.getColumnId(colIndex);
33384         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33385         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33386
33387         if(Roo.isSafari){
33388             this.updateHeaders();
33389         }
33390         this.updateSplitters();
33391         this.layout();
33392     },
33393
33394     insertRows : function(dm, firstRow, lastRow, isUpdate){
33395         if(firstRow == 0 && lastRow == dm.getCount()-1){
33396             this.refresh();
33397         }else{
33398             if(!isUpdate){
33399                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33400             }
33401             var s = this.getScrollState();
33402             var markup = this.renderRows(firstRow, lastRow);
33403             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33404             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33405             this.restoreScroll(s);
33406             if(!isUpdate){
33407                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33408                 this.syncRowHeights(firstRow, lastRow);
33409                 this.stripeRows(firstRow);
33410                 this.layout();
33411             }
33412         }
33413     },
33414
33415     bufferRows : function(markup, target, index){
33416         var before = null, trows = target.rows, tbody = target.tBodies[0];
33417         if(index < trows.length){
33418             before = trows[index];
33419         }
33420         var b = document.createElement("div");
33421         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33422         var rows = b.firstChild.rows;
33423         for(var i = 0, len = rows.length; i < len; i++){
33424             if(before){
33425                 tbody.insertBefore(rows[0], before);
33426             }else{
33427                 tbody.appendChild(rows[0]);
33428             }
33429         }
33430         b.innerHTML = "";
33431         b = null;
33432     },
33433
33434     deleteRows : function(dm, firstRow, lastRow){
33435         if(dm.getRowCount()<1){
33436             this.fireEvent("beforerefresh", this);
33437             this.mainBody.update("");
33438             this.lockedBody.update("");
33439             this.fireEvent("refresh", this);
33440         }else{
33441             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33442             var bt = this.getBodyTable();
33443             var tbody = bt.firstChild;
33444             var rows = bt.rows;
33445             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33446                 tbody.removeChild(rows[firstRow]);
33447             }
33448             this.stripeRows(firstRow);
33449             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33450         }
33451     },
33452
33453     updateRows : function(dataSource, firstRow, lastRow){
33454         var s = this.getScrollState();
33455         this.refresh();
33456         this.restoreScroll(s);
33457     },
33458
33459     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33460         if(!noRefresh){
33461            this.refresh();
33462         }
33463         this.updateHeaderSortState();
33464     },
33465
33466     getScrollState : function(){
33467         
33468         var sb = this.scroller.dom;
33469         return {left: sb.scrollLeft, top: sb.scrollTop};
33470     },
33471
33472     stripeRows : function(startRow){
33473         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33474             return;
33475         }
33476         startRow = startRow || 0;
33477         var rows = this.getBodyTable().rows;
33478         var lrows = this.getLockedTable().rows;
33479         var cls = ' x-grid-row-alt ';
33480         for(var i = startRow, len = rows.length; i < len; i++){
33481             var row = rows[i], lrow = lrows[i];
33482             var isAlt = ((i+1) % 2 == 0);
33483             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33484             if(isAlt == hasAlt){
33485                 continue;
33486             }
33487             if(isAlt){
33488                 row.className += " x-grid-row-alt";
33489             }else{
33490                 row.className = row.className.replace("x-grid-row-alt", "");
33491             }
33492             if(lrow){
33493                 lrow.className = row.className;
33494             }
33495         }
33496     },
33497
33498     restoreScroll : function(state){
33499         //Roo.log('GridView.restoreScroll');
33500         var sb = this.scroller.dom;
33501         sb.scrollLeft = state.left;
33502         sb.scrollTop = state.top;
33503         this.syncScroll();
33504     },
33505
33506     syncScroll : function(){
33507         //Roo.log('GridView.syncScroll');
33508         var sb = this.scroller.dom;
33509         var sh = this.mainHd.dom;
33510         var bs = this.mainBody.dom;
33511         var lv = this.lockedBody.dom;
33512         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33513         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33514     },
33515
33516     handleScroll : function(e){
33517         this.syncScroll();
33518         var sb = this.scroller.dom;
33519         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33520         e.stopEvent();
33521     },
33522
33523     handleWheel : function(e){
33524         var d = e.getWheelDelta();
33525         this.scroller.dom.scrollTop -= d*22;
33526         // set this here to prevent jumpy scrolling on large tables
33527         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33528         e.stopEvent();
33529     },
33530
33531     renderRows : function(startRow, endRow){
33532         // pull in all the crap needed to render rows
33533         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33534         var colCount = cm.getColumnCount();
33535
33536         if(ds.getCount() < 1){
33537             return ["", ""];
33538         }
33539
33540         // build a map for all the columns
33541         var cs = [];
33542         for(var i = 0; i < colCount; i++){
33543             var name = cm.getDataIndex(i);
33544             cs[i] = {
33545                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33546                 renderer : cm.getRenderer(i),
33547                 id : cm.getColumnId(i),
33548                 locked : cm.isLocked(i),
33549                 has_editor : cm.isCellEditable(i)
33550             };
33551         }
33552
33553         startRow = startRow || 0;
33554         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33555
33556         // records to render
33557         var rs = ds.getRange(startRow, endRow);
33558
33559         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33560     },
33561
33562     // As much as I hate to duplicate code, this was branched because FireFox really hates
33563     // [].join("") on strings. The performance difference was substantial enough to
33564     // branch this function
33565     doRender : Roo.isGecko ?
33566             function(cs, rs, ds, startRow, colCount, stripe){
33567                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33568                 // buffers
33569                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33570                 
33571                 var hasListener = this.grid.hasListener('rowclass');
33572                 var rowcfg = {};
33573                 for(var j = 0, len = rs.length; j < len; j++){
33574                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33575                     for(var i = 0; i < colCount; i++){
33576                         c = cs[i];
33577                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33578                         p.id = c.id;
33579                         p.css = p.attr = "";
33580                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33581                         if(p.value == undefined || p.value === "") {
33582                             p.value = "&#160;";
33583                         }
33584                         if(c.has_editor){
33585                             p.css += ' x-grid-editable-cell';
33586                         }
33587                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33588                             p.css +=  ' x-grid-dirty-cell';
33589                         }
33590                         var markup = ct.apply(p);
33591                         if(!c.locked){
33592                             cb+= markup;
33593                         }else{
33594                             lcb+= markup;
33595                         }
33596                     }
33597                     var alt = [];
33598                     if(stripe && ((rowIndex+1) % 2 == 0)){
33599                         alt.push("x-grid-row-alt")
33600                     }
33601                     if(r.dirty){
33602                         alt.push(  " x-grid-dirty-row");
33603                     }
33604                     rp.cells = lcb;
33605                     if(this.getRowClass){
33606                         alt.push(this.getRowClass(r, rowIndex));
33607                     }
33608                     if (hasListener) {
33609                         rowcfg = {
33610                              
33611                             record: r,
33612                             rowIndex : rowIndex,
33613                             rowClass : ''
33614                         };
33615                         this.grid.fireEvent('rowclass', this, rowcfg);
33616                         alt.push(rowcfg.rowClass);
33617                     }
33618                     rp.alt = alt.join(" ");
33619                     lbuf+= rt.apply(rp);
33620                     rp.cells = cb;
33621                     buf+=  rt.apply(rp);
33622                 }
33623                 return [lbuf, buf];
33624             } :
33625             function(cs, rs, ds, startRow, colCount, stripe){
33626                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33627                 // buffers
33628                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33629                 var hasListener = this.grid.hasListener('rowclass');
33630  
33631                 var rowcfg = {};
33632                 for(var j = 0, len = rs.length; j < len; j++){
33633                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33634                     for(var i = 0; i < colCount; i++){
33635                         c = cs[i];
33636                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33637                         p.id = c.id;
33638                         p.css = p.attr = "";
33639                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33640                         if(p.value == undefined || p.value === "") {
33641                             p.value = "&#160;";
33642                         }
33643                         //Roo.log(c);
33644                          if(c.has_editor){
33645                             p.css += ' x-grid-editable-cell';
33646                         }
33647                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33648                             p.css += ' x-grid-dirty-cell' 
33649                         }
33650                         
33651                         var markup = ct.apply(p);
33652                         if(!c.locked){
33653                             cb[cb.length] = markup;
33654                         }else{
33655                             lcb[lcb.length] = markup;
33656                         }
33657                     }
33658                     var alt = [];
33659                     if(stripe && ((rowIndex+1) % 2 == 0)){
33660                         alt.push( "x-grid-row-alt");
33661                     }
33662                     if(r.dirty){
33663                         alt.push(" x-grid-dirty-row");
33664                     }
33665                     rp.cells = lcb;
33666                     if(this.getRowClass){
33667                         alt.push( this.getRowClass(r, rowIndex));
33668                     }
33669                     if (hasListener) {
33670                         rowcfg = {
33671                              
33672                             record: r,
33673                             rowIndex : rowIndex,
33674                             rowClass : ''
33675                         };
33676                         this.grid.fireEvent('rowclass', this, rowcfg);
33677                         alt.push(rowcfg.rowClass);
33678                     }
33679                     
33680                     rp.alt = alt.join(" ");
33681                     rp.cells = lcb.join("");
33682                     lbuf[lbuf.length] = rt.apply(rp);
33683                     rp.cells = cb.join("");
33684                     buf[buf.length] =  rt.apply(rp);
33685                 }
33686                 return [lbuf.join(""), buf.join("")];
33687             },
33688
33689     renderBody : function(){
33690         var markup = this.renderRows();
33691         var bt = this.templates.body;
33692         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33693     },
33694
33695     /**
33696      * Refreshes the grid
33697      * @param {Boolean} headersToo
33698      */
33699     refresh : function(headersToo){
33700         this.fireEvent("beforerefresh", this);
33701         this.grid.stopEditing();
33702         var result = this.renderBody();
33703         this.lockedBody.update(result[0]);
33704         this.mainBody.update(result[1]);
33705         if(headersToo === true){
33706             this.updateHeaders();
33707             this.updateColumns();
33708             this.updateSplitters();
33709             this.updateHeaderSortState();
33710         }
33711         this.syncRowHeights();
33712         this.layout();
33713         this.fireEvent("refresh", this);
33714     },
33715
33716     handleColumnMove : function(cm, oldIndex, newIndex){
33717         this.indexMap = null;
33718         var s = this.getScrollState();
33719         this.refresh(true);
33720         this.restoreScroll(s);
33721         this.afterMove(newIndex);
33722     },
33723
33724     afterMove : function(colIndex){
33725         if(this.enableMoveAnim && Roo.enableFx){
33726             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33727         }
33728         // if multisort - fix sortOrder, and reload..
33729         if (this.grid.dataSource.multiSort) {
33730             // the we can call sort again..
33731             var dm = this.grid.dataSource;
33732             var cm = this.grid.colModel;
33733             var so = [];
33734             for(var i = 0; i < cm.config.length; i++ ) {
33735                 
33736                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33737                     continue; // dont' bother, it's not in sort list or being set.
33738                 }
33739                 
33740                 so.push(cm.config[i].dataIndex);
33741             };
33742             dm.sortOrder = so;
33743             dm.load(dm.lastOptions);
33744             
33745             
33746         }
33747         
33748     },
33749
33750     updateCell : function(dm, rowIndex, dataIndex){
33751         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33752         if(typeof colIndex == "undefined"){ // not present in grid
33753             return;
33754         }
33755         var cm = this.grid.colModel;
33756         var cell = this.getCell(rowIndex, colIndex);
33757         var cellText = this.getCellText(rowIndex, colIndex);
33758
33759         var p = {
33760             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33761             id : cm.getColumnId(colIndex),
33762             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33763         };
33764         var renderer = cm.getRenderer(colIndex);
33765         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33766         if(typeof val == "undefined" || val === "") {
33767             val = "&#160;";
33768         }
33769         cellText.innerHTML = val;
33770         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33771         this.syncRowHeights(rowIndex, rowIndex);
33772     },
33773
33774     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33775         var maxWidth = 0;
33776         if(this.grid.autoSizeHeaders){
33777             var h = this.getHeaderCellMeasure(colIndex);
33778             maxWidth = Math.max(maxWidth, h.scrollWidth);
33779         }
33780         var tb, index;
33781         if(this.cm.isLocked(colIndex)){
33782             tb = this.getLockedTable();
33783             index = colIndex;
33784         }else{
33785             tb = this.getBodyTable();
33786             index = colIndex - this.cm.getLockedCount();
33787         }
33788         if(tb && tb.rows){
33789             var rows = tb.rows;
33790             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33791             for(var i = 0; i < stopIndex; i++){
33792                 var cell = rows[i].childNodes[index].firstChild;
33793                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33794             }
33795         }
33796         return maxWidth + /*margin for error in IE*/ 5;
33797     },
33798     /**
33799      * Autofit a column to its content.
33800      * @param {Number} colIndex
33801      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33802      */
33803      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33804          if(this.cm.isHidden(colIndex)){
33805              return; // can't calc a hidden column
33806          }
33807         if(forceMinSize){
33808             var cid = this.cm.getColumnId(colIndex);
33809             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33810            if(this.grid.autoSizeHeaders){
33811                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33812            }
33813         }
33814         var newWidth = this.calcColumnWidth(colIndex);
33815         this.cm.setColumnWidth(colIndex,
33816             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33817         if(!suppressEvent){
33818             this.grid.fireEvent("columnresize", colIndex, newWidth);
33819         }
33820     },
33821
33822     /**
33823      * Autofits all columns to their content and then expands to fit any extra space in the grid
33824      */
33825      autoSizeColumns : function(){
33826         var cm = this.grid.colModel;
33827         var colCount = cm.getColumnCount();
33828         for(var i = 0; i < colCount; i++){
33829             this.autoSizeColumn(i, true, true);
33830         }
33831         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33832             this.fitColumns();
33833         }else{
33834             this.updateColumns();
33835             this.layout();
33836         }
33837     },
33838
33839     /**
33840      * Autofits all columns to the grid's width proportionate with their current size
33841      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33842      */
33843     fitColumns : function(reserveScrollSpace){
33844         var cm = this.grid.colModel;
33845         var colCount = cm.getColumnCount();
33846         var cols = [];
33847         var width = 0;
33848         var i, w;
33849         for (i = 0; i < colCount; i++){
33850             if(!cm.isHidden(i) && !cm.isFixed(i)){
33851                 w = cm.getColumnWidth(i);
33852                 cols.push(i);
33853                 cols.push(w);
33854                 width += w;
33855             }
33856         }
33857         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33858         if(reserveScrollSpace){
33859             avail -= 17;
33860         }
33861         var frac = (avail - cm.getTotalWidth())/width;
33862         while (cols.length){
33863             w = cols.pop();
33864             i = cols.pop();
33865             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33866         }
33867         this.updateColumns();
33868         this.layout();
33869     },
33870
33871     onRowSelect : function(rowIndex){
33872         var row = this.getRowComposite(rowIndex);
33873         row.addClass("x-grid-row-selected");
33874     },
33875
33876     onRowDeselect : function(rowIndex){
33877         var row = this.getRowComposite(rowIndex);
33878         row.removeClass("x-grid-row-selected");
33879     },
33880
33881     onCellSelect : function(row, col){
33882         var cell = this.getCell(row, col);
33883         if(cell){
33884             Roo.fly(cell).addClass("x-grid-cell-selected");
33885         }
33886     },
33887
33888     onCellDeselect : function(row, col){
33889         var cell = this.getCell(row, col);
33890         if(cell){
33891             Roo.fly(cell).removeClass("x-grid-cell-selected");
33892         }
33893     },
33894
33895     updateHeaderSortState : function(){
33896         
33897         // sort state can be single { field: xxx, direction : yyy}
33898         // or   { xxx=>ASC , yyy : DESC ..... }
33899         
33900         var mstate = {};
33901         if (!this.ds.multiSort) { 
33902             var state = this.ds.getSortState();
33903             if(!state){
33904                 return;
33905             }
33906             mstate[state.field] = state.direction;
33907             // FIXME... - this is not used here.. but might be elsewhere..
33908             this.sortState = state;
33909             
33910         } else {
33911             mstate = this.ds.sortToggle;
33912         }
33913         //remove existing sort classes..
33914         
33915         var sc = this.sortClasses;
33916         var hds = this.el.select(this.headerSelector).removeClass(sc);
33917         
33918         for(var f in mstate) {
33919         
33920             var sortColumn = this.cm.findColumnIndex(f);
33921             
33922             if(sortColumn != -1){
33923                 var sortDir = mstate[f];        
33924                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33925             }
33926         }
33927         
33928          
33929         
33930     },
33931
33932
33933     handleHeaderClick : function(g, index,e){
33934         
33935         Roo.log("header click");
33936         
33937         if (Roo.isTouch) {
33938             // touch events on header are handled by context
33939             this.handleHdCtx(g,index,e);
33940             return;
33941         }
33942         
33943         
33944         if(this.headersDisabled){
33945             return;
33946         }
33947         var dm = g.dataSource, cm = g.colModel;
33948         if(!cm.isSortable(index)){
33949             return;
33950         }
33951         g.stopEditing();
33952         
33953         if (dm.multiSort) {
33954             // update the sortOrder
33955             var so = [];
33956             for(var i = 0; i < cm.config.length; i++ ) {
33957                 
33958                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33959                     continue; // dont' bother, it's not in sort list or being set.
33960                 }
33961                 
33962                 so.push(cm.config[i].dataIndex);
33963             };
33964             dm.sortOrder = so;
33965         }
33966         
33967         
33968         dm.sort(cm.getDataIndex(index));
33969     },
33970
33971
33972     destroy : function(){
33973         if(this.colMenu){
33974             this.colMenu.removeAll();
33975             Roo.menu.MenuMgr.unregister(this.colMenu);
33976             this.colMenu.getEl().remove();
33977             delete this.colMenu;
33978         }
33979         if(this.hmenu){
33980             this.hmenu.removeAll();
33981             Roo.menu.MenuMgr.unregister(this.hmenu);
33982             this.hmenu.getEl().remove();
33983             delete this.hmenu;
33984         }
33985         if(this.grid.enableColumnMove){
33986             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33987             if(dds){
33988                 for(var dd in dds){
33989                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33990                         var elid = dds[dd].dragElId;
33991                         dds[dd].unreg();
33992                         Roo.get(elid).remove();
33993                     } else if(dds[dd].config.isTarget){
33994                         dds[dd].proxyTop.remove();
33995                         dds[dd].proxyBottom.remove();
33996                         dds[dd].unreg();
33997                     }
33998                     if(Roo.dd.DDM.locationCache[dd]){
33999                         delete Roo.dd.DDM.locationCache[dd];
34000                     }
34001                 }
34002                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34003             }
34004         }
34005         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34006         this.bind(null, null);
34007         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34008     },
34009
34010     handleLockChange : function(){
34011         this.refresh(true);
34012     },
34013
34014     onDenyColumnLock : function(){
34015
34016     },
34017
34018     onDenyColumnHide : function(){
34019
34020     },
34021
34022     handleHdMenuClick : function(item){
34023         var index = this.hdCtxIndex;
34024         var cm = this.cm, ds = this.ds;
34025         switch(item.id){
34026             case "asc":
34027                 ds.sort(cm.getDataIndex(index), "ASC");
34028                 break;
34029             case "desc":
34030                 ds.sort(cm.getDataIndex(index), "DESC");
34031                 break;
34032             case "lock":
34033                 var lc = cm.getLockedCount();
34034                 if(cm.getColumnCount(true) <= lc+1){
34035                     this.onDenyColumnLock();
34036                     return;
34037                 }
34038                 if(lc != index){
34039                     cm.setLocked(index, true, true);
34040                     cm.moveColumn(index, lc);
34041                     this.grid.fireEvent("columnmove", index, lc);
34042                 }else{
34043                     cm.setLocked(index, true);
34044                 }
34045             break;
34046             case "unlock":
34047                 var lc = cm.getLockedCount();
34048                 if((lc-1) != index){
34049                     cm.setLocked(index, false, true);
34050                     cm.moveColumn(index, lc-1);
34051                     this.grid.fireEvent("columnmove", index, lc-1);
34052                 }else{
34053                     cm.setLocked(index, false);
34054                 }
34055             break;
34056             case 'wider': // used to expand cols on touch..
34057             case 'narrow':
34058                 var cw = cm.getColumnWidth(index);
34059                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34060                 cw = Math.max(0, cw);
34061                 cw = Math.min(cw,4000);
34062                 cm.setColumnWidth(index, cw);
34063                 break;
34064                 
34065             default:
34066                 index = cm.getIndexById(item.id.substr(4));
34067                 if(index != -1){
34068                     if(item.checked && cm.getColumnCount(true) <= 1){
34069                         this.onDenyColumnHide();
34070                         return false;
34071                     }
34072                     cm.setHidden(index, item.checked);
34073                 }
34074         }
34075         return true;
34076     },
34077
34078     beforeColMenuShow : function(){
34079         var cm = this.cm,  colCount = cm.getColumnCount();
34080         this.colMenu.removeAll();
34081         for(var i = 0; i < colCount; i++){
34082             this.colMenu.add(new Roo.menu.CheckItem({
34083                 id: "col-"+cm.getColumnId(i),
34084                 text: cm.getColumnHeader(i),
34085                 checked: !cm.isHidden(i),
34086                 hideOnClick:false
34087             }));
34088         }
34089     },
34090
34091     handleHdCtx : function(g, index, e){
34092         e.stopEvent();
34093         var hd = this.getHeaderCell(index);
34094         this.hdCtxIndex = index;
34095         var ms = this.hmenu.items, cm = this.cm;
34096         ms.get("asc").setDisabled(!cm.isSortable(index));
34097         ms.get("desc").setDisabled(!cm.isSortable(index));
34098         if(this.grid.enableColLock !== false){
34099             ms.get("lock").setDisabled(cm.isLocked(index));
34100             ms.get("unlock").setDisabled(!cm.isLocked(index));
34101         }
34102         this.hmenu.show(hd, "tl-bl");
34103     },
34104
34105     handleHdOver : function(e){
34106         var hd = this.findHeaderCell(e.getTarget());
34107         if(hd && !this.headersDisabled){
34108             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34109                this.fly(hd).addClass("x-grid-hd-over");
34110             }
34111         }
34112     },
34113
34114     handleHdOut : function(e){
34115         var hd = this.findHeaderCell(e.getTarget());
34116         if(hd){
34117             this.fly(hd).removeClass("x-grid-hd-over");
34118         }
34119     },
34120
34121     handleSplitDblClick : function(e, t){
34122         var i = this.getCellIndex(t);
34123         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34124             this.autoSizeColumn(i, true);
34125             this.layout();
34126         }
34127     },
34128
34129     render : function(){
34130
34131         var cm = this.cm;
34132         var colCount = cm.getColumnCount();
34133
34134         if(this.grid.monitorWindowResize === true){
34135             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34136         }
34137         var header = this.renderHeaders();
34138         var body = this.templates.body.apply({rows:""});
34139         var html = this.templates.master.apply({
34140             lockedBody: body,
34141             body: body,
34142             lockedHeader: header[0],
34143             header: header[1]
34144         });
34145
34146         //this.updateColumns();
34147
34148         this.grid.getGridEl().dom.innerHTML = html;
34149
34150         this.initElements();
34151         
34152         // a kludge to fix the random scolling effect in webkit
34153         this.el.on("scroll", function() {
34154             this.el.dom.scrollTop=0; // hopefully not recursive..
34155         },this);
34156
34157         this.scroller.on("scroll", this.handleScroll, this);
34158         this.lockedBody.on("mousewheel", this.handleWheel, this);
34159         this.mainBody.on("mousewheel", this.handleWheel, this);
34160
34161         this.mainHd.on("mouseover", this.handleHdOver, this);
34162         this.mainHd.on("mouseout", this.handleHdOut, this);
34163         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34164                 {delegate: "."+this.splitClass});
34165
34166         this.lockedHd.on("mouseover", this.handleHdOver, this);
34167         this.lockedHd.on("mouseout", this.handleHdOut, this);
34168         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34169                 {delegate: "."+this.splitClass});
34170
34171         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34172             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34173         }
34174
34175         this.updateSplitters();
34176
34177         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34178             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34179             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34180         }
34181
34182         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34183             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34184             this.hmenu.add(
34185                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34186                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34187             );
34188             if(this.grid.enableColLock !== false){
34189                 this.hmenu.add('-',
34190                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34191                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34192                 );
34193             }
34194             if (Roo.isTouch) {
34195                  this.hmenu.add('-',
34196                     {id:"wider", text: this.columnsWiderText},
34197                     {id:"narrow", text: this.columnsNarrowText }
34198                 );
34199                 
34200                  
34201             }
34202             
34203             if(this.grid.enableColumnHide !== false){
34204
34205                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34206                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34207                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34208
34209                 this.hmenu.add('-',
34210                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34211                 );
34212             }
34213             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34214
34215             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34216         }
34217
34218         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34219             this.dd = new Roo.grid.GridDragZone(this.grid, {
34220                 ddGroup : this.grid.ddGroup || 'GridDD'
34221             });
34222             
34223         }
34224
34225         /*
34226         for(var i = 0; i < colCount; i++){
34227             if(cm.isHidden(i)){
34228                 this.hideColumn(i);
34229             }
34230             if(cm.config[i].align){
34231                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34232                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34233             }
34234         }*/
34235         
34236         this.updateHeaderSortState();
34237
34238         this.beforeInitialResize();
34239         this.layout(true);
34240
34241         // two part rendering gives faster view to the user
34242         this.renderPhase2.defer(1, this);
34243     },
34244
34245     renderPhase2 : function(){
34246         // render the rows now
34247         this.refresh();
34248         if(this.grid.autoSizeColumns){
34249             this.autoSizeColumns();
34250         }
34251     },
34252
34253     beforeInitialResize : function(){
34254
34255     },
34256
34257     onColumnSplitterMoved : function(i, w){
34258         this.userResized = true;
34259         var cm = this.grid.colModel;
34260         cm.setColumnWidth(i, w, true);
34261         var cid = cm.getColumnId(i);
34262         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34263         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34264         this.updateSplitters();
34265         this.layout();
34266         this.grid.fireEvent("columnresize", i, w);
34267     },
34268
34269     syncRowHeights : function(startIndex, endIndex){
34270         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34271             startIndex = startIndex || 0;
34272             var mrows = this.getBodyTable().rows;
34273             var lrows = this.getLockedTable().rows;
34274             var len = mrows.length-1;
34275             endIndex = Math.min(endIndex || len, len);
34276             for(var i = startIndex; i <= endIndex; i++){
34277                 var m = mrows[i], l = lrows[i];
34278                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34279                 m.style.height = l.style.height = h + "px";
34280             }
34281         }
34282     },
34283
34284     layout : function(initialRender, is2ndPass){
34285         var g = this.grid;
34286         var auto = g.autoHeight;
34287         var scrollOffset = 16;
34288         var c = g.getGridEl(), cm = this.cm,
34289                 expandCol = g.autoExpandColumn,
34290                 gv = this;
34291         //c.beginMeasure();
34292
34293         if(!c.dom.offsetWidth){ // display:none?
34294             if(initialRender){
34295                 this.lockedWrap.show();
34296                 this.mainWrap.show();
34297             }
34298             return;
34299         }
34300
34301         var hasLock = this.cm.isLocked(0);
34302
34303         var tbh = this.headerPanel.getHeight();
34304         var bbh = this.footerPanel.getHeight();
34305
34306         if(auto){
34307             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34308             var newHeight = ch + c.getBorderWidth("tb");
34309             if(g.maxHeight){
34310                 newHeight = Math.min(g.maxHeight, newHeight);
34311             }
34312             c.setHeight(newHeight);
34313         }
34314
34315         if(g.autoWidth){
34316             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34317         }
34318
34319         var s = this.scroller;
34320
34321         var csize = c.getSize(true);
34322
34323         this.el.setSize(csize.width, csize.height);
34324
34325         this.headerPanel.setWidth(csize.width);
34326         this.footerPanel.setWidth(csize.width);
34327
34328         var hdHeight = this.mainHd.getHeight();
34329         var vw = csize.width;
34330         var vh = csize.height - (tbh + bbh);
34331
34332         s.setSize(vw, vh);
34333
34334         var bt = this.getBodyTable();
34335         
34336         if(cm.getLockedCount() == cm.config.length){
34337             bt = this.getLockedTable();
34338         }
34339         
34340         var ltWidth = hasLock ?
34341                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34342
34343         var scrollHeight = bt.offsetHeight;
34344         var scrollWidth = ltWidth + bt.offsetWidth;
34345         var vscroll = false, hscroll = false;
34346
34347         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34348
34349         var lw = this.lockedWrap, mw = this.mainWrap;
34350         var lb = this.lockedBody, mb = this.mainBody;
34351
34352         setTimeout(function(){
34353             var t = s.dom.offsetTop;
34354             var w = s.dom.clientWidth,
34355                 h = s.dom.clientHeight;
34356
34357             lw.setTop(t);
34358             lw.setSize(ltWidth, h);
34359
34360             mw.setLeftTop(ltWidth, t);
34361             mw.setSize(w-ltWidth, h);
34362
34363             lb.setHeight(h-hdHeight);
34364             mb.setHeight(h-hdHeight);
34365
34366             if(is2ndPass !== true && !gv.userResized && expandCol){
34367                 // high speed resize without full column calculation
34368                 
34369                 var ci = cm.getIndexById(expandCol);
34370                 if (ci < 0) {
34371                     ci = cm.findColumnIndex(expandCol);
34372                 }
34373                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34374                 var expandId = cm.getColumnId(ci);
34375                 var  tw = cm.getTotalWidth(false);
34376                 var currentWidth = cm.getColumnWidth(ci);
34377                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34378                 if(currentWidth != cw){
34379                     cm.setColumnWidth(ci, cw, true);
34380                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34381                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34382                     gv.updateSplitters();
34383                     gv.layout(false, true);
34384                 }
34385             }
34386
34387             if(initialRender){
34388                 lw.show();
34389                 mw.show();
34390             }
34391             //c.endMeasure();
34392         }, 10);
34393     },
34394
34395     onWindowResize : function(){
34396         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34397             return;
34398         }
34399         this.layout();
34400     },
34401
34402     appendFooter : function(parentEl){
34403         return null;
34404     },
34405
34406     sortAscText : "Sort Ascending",
34407     sortDescText : "Sort Descending",
34408     lockText : "Lock Column",
34409     unlockText : "Unlock Column",
34410     columnsText : "Columns",
34411  
34412     columnsWiderText : "Wider",
34413     columnsNarrowText : "Thinner"
34414 });
34415
34416
34417 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34418     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34419     this.proxy.el.addClass('x-grid3-col-dd');
34420 };
34421
34422 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34423     handleMouseDown : function(e){
34424
34425     },
34426
34427     callHandleMouseDown : function(e){
34428         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34429     }
34430 });
34431 /*
34432  * Based on:
34433  * Ext JS Library 1.1.1
34434  * Copyright(c) 2006-2007, Ext JS, LLC.
34435  *
34436  * Originally Released Under LGPL - original licence link has changed is not relivant.
34437  *
34438  * Fork - LGPL
34439  * <script type="text/javascript">
34440  */
34441  
34442 // private
34443 // This is a support class used internally by the Grid components
34444 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34445     this.grid = grid;
34446     this.view = grid.getView();
34447     this.proxy = this.view.resizeProxy;
34448     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34449         "gridSplitters" + this.grid.getGridEl().id, {
34450         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34451     });
34452     this.setHandleElId(Roo.id(hd));
34453     this.setOuterHandleElId(Roo.id(hd2));
34454     this.scroll = false;
34455 };
34456 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34457     fly: Roo.Element.fly,
34458
34459     b4StartDrag : function(x, y){
34460         this.view.headersDisabled = true;
34461         this.proxy.setHeight(this.view.mainWrap.getHeight());
34462         var w = this.cm.getColumnWidth(this.cellIndex);
34463         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34464         this.resetConstraints();
34465         this.setXConstraint(minw, 1000);
34466         this.setYConstraint(0, 0);
34467         this.minX = x - minw;
34468         this.maxX = x + 1000;
34469         this.startPos = x;
34470         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34471     },
34472
34473
34474     handleMouseDown : function(e){
34475         ev = Roo.EventObject.setEvent(e);
34476         var t = this.fly(ev.getTarget());
34477         if(t.hasClass("x-grid-split")){
34478             this.cellIndex = this.view.getCellIndex(t.dom);
34479             this.split = t.dom;
34480             this.cm = this.grid.colModel;
34481             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34482                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34483             }
34484         }
34485     },
34486
34487     endDrag : function(e){
34488         this.view.headersDisabled = false;
34489         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34490         var diff = endX - this.startPos;
34491         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34492     },
34493
34494     autoOffset : function(){
34495         this.setDelta(0,0);
34496     }
34497 });/*
34498  * Based on:
34499  * Ext JS Library 1.1.1
34500  * Copyright(c) 2006-2007, Ext JS, LLC.
34501  *
34502  * Originally Released Under LGPL - original licence link has changed is not relivant.
34503  *
34504  * Fork - LGPL
34505  * <script type="text/javascript">
34506  */
34507  
34508 // private
34509 // This is a support class used internally by the Grid components
34510 Roo.grid.GridDragZone = function(grid, config){
34511     this.view = grid.getView();
34512     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34513     if(this.view.lockedBody){
34514         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34515         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34516     }
34517     this.scroll = false;
34518     this.grid = grid;
34519     this.ddel = document.createElement('div');
34520     this.ddel.className = 'x-grid-dd-wrap';
34521 };
34522
34523 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34524     ddGroup : "GridDD",
34525
34526     getDragData : function(e){
34527         var t = Roo.lib.Event.getTarget(e);
34528         var rowIndex = this.view.findRowIndex(t);
34529         var sm = this.grid.selModel;
34530             
34531         //Roo.log(rowIndex);
34532         
34533         if (sm.getSelectedCell) {
34534             // cell selection..
34535             if (!sm.getSelectedCell()) {
34536                 return false;
34537             }
34538             if (rowIndex != sm.getSelectedCell()[0]) {
34539                 return false;
34540             }
34541         
34542         }
34543         
34544         if(rowIndex !== false){
34545             
34546             // if editorgrid.. 
34547             
34548             
34549             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34550                
34551             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34552               //  
34553             //}
34554             if (e.hasModifier()){
34555                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34556             }
34557             
34558             Roo.log("getDragData");
34559             
34560             return {
34561                 grid: this.grid,
34562                 ddel: this.ddel,
34563                 rowIndex: rowIndex,
34564                 selections:sm.getSelections ? sm.getSelections() : (
34565                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34566                 )
34567             };
34568         }
34569         return false;
34570     },
34571
34572     onInitDrag : function(e){
34573         var data = this.dragData;
34574         this.ddel.innerHTML = this.grid.getDragDropText();
34575         this.proxy.update(this.ddel);
34576         // fire start drag?
34577     },
34578
34579     afterRepair : function(){
34580         this.dragging = false;
34581     },
34582
34583     getRepairXY : function(e, data){
34584         return false;
34585     },
34586
34587     onEndDrag : function(data, e){
34588         // fire end drag?
34589     },
34590
34591     onValidDrop : function(dd, e, id){
34592         // fire drag drop?
34593         this.hideProxy();
34594     },
34595
34596     beforeInvalidDrop : function(e, id){
34597
34598     }
34599 });/*
34600  * Based on:
34601  * Ext JS Library 1.1.1
34602  * Copyright(c) 2006-2007, Ext JS, LLC.
34603  *
34604  * Originally Released Under LGPL - original licence link has changed is not relivant.
34605  *
34606  * Fork - LGPL
34607  * <script type="text/javascript">
34608  */
34609  
34610
34611 /**
34612  * @class Roo.grid.ColumnModel
34613  * @extends Roo.util.Observable
34614  * This is the default implementation of a ColumnModel used by the Grid. It defines
34615  * the columns in the grid.
34616  * <br>Usage:<br>
34617  <pre><code>
34618  var colModel = new Roo.grid.ColumnModel([
34619         {header: "Ticker", width: 60, sortable: true, locked: true},
34620         {header: "Company Name", width: 150, sortable: true},
34621         {header: "Market Cap.", width: 100, sortable: true},
34622         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34623         {header: "Employees", width: 100, sortable: true, resizable: false}
34624  ]);
34625  </code></pre>
34626  * <p>
34627  
34628  * The config options listed for this class are options which may appear in each
34629  * individual column definition.
34630  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34631  * @constructor
34632  * @param {Object} config An Array of column config objects. See this class's
34633  * config objects for details.
34634 */
34635 Roo.grid.ColumnModel = function(config){
34636         /**
34637      * The config passed into the constructor
34638      */
34639     this.config = config;
34640     this.lookup = {};
34641
34642     // if no id, create one
34643     // if the column does not have a dataIndex mapping,
34644     // map it to the order it is in the config
34645     for(var i = 0, len = config.length; i < len; i++){
34646         var c = config[i];
34647         if(typeof c.dataIndex == "undefined"){
34648             c.dataIndex = i;
34649         }
34650         if(typeof c.renderer == "string"){
34651             c.renderer = Roo.util.Format[c.renderer];
34652         }
34653         if(typeof c.id == "undefined"){
34654             c.id = Roo.id();
34655         }
34656         if(c.editor && c.editor.xtype){
34657             c.editor  = Roo.factory(c.editor, Roo.grid);
34658         }
34659         if(c.editor && c.editor.isFormField){
34660             c.editor = new Roo.grid.GridEditor(c.editor);
34661         }
34662         this.lookup[c.id] = c;
34663     }
34664
34665     /**
34666      * The width of columns which have no width specified (defaults to 100)
34667      * @type Number
34668      */
34669     this.defaultWidth = 100;
34670
34671     /**
34672      * Default sortable of columns which have no sortable specified (defaults to false)
34673      * @type Boolean
34674      */
34675     this.defaultSortable = false;
34676
34677     this.addEvents({
34678         /**
34679              * @event widthchange
34680              * Fires when the width of a column changes.
34681              * @param {ColumnModel} this
34682              * @param {Number} columnIndex The column index
34683              * @param {Number} newWidth The new width
34684              */
34685             "widthchange": true,
34686         /**
34687              * @event headerchange
34688              * Fires when the text of a header changes.
34689              * @param {ColumnModel} this
34690              * @param {Number} columnIndex The column index
34691              * @param {Number} newText The new header text
34692              */
34693             "headerchange": true,
34694         /**
34695              * @event hiddenchange
34696              * Fires when a column is hidden or "unhidden".
34697              * @param {ColumnModel} this
34698              * @param {Number} columnIndex The column index
34699              * @param {Boolean} hidden true if hidden, false otherwise
34700              */
34701             "hiddenchange": true,
34702             /**
34703          * @event columnmoved
34704          * Fires when a column is moved.
34705          * @param {ColumnModel} this
34706          * @param {Number} oldIndex
34707          * @param {Number} newIndex
34708          */
34709         "columnmoved" : true,
34710         /**
34711          * @event columlockchange
34712          * Fires when a column's locked state is changed
34713          * @param {ColumnModel} this
34714          * @param {Number} colIndex
34715          * @param {Boolean} locked true if locked
34716          */
34717         "columnlockchange" : true
34718     });
34719     Roo.grid.ColumnModel.superclass.constructor.call(this);
34720 };
34721 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34722     /**
34723      * @cfg {String} header The header text to display in the Grid view.
34724      */
34725     /**
34726      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34727      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34728      * specified, the column's index is used as an index into the Record's data Array.
34729      */
34730     /**
34731      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34732      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34733      */
34734     /**
34735      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34736      * Defaults to the value of the {@link #defaultSortable} property.
34737      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34738      */
34739     /**
34740      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34741      */
34742     /**
34743      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34744      */
34745     /**
34746      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34747      */
34748     /**
34749      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34750      */
34751     /**
34752      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34753      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34754      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34755      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34756      */
34757        /**
34758      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34759      */
34760     /**
34761      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34762      */
34763     /**
34764      * @cfg {String} cursor (Optional)
34765      */
34766     /**
34767      * @cfg {String} tooltip (Optional)
34768      */
34769     /**
34770      * @cfg {Number} xs (Optional)
34771      */
34772     /**
34773      * @cfg {Number} sm (Optional)
34774      */
34775     /**
34776      * @cfg {Number} md (Optional)
34777      */
34778     /**
34779      * @cfg {Number} lg (Optional)
34780      */
34781     /**
34782      * Returns the id of the column at the specified index.
34783      * @param {Number} index The column index
34784      * @return {String} the id
34785      */
34786     getColumnId : function(index){
34787         return this.config[index].id;
34788     },
34789
34790     /**
34791      * Returns the column for a specified id.
34792      * @param {String} id The column id
34793      * @return {Object} the column
34794      */
34795     getColumnById : function(id){
34796         return this.lookup[id];
34797     },
34798
34799     
34800     /**
34801      * Returns the column for a specified dataIndex.
34802      * @param {String} dataIndex The column dataIndex
34803      * @return {Object|Boolean} the column or false if not found
34804      */
34805     getColumnByDataIndex: function(dataIndex){
34806         var index = this.findColumnIndex(dataIndex);
34807         return index > -1 ? this.config[index] : false;
34808     },
34809     
34810     /**
34811      * Returns the index for a specified column id.
34812      * @param {String} id The column id
34813      * @return {Number} the index, or -1 if not found
34814      */
34815     getIndexById : function(id){
34816         for(var i = 0, len = this.config.length; i < len; i++){
34817             if(this.config[i].id == id){
34818                 return i;
34819             }
34820         }
34821         return -1;
34822     },
34823     
34824     /**
34825      * Returns the index for a specified column dataIndex.
34826      * @param {String} dataIndex The column dataIndex
34827      * @return {Number} the index, or -1 if not found
34828      */
34829     
34830     findColumnIndex : function(dataIndex){
34831         for(var i = 0, len = this.config.length; i < len; i++){
34832             if(this.config[i].dataIndex == dataIndex){
34833                 return i;
34834             }
34835         }
34836         return -1;
34837     },
34838     
34839     
34840     moveColumn : function(oldIndex, newIndex){
34841         var c = this.config[oldIndex];
34842         this.config.splice(oldIndex, 1);
34843         this.config.splice(newIndex, 0, c);
34844         this.dataMap = null;
34845         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34846     },
34847
34848     isLocked : function(colIndex){
34849         return this.config[colIndex].locked === true;
34850     },
34851
34852     setLocked : function(colIndex, value, suppressEvent){
34853         if(this.isLocked(colIndex) == value){
34854             return;
34855         }
34856         this.config[colIndex].locked = value;
34857         if(!suppressEvent){
34858             this.fireEvent("columnlockchange", this, colIndex, value);
34859         }
34860     },
34861
34862     getTotalLockedWidth : function(){
34863         var totalWidth = 0;
34864         for(var i = 0; i < this.config.length; i++){
34865             if(this.isLocked(i) && !this.isHidden(i)){
34866                 this.totalWidth += this.getColumnWidth(i);
34867             }
34868         }
34869         return totalWidth;
34870     },
34871
34872     getLockedCount : function(){
34873         for(var i = 0, len = this.config.length; i < len; i++){
34874             if(!this.isLocked(i)){
34875                 return i;
34876             }
34877         }
34878         
34879         return this.config.length;
34880     },
34881
34882     /**
34883      * Returns the number of columns.
34884      * @return {Number}
34885      */
34886     getColumnCount : function(visibleOnly){
34887         if(visibleOnly === true){
34888             var c = 0;
34889             for(var i = 0, len = this.config.length; i < len; i++){
34890                 if(!this.isHidden(i)){
34891                     c++;
34892                 }
34893             }
34894             return c;
34895         }
34896         return this.config.length;
34897     },
34898
34899     /**
34900      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34901      * @param {Function} fn
34902      * @param {Object} scope (optional)
34903      * @return {Array} result
34904      */
34905     getColumnsBy : function(fn, scope){
34906         var r = [];
34907         for(var i = 0, len = this.config.length; i < len; i++){
34908             var c = this.config[i];
34909             if(fn.call(scope||this, c, i) === true){
34910                 r[r.length] = c;
34911             }
34912         }
34913         return r;
34914     },
34915
34916     /**
34917      * Returns true if the specified column is sortable.
34918      * @param {Number} col The column index
34919      * @return {Boolean}
34920      */
34921     isSortable : function(col){
34922         if(typeof this.config[col].sortable == "undefined"){
34923             return this.defaultSortable;
34924         }
34925         return this.config[col].sortable;
34926     },
34927
34928     /**
34929      * Returns the rendering (formatting) function defined for the column.
34930      * @param {Number} col The column index.
34931      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34932      */
34933     getRenderer : function(col){
34934         if(!this.config[col].renderer){
34935             return Roo.grid.ColumnModel.defaultRenderer;
34936         }
34937         return this.config[col].renderer;
34938     },
34939
34940     /**
34941      * Sets the rendering (formatting) function for a column.
34942      * @param {Number} col The column index
34943      * @param {Function} fn The function to use to process the cell's raw data
34944      * to return HTML markup for the grid view. The render function is called with
34945      * the following parameters:<ul>
34946      * <li>Data value.</li>
34947      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34948      * <li>css A CSS style string to apply to the table cell.</li>
34949      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34950      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34951      * <li>Row index</li>
34952      * <li>Column index</li>
34953      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34954      */
34955     setRenderer : function(col, fn){
34956         this.config[col].renderer = fn;
34957     },
34958
34959     /**
34960      * Returns the width for the specified column.
34961      * @param {Number} col The column index
34962      * @return {Number}
34963      */
34964     getColumnWidth : function(col){
34965         return this.config[col].width * 1 || this.defaultWidth;
34966     },
34967
34968     /**
34969      * Sets the width for a column.
34970      * @param {Number} col The column index
34971      * @param {Number} width The new width
34972      */
34973     setColumnWidth : function(col, width, suppressEvent){
34974         this.config[col].width = width;
34975         this.totalWidth = null;
34976         if(!suppressEvent){
34977              this.fireEvent("widthchange", this, col, width);
34978         }
34979     },
34980
34981     /**
34982      * Returns the total width of all columns.
34983      * @param {Boolean} includeHidden True to include hidden column widths
34984      * @return {Number}
34985      */
34986     getTotalWidth : function(includeHidden){
34987         if(!this.totalWidth){
34988             this.totalWidth = 0;
34989             for(var i = 0, len = this.config.length; i < len; i++){
34990                 if(includeHidden || !this.isHidden(i)){
34991                     this.totalWidth += this.getColumnWidth(i);
34992                 }
34993             }
34994         }
34995         return this.totalWidth;
34996     },
34997
34998     /**
34999      * Returns the header for the specified column.
35000      * @param {Number} col The column index
35001      * @return {String}
35002      */
35003     getColumnHeader : function(col){
35004         return this.config[col].header;
35005     },
35006
35007     /**
35008      * Sets the header for a column.
35009      * @param {Number} col The column index
35010      * @param {String} header The new header
35011      */
35012     setColumnHeader : function(col, header){
35013         this.config[col].header = header;
35014         this.fireEvent("headerchange", this, col, header);
35015     },
35016
35017     /**
35018      * Returns the tooltip for the specified column.
35019      * @param {Number} col The column index
35020      * @return {String}
35021      */
35022     getColumnTooltip : function(col){
35023             return this.config[col].tooltip;
35024     },
35025     /**
35026      * Sets the tooltip for a column.
35027      * @param {Number} col The column index
35028      * @param {String} tooltip The new tooltip
35029      */
35030     setColumnTooltip : function(col, tooltip){
35031             this.config[col].tooltip = tooltip;
35032     },
35033
35034     /**
35035      * Returns the dataIndex for the specified column.
35036      * @param {Number} col The column index
35037      * @return {Number}
35038      */
35039     getDataIndex : function(col){
35040         return this.config[col].dataIndex;
35041     },
35042
35043     /**
35044      * Sets the dataIndex for a column.
35045      * @param {Number} col The column index
35046      * @param {Number} dataIndex The new dataIndex
35047      */
35048     setDataIndex : function(col, dataIndex){
35049         this.config[col].dataIndex = dataIndex;
35050     },
35051
35052     
35053     
35054     /**
35055      * Returns true if the cell is editable.
35056      * @param {Number} colIndex The column index
35057      * @param {Number} rowIndex The row index - this is nto actually used..?
35058      * @return {Boolean}
35059      */
35060     isCellEditable : function(colIndex, rowIndex){
35061         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35062     },
35063
35064     /**
35065      * Returns the editor defined for the cell/column.
35066      * return false or null to disable editing.
35067      * @param {Number} colIndex The column index
35068      * @param {Number} rowIndex The row index
35069      * @return {Object}
35070      */
35071     getCellEditor : function(colIndex, rowIndex){
35072         return this.config[colIndex].editor;
35073     },
35074
35075     /**
35076      * Sets if a column is editable.
35077      * @param {Number} col The column index
35078      * @param {Boolean} editable True if the column is editable
35079      */
35080     setEditable : function(col, editable){
35081         this.config[col].editable = editable;
35082     },
35083
35084
35085     /**
35086      * Returns true if the column is hidden.
35087      * @param {Number} colIndex The column index
35088      * @return {Boolean}
35089      */
35090     isHidden : function(colIndex){
35091         return this.config[colIndex].hidden;
35092     },
35093
35094
35095     /**
35096      * Returns true if the column width cannot be changed
35097      */
35098     isFixed : function(colIndex){
35099         return this.config[colIndex].fixed;
35100     },
35101
35102     /**
35103      * Returns true if the column can be resized
35104      * @return {Boolean}
35105      */
35106     isResizable : function(colIndex){
35107         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35108     },
35109     /**
35110      * Sets if a column is hidden.
35111      * @param {Number} colIndex The column index
35112      * @param {Boolean} hidden True if the column is hidden
35113      */
35114     setHidden : function(colIndex, hidden){
35115         this.config[colIndex].hidden = hidden;
35116         this.totalWidth = null;
35117         this.fireEvent("hiddenchange", this, colIndex, hidden);
35118     },
35119
35120     /**
35121      * Sets the editor for a column.
35122      * @param {Number} col The column index
35123      * @param {Object} editor The editor object
35124      */
35125     setEditor : function(col, editor){
35126         this.config[col].editor = editor;
35127     }
35128 });
35129
35130 Roo.grid.ColumnModel.defaultRenderer = function(value)
35131 {
35132     if(typeof value == "object") {
35133         return value;
35134     }
35135         if(typeof value == "string" && value.length < 1){
35136             return "&#160;";
35137         }
35138     
35139         return String.format("{0}", value);
35140 };
35141
35142 // Alias for backwards compatibility
35143 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35144 /*
35145  * Based on:
35146  * Ext JS Library 1.1.1
35147  * Copyright(c) 2006-2007, Ext JS, LLC.
35148  *
35149  * Originally Released Under LGPL - original licence link has changed is not relivant.
35150  *
35151  * Fork - LGPL
35152  * <script type="text/javascript">
35153  */
35154
35155 /**
35156  * @class Roo.grid.AbstractSelectionModel
35157  * @extends Roo.util.Observable
35158  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35159  * implemented by descendant classes.  This class should not be directly instantiated.
35160  * @constructor
35161  */
35162 Roo.grid.AbstractSelectionModel = function(){
35163     this.locked = false;
35164     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35165 };
35166
35167 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35168     /** @ignore Called by the grid automatically. Do not call directly. */
35169     init : function(grid){
35170         this.grid = grid;
35171         this.initEvents();
35172     },
35173
35174     /**
35175      * Locks the selections.
35176      */
35177     lock : function(){
35178         this.locked = true;
35179     },
35180
35181     /**
35182      * Unlocks the selections.
35183      */
35184     unlock : function(){
35185         this.locked = false;
35186     },
35187
35188     /**
35189      * Returns true if the selections are locked.
35190      * @return {Boolean}
35191      */
35192     isLocked : function(){
35193         return this.locked;
35194     }
35195 });/*
35196  * Based on:
35197  * Ext JS Library 1.1.1
35198  * Copyright(c) 2006-2007, Ext JS, LLC.
35199  *
35200  * Originally Released Under LGPL - original licence link has changed is not relivant.
35201  *
35202  * Fork - LGPL
35203  * <script type="text/javascript">
35204  */
35205 /**
35206  * @extends Roo.grid.AbstractSelectionModel
35207  * @class Roo.grid.RowSelectionModel
35208  * The default SelectionModel used by {@link Roo.grid.Grid}.
35209  * It supports multiple selections and keyboard selection/navigation. 
35210  * @constructor
35211  * @param {Object} config
35212  */
35213 Roo.grid.RowSelectionModel = function(config){
35214     Roo.apply(this, config);
35215     this.selections = new Roo.util.MixedCollection(false, function(o){
35216         return o.id;
35217     });
35218
35219     this.last = false;
35220     this.lastActive = false;
35221
35222     this.addEvents({
35223         /**
35224              * @event selectionchange
35225              * Fires when the selection changes
35226              * @param {SelectionModel} this
35227              */
35228             "selectionchange" : true,
35229         /**
35230              * @event afterselectionchange
35231              * Fires after the selection changes (eg. by key press or clicking)
35232              * @param {SelectionModel} this
35233              */
35234             "afterselectionchange" : true,
35235         /**
35236              * @event beforerowselect
35237              * Fires when a row is selected being selected, return false to cancel.
35238              * @param {SelectionModel} this
35239              * @param {Number} rowIndex The selected index
35240              * @param {Boolean} keepExisting False if other selections will be cleared
35241              */
35242             "beforerowselect" : true,
35243         /**
35244              * @event rowselect
35245              * Fires when a row is selected.
35246              * @param {SelectionModel} this
35247              * @param {Number} rowIndex The selected index
35248              * @param {Roo.data.Record} r The record
35249              */
35250             "rowselect" : true,
35251         /**
35252              * @event rowdeselect
35253              * Fires when a row is deselected.
35254              * @param {SelectionModel} this
35255              * @param {Number} rowIndex The selected index
35256              */
35257         "rowdeselect" : true
35258     });
35259     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35260     this.locked = false;
35261 };
35262
35263 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35264     /**
35265      * @cfg {Boolean} singleSelect
35266      * True to allow selection of only one row at a time (defaults to false)
35267      */
35268     singleSelect : false,
35269
35270     // private
35271     initEvents : function(){
35272
35273         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35274             this.grid.on("mousedown", this.handleMouseDown, this);
35275         }else{ // allow click to work like normal
35276             this.grid.on("rowclick", this.handleDragableRowClick, this);
35277         }
35278
35279         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35280             "up" : function(e){
35281                 if(!e.shiftKey){
35282                     this.selectPrevious(e.shiftKey);
35283                 }else if(this.last !== false && this.lastActive !== false){
35284                     var last = this.last;
35285                     this.selectRange(this.last,  this.lastActive-1);
35286                     this.grid.getView().focusRow(this.lastActive);
35287                     if(last !== false){
35288                         this.last = last;
35289                     }
35290                 }else{
35291                     this.selectFirstRow();
35292                 }
35293                 this.fireEvent("afterselectionchange", this);
35294             },
35295             "down" : function(e){
35296                 if(!e.shiftKey){
35297                     this.selectNext(e.shiftKey);
35298                 }else if(this.last !== false && this.lastActive !== false){
35299                     var last = this.last;
35300                     this.selectRange(this.last,  this.lastActive+1);
35301                     this.grid.getView().focusRow(this.lastActive);
35302                     if(last !== false){
35303                         this.last = last;
35304                     }
35305                 }else{
35306                     this.selectFirstRow();
35307                 }
35308                 this.fireEvent("afterselectionchange", this);
35309             },
35310             scope: this
35311         });
35312
35313         var view = this.grid.view;
35314         view.on("refresh", this.onRefresh, this);
35315         view.on("rowupdated", this.onRowUpdated, this);
35316         view.on("rowremoved", this.onRemove, this);
35317     },
35318
35319     // private
35320     onRefresh : function(){
35321         var ds = this.grid.dataSource, i, v = this.grid.view;
35322         var s = this.selections;
35323         s.each(function(r){
35324             if((i = ds.indexOfId(r.id)) != -1){
35325                 v.onRowSelect(i);
35326                 s.add(ds.getAt(i)); // updating the selection relate data
35327             }else{
35328                 s.remove(r);
35329             }
35330         });
35331     },
35332
35333     // private
35334     onRemove : function(v, index, r){
35335         this.selections.remove(r);
35336     },
35337
35338     // private
35339     onRowUpdated : function(v, index, r){
35340         if(this.isSelected(r)){
35341             v.onRowSelect(index);
35342         }
35343     },
35344
35345     /**
35346      * Select records.
35347      * @param {Array} records The records to select
35348      * @param {Boolean} keepExisting (optional) True to keep existing selections
35349      */
35350     selectRecords : function(records, keepExisting){
35351         if(!keepExisting){
35352             this.clearSelections();
35353         }
35354         var ds = this.grid.dataSource;
35355         for(var i = 0, len = records.length; i < len; i++){
35356             this.selectRow(ds.indexOf(records[i]), true);
35357         }
35358     },
35359
35360     /**
35361      * Gets the number of selected rows.
35362      * @return {Number}
35363      */
35364     getCount : function(){
35365         return this.selections.length;
35366     },
35367
35368     /**
35369      * Selects the first row in the grid.
35370      */
35371     selectFirstRow : function(){
35372         this.selectRow(0);
35373     },
35374
35375     /**
35376      * Select the last row.
35377      * @param {Boolean} keepExisting (optional) True to keep existing selections
35378      */
35379     selectLastRow : function(keepExisting){
35380         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35381     },
35382
35383     /**
35384      * Selects the row immediately following the last selected row.
35385      * @param {Boolean} keepExisting (optional) True to keep existing selections
35386      */
35387     selectNext : function(keepExisting){
35388         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35389             this.selectRow(this.last+1, keepExisting);
35390             this.grid.getView().focusRow(this.last);
35391         }
35392     },
35393
35394     /**
35395      * Selects the row that precedes the last selected row.
35396      * @param {Boolean} keepExisting (optional) True to keep existing selections
35397      */
35398     selectPrevious : function(keepExisting){
35399         if(this.last){
35400             this.selectRow(this.last-1, keepExisting);
35401             this.grid.getView().focusRow(this.last);
35402         }
35403     },
35404
35405     /**
35406      * Returns the selected records
35407      * @return {Array} Array of selected records
35408      */
35409     getSelections : function(){
35410         return [].concat(this.selections.items);
35411     },
35412
35413     /**
35414      * Returns the first selected record.
35415      * @return {Record}
35416      */
35417     getSelected : function(){
35418         return this.selections.itemAt(0);
35419     },
35420
35421
35422     /**
35423      * Clears all selections.
35424      */
35425     clearSelections : function(fast){
35426         if(this.locked) {
35427             return;
35428         }
35429         if(fast !== true){
35430             var ds = this.grid.dataSource;
35431             var s = this.selections;
35432             s.each(function(r){
35433                 this.deselectRow(ds.indexOfId(r.id));
35434             }, this);
35435             s.clear();
35436         }else{
35437             this.selections.clear();
35438         }
35439         this.last = false;
35440     },
35441
35442
35443     /**
35444      * Selects all rows.
35445      */
35446     selectAll : function(){
35447         if(this.locked) {
35448             return;
35449         }
35450         this.selections.clear();
35451         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35452             this.selectRow(i, true);
35453         }
35454     },
35455
35456     /**
35457      * Returns True if there is a selection.
35458      * @return {Boolean}
35459      */
35460     hasSelection : function(){
35461         return this.selections.length > 0;
35462     },
35463
35464     /**
35465      * Returns True if the specified row is selected.
35466      * @param {Number/Record} record The record or index of the record to check
35467      * @return {Boolean}
35468      */
35469     isSelected : function(index){
35470         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35471         return (r && this.selections.key(r.id) ? true : false);
35472     },
35473
35474     /**
35475      * Returns True if the specified record id is selected.
35476      * @param {String} id The id of record to check
35477      * @return {Boolean}
35478      */
35479     isIdSelected : function(id){
35480         return (this.selections.key(id) ? true : false);
35481     },
35482
35483     // private
35484     handleMouseDown : function(e, t){
35485         var view = this.grid.getView(), rowIndex;
35486         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35487             return;
35488         };
35489         if(e.shiftKey && this.last !== false){
35490             var last = this.last;
35491             this.selectRange(last, rowIndex, e.ctrlKey);
35492             this.last = last; // reset the last
35493             view.focusRow(rowIndex);
35494         }else{
35495             var isSelected = this.isSelected(rowIndex);
35496             if(e.button !== 0 && isSelected){
35497                 view.focusRow(rowIndex);
35498             }else if(e.ctrlKey && isSelected){
35499                 this.deselectRow(rowIndex);
35500             }else if(!isSelected){
35501                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35502                 view.focusRow(rowIndex);
35503             }
35504         }
35505         this.fireEvent("afterselectionchange", this);
35506     },
35507     // private
35508     handleDragableRowClick :  function(grid, rowIndex, e) 
35509     {
35510         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35511             this.selectRow(rowIndex, false);
35512             grid.view.focusRow(rowIndex);
35513              this.fireEvent("afterselectionchange", this);
35514         }
35515     },
35516     
35517     /**
35518      * Selects multiple rows.
35519      * @param {Array} rows Array of the indexes of the row to select
35520      * @param {Boolean} keepExisting (optional) True to keep existing selections
35521      */
35522     selectRows : function(rows, keepExisting){
35523         if(!keepExisting){
35524             this.clearSelections();
35525         }
35526         for(var i = 0, len = rows.length; i < len; i++){
35527             this.selectRow(rows[i], true);
35528         }
35529     },
35530
35531     /**
35532      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35533      * @param {Number} startRow The index of the first row in the range
35534      * @param {Number} endRow The index of the last row in the range
35535      * @param {Boolean} keepExisting (optional) True to retain existing selections
35536      */
35537     selectRange : function(startRow, endRow, keepExisting){
35538         if(this.locked) {
35539             return;
35540         }
35541         if(!keepExisting){
35542             this.clearSelections();
35543         }
35544         if(startRow <= endRow){
35545             for(var i = startRow; i <= endRow; i++){
35546                 this.selectRow(i, true);
35547             }
35548         }else{
35549             for(var i = startRow; i >= endRow; i--){
35550                 this.selectRow(i, true);
35551             }
35552         }
35553     },
35554
35555     /**
35556      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35557      * @param {Number} startRow The index of the first row in the range
35558      * @param {Number} endRow The index of the last row in the range
35559      */
35560     deselectRange : function(startRow, endRow, preventViewNotify){
35561         if(this.locked) {
35562             return;
35563         }
35564         for(var i = startRow; i <= endRow; i++){
35565             this.deselectRow(i, preventViewNotify);
35566         }
35567     },
35568
35569     /**
35570      * Selects a row.
35571      * @param {Number} row The index of the row to select
35572      * @param {Boolean} keepExisting (optional) True to keep existing selections
35573      */
35574     selectRow : function(index, keepExisting, preventViewNotify){
35575         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35576             return;
35577         }
35578         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35579             if(!keepExisting || this.singleSelect){
35580                 this.clearSelections();
35581             }
35582             var r = this.grid.dataSource.getAt(index);
35583             this.selections.add(r);
35584             this.last = this.lastActive = index;
35585             if(!preventViewNotify){
35586                 this.grid.getView().onRowSelect(index);
35587             }
35588             this.fireEvent("rowselect", this, index, r);
35589             this.fireEvent("selectionchange", this);
35590         }
35591     },
35592
35593     /**
35594      * Deselects a row.
35595      * @param {Number} row The index of the row to deselect
35596      */
35597     deselectRow : function(index, preventViewNotify){
35598         if(this.locked) {
35599             return;
35600         }
35601         if(this.last == index){
35602             this.last = false;
35603         }
35604         if(this.lastActive == index){
35605             this.lastActive = false;
35606         }
35607         var r = this.grid.dataSource.getAt(index);
35608         this.selections.remove(r);
35609         if(!preventViewNotify){
35610             this.grid.getView().onRowDeselect(index);
35611         }
35612         this.fireEvent("rowdeselect", this, index);
35613         this.fireEvent("selectionchange", this);
35614     },
35615
35616     // private
35617     restoreLast : function(){
35618         if(this._last){
35619             this.last = this._last;
35620         }
35621     },
35622
35623     // private
35624     acceptsNav : function(row, col, cm){
35625         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35626     },
35627
35628     // private
35629     onEditorKey : function(field, e){
35630         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35631         if(k == e.TAB){
35632             e.stopEvent();
35633             ed.completeEdit();
35634             if(e.shiftKey){
35635                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35636             }else{
35637                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35638             }
35639         }else if(k == e.ENTER && !e.ctrlKey){
35640             e.stopEvent();
35641             ed.completeEdit();
35642             if(e.shiftKey){
35643                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35644             }else{
35645                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35646             }
35647         }else if(k == e.ESC){
35648             ed.cancelEdit();
35649         }
35650         if(newCell){
35651             g.startEditing(newCell[0], newCell[1]);
35652         }
35653     }
35654 });/*
35655  * Based on:
35656  * Ext JS Library 1.1.1
35657  * Copyright(c) 2006-2007, Ext JS, LLC.
35658  *
35659  * Originally Released Under LGPL - original licence link has changed is not relivant.
35660  *
35661  * Fork - LGPL
35662  * <script type="text/javascript">
35663  */
35664 /**
35665  * @class Roo.grid.CellSelectionModel
35666  * @extends Roo.grid.AbstractSelectionModel
35667  * This class provides the basic implementation for cell selection in a grid.
35668  * @constructor
35669  * @param {Object} config The object containing the configuration of this model.
35670  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35671  */
35672 Roo.grid.CellSelectionModel = function(config){
35673     Roo.apply(this, config);
35674
35675     this.selection = null;
35676
35677     this.addEvents({
35678         /**
35679              * @event beforerowselect
35680              * Fires before a cell is selected.
35681              * @param {SelectionModel} this
35682              * @param {Number} rowIndex The selected row index
35683              * @param {Number} colIndex The selected cell index
35684              */
35685             "beforecellselect" : true,
35686         /**
35687              * @event cellselect
35688              * Fires when a cell is selected.
35689              * @param {SelectionModel} this
35690              * @param {Number} rowIndex The selected row index
35691              * @param {Number} colIndex The selected cell index
35692              */
35693             "cellselect" : true,
35694         /**
35695              * @event selectionchange
35696              * Fires when the active selection changes.
35697              * @param {SelectionModel} this
35698              * @param {Object} selection null for no selection or an object (o) with two properties
35699                 <ul>
35700                 <li>o.record: the record object for the row the selection is in</li>
35701                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35702                 </ul>
35703              */
35704             "selectionchange" : true,
35705         /**
35706              * @event tabend
35707              * Fires when the tab (or enter) was pressed on the last editable cell
35708              * You can use this to trigger add new row.
35709              * @param {SelectionModel} this
35710              */
35711             "tabend" : true,
35712          /**
35713              * @event beforeeditnext
35714              * Fires before the next editable sell is made active
35715              * You can use this to skip to another cell or fire the tabend
35716              *    if you set cell to false
35717              * @param {Object} eventdata object : { cell : [ row, col ] } 
35718              */
35719             "beforeeditnext" : true
35720     });
35721     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35722 };
35723
35724 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35725     
35726     enter_is_tab: false,
35727
35728     /** @ignore */
35729     initEvents : function(){
35730         this.grid.on("mousedown", this.handleMouseDown, this);
35731         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35732         var view = this.grid.view;
35733         view.on("refresh", this.onViewChange, this);
35734         view.on("rowupdated", this.onRowUpdated, this);
35735         view.on("beforerowremoved", this.clearSelections, this);
35736         view.on("beforerowsinserted", this.clearSelections, this);
35737         if(this.grid.isEditor){
35738             this.grid.on("beforeedit", this.beforeEdit,  this);
35739         }
35740     },
35741
35742         //private
35743     beforeEdit : function(e){
35744         this.select(e.row, e.column, false, true, e.record);
35745     },
35746
35747         //private
35748     onRowUpdated : function(v, index, r){
35749         if(this.selection && this.selection.record == r){
35750             v.onCellSelect(index, this.selection.cell[1]);
35751         }
35752     },
35753
35754         //private
35755     onViewChange : function(){
35756         this.clearSelections(true);
35757     },
35758
35759         /**
35760          * Returns the currently selected cell,.
35761          * @return {Array} The selected cell (row, column) or null if none selected.
35762          */
35763     getSelectedCell : function(){
35764         return this.selection ? this.selection.cell : null;
35765     },
35766
35767     /**
35768      * Clears all selections.
35769      * @param {Boolean} true to prevent the gridview from being notified about the change.
35770      */
35771     clearSelections : function(preventNotify){
35772         var s = this.selection;
35773         if(s){
35774             if(preventNotify !== true){
35775                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35776             }
35777             this.selection = null;
35778             this.fireEvent("selectionchange", this, null);
35779         }
35780     },
35781
35782     /**
35783      * Returns true if there is a selection.
35784      * @return {Boolean}
35785      */
35786     hasSelection : function(){
35787         return this.selection ? true : false;
35788     },
35789
35790     /** @ignore */
35791     handleMouseDown : function(e, t){
35792         var v = this.grid.getView();
35793         if(this.isLocked()){
35794             return;
35795         };
35796         var row = v.findRowIndex(t);
35797         var cell = v.findCellIndex(t);
35798         if(row !== false && cell !== false){
35799             this.select(row, cell);
35800         }
35801     },
35802
35803     /**
35804      * Selects a cell.
35805      * @param {Number} rowIndex
35806      * @param {Number} collIndex
35807      */
35808     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35809         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35810             this.clearSelections();
35811             r = r || this.grid.dataSource.getAt(rowIndex);
35812             this.selection = {
35813                 record : r,
35814                 cell : [rowIndex, colIndex]
35815             };
35816             if(!preventViewNotify){
35817                 var v = this.grid.getView();
35818                 v.onCellSelect(rowIndex, colIndex);
35819                 if(preventFocus !== true){
35820                     v.focusCell(rowIndex, colIndex);
35821                 }
35822             }
35823             this.fireEvent("cellselect", this, rowIndex, colIndex);
35824             this.fireEvent("selectionchange", this, this.selection);
35825         }
35826     },
35827
35828         //private
35829     isSelectable : function(rowIndex, colIndex, cm){
35830         return !cm.isHidden(colIndex);
35831     },
35832
35833     /** @ignore */
35834     handleKeyDown : function(e){
35835         //Roo.log('Cell Sel Model handleKeyDown');
35836         if(!e.isNavKeyPress()){
35837             return;
35838         }
35839         var g = this.grid, s = this.selection;
35840         if(!s){
35841             e.stopEvent();
35842             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35843             if(cell){
35844                 this.select(cell[0], cell[1]);
35845             }
35846             return;
35847         }
35848         var sm = this;
35849         var walk = function(row, col, step){
35850             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35851         };
35852         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35853         var newCell;
35854
35855       
35856
35857         switch(k){
35858             case e.TAB:
35859                 // handled by onEditorKey
35860                 if (g.isEditor && g.editing) {
35861                     return;
35862                 }
35863                 if(e.shiftKey) {
35864                     newCell = walk(r, c-1, -1);
35865                 } else {
35866                     newCell = walk(r, c+1, 1);
35867                 }
35868                 break;
35869             
35870             case e.DOWN:
35871                newCell = walk(r+1, c, 1);
35872                 break;
35873             
35874             case e.UP:
35875                 newCell = walk(r-1, c, -1);
35876                 break;
35877             
35878             case e.RIGHT:
35879                 newCell = walk(r, c+1, 1);
35880                 break;
35881             
35882             case e.LEFT:
35883                 newCell = walk(r, c-1, -1);
35884                 break;
35885             
35886             case e.ENTER:
35887                 
35888                 if(g.isEditor && !g.editing){
35889                    g.startEditing(r, c);
35890                    e.stopEvent();
35891                    return;
35892                 }
35893                 
35894                 
35895              break;
35896         };
35897         if(newCell){
35898             this.select(newCell[0], newCell[1]);
35899             e.stopEvent();
35900             
35901         }
35902     },
35903
35904     acceptsNav : function(row, col, cm){
35905         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35906     },
35907     /**
35908      * Selects a cell.
35909      * @param {Number} field (not used) - as it's normally used as a listener
35910      * @param {Number} e - event - fake it by using
35911      *
35912      * var e = Roo.EventObjectImpl.prototype;
35913      * e.keyCode = e.TAB
35914      *
35915      * 
35916      */
35917     onEditorKey : function(field, e){
35918         
35919         var k = e.getKey(),
35920             newCell,
35921             g = this.grid,
35922             ed = g.activeEditor,
35923             forward = false;
35924         ///Roo.log('onEditorKey' + k);
35925         
35926         
35927         if (this.enter_is_tab && k == e.ENTER) {
35928             k = e.TAB;
35929         }
35930         
35931         if(k == e.TAB){
35932             if(e.shiftKey){
35933                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35934             }else{
35935                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35936                 forward = true;
35937             }
35938             
35939             e.stopEvent();
35940             
35941         } else if(k == e.ENTER &&  !e.ctrlKey){
35942             ed.completeEdit();
35943             e.stopEvent();
35944             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35945         
35946                 } else if(k == e.ESC){
35947             ed.cancelEdit();
35948         }
35949                 
35950         if (newCell) {
35951             var ecall = { cell : newCell, forward : forward };
35952             this.fireEvent('beforeeditnext', ecall );
35953             newCell = ecall.cell;
35954                         forward = ecall.forward;
35955         }
35956                 
35957         if(newCell){
35958             //Roo.log('next cell after edit');
35959             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35960         } else if (forward) {
35961             // tabbed past last
35962             this.fireEvent.defer(100, this, ['tabend',this]);
35963         }
35964     }
35965 });/*
35966  * Based on:
35967  * Ext JS Library 1.1.1
35968  * Copyright(c) 2006-2007, Ext JS, LLC.
35969  *
35970  * Originally Released Under LGPL - original licence link has changed is not relivant.
35971  *
35972  * Fork - LGPL
35973  * <script type="text/javascript">
35974  */
35975  
35976 /**
35977  * @class Roo.grid.EditorGrid
35978  * @extends Roo.grid.Grid
35979  * Class for creating and editable grid.
35980  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35981  * The container MUST have some type of size defined for the grid to fill. The container will be 
35982  * automatically set to position relative if it isn't already.
35983  * @param {Object} dataSource The data model to bind to
35984  * @param {Object} colModel The column model with info about this grid's columns
35985  */
35986 Roo.grid.EditorGrid = function(container, config){
35987     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35988     this.getGridEl().addClass("xedit-grid");
35989
35990     if(!this.selModel){
35991         this.selModel = new Roo.grid.CellSelectionModel();
35992     }
35993
35994     this.activeEditor = null;
35995
35996         this.addEvents({
35997             /**
35998              * @event beforeedit
35999              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36000              * <ul style="padding:5px;padding-left:16px;">
36001              * <li>grid - This grid</li>
36002              * <li>record - The record being edited</li>
36003              * <li>field - The field name being edited</li>
36004              * <li>value - The value for the field being edited.</li>
36005              * <li>row - The grid row index</li>
36006              * <li>column - The grid column index</li>
36007              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36008              * </ul>
36009              * @param {Object} e An edit event (see above for description)
36010              */
36011             "beforeedit" : true,
36012             /**
36013              * @event afteredit
36014              * Fires after a cell is edited. <br />
36015              * <ul style="padding:5px;padding-left:16px;">
36016              * <li>grid - This grid</li>
36017              * <li>record - The record being edited</li>
36018              * <li>field - The field name being edited</li>
36019              * <li>value - The value being set</li>
36020              * <li>originalValue - The original value for the field, before the edit.</li>
36021              * <li>row - The grid row index</li>
36022              * <li>column - The grid column index</li>
36023              * </ul>
36024              * @param {Object} e An edit event (see above for description)
36025              */
36026             "afteredit" : true,
36027             /**
36028              * @event validateedit
36029              * Fires after a cell is edited, but before the value is set in the record. 
36030          * You can use this to modify the value being set in the field, Return false
36031              * to cancel the change. The edit event object has the following properties <br />
36032              * <ul style="padding:5px;padding-left:16px;">
36033          * <li>editor - This editor</li>
36034              * <li>grid - This grid</li>
36035              * <li>record - The record being edited</li>
36036              * <li>field - The field name being edited</li>
36037              * <li>value - The value being set</li>
36038              * <li>originalValue - The original value for the field, before the edit.</li>
36039              * <li>row - The grid row index</li>
36040              * <li>column - The grid column index</li>
36041              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36042              * </ul>
36043              * @param {Object} e An edit event (see above for description)
36044              */
36045             "validateedit" : true
36046         });
36047     this.on("bodyscroll", this.stopEditing,  this);
36048     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36049 };
36050
36051 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36052     /**
36053      * @cfg {Number} clicksToEdit
36054      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36055      */
36056     clicksToEdit: 2,
36057
36058     // private
36059     isEditor : true,
36060     // private
36061     trackMouseOver: false, // causes very odd FF errors
36062
36063     onCellDblClick : function(g, row, col){
36064         this.startEditing(row, col);
36065     },
36066
36067     onEditComplete : function(ed, value, startValue){
36068         this.editing = false;
36069         this.activeEditor = null;
36070         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36071         var r = ed.record;
36072         var field = this.colModel.getDataIndex(ed.col);
36073         var e = {
36074             grid: this,
36075             record: r,
36076             field: field,
36077             originalValue: startValue,
36078             value: value,
36079             row: ed.row,
36080             column: ed.col,
36081             cancel:false,
36082             editor: ed
36083         };
36084         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36085         cell.show();
36086           
36087         if(String(value) !== String(startValue)){
36088             
36089             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36090                 r.set(field, e.value);
36091                 // if we are dealing with a combo box..
36092                 // then we also set the 'name' colum to be the displayField
36093                 if (ed.field.displayField && ed.field.name) {
36094                     r.set(ed.field.name, ed.field.el.dom.value);
36095                 }
36096                 
36097                 delete e.cancel; //?? why!!!
36098                 this.fireEvent("afteredit", e);
36099             }
36100         } else {
36101             this.fireEvent("afteredit", e); // always fire it!
36102         }
36103         this.view.focusCell(ed.row, ed.col);
36104     },
36105
36106     /**
36107      * Starts editing the specified for the specified row/column
36108      * @param {Number} rowIndex
36109      * @param {Number} colIndex
36110      */
36111     startEditing : function(row, col){
36112         this.stopEditing();
36113         if(this.colModel.isCellEditable(col, row)){
36114             this.view.ensureVisible(row, col, true);
36115           
36116             var r = this.dataSource.getAt(row);
36117             var field = this.colModel.getDataIndex(col);
36118             var cell = Roo.get(this.view.getCell(row,col));
36119             var e = {
36120                 grid: this,
36121                 record: r,
36122                 field: field,
36123                 value: r.data[field],
36124                 row: row,
36125                 column: col,
36126                 cancel:false 
36127             };
36128             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36129                 this.editing = true;
36130                 var ed = this.colModel.getCellEditor(col, row);
36131                 
36132                 if (!ed) {
36133                     return;
36134                 }
36135                 if(!ed.rendered){
36136                     ed.render(ed.parentEl || document.body);
36137                 }
36138                 ed.field.reset();
36139                
36140                 cell.hide();
36141                 
36142                 (function(){ // complex but required for focus issues in safari, ie and opera
36143                     ed.row = row;
36144                     ed.col = col;
36145                     ed.record = r;
36146                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36147                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36148                     this.activeEditor = ed;
36149                     var v = r.data[field];
36150                     ed.startEdit(this.view.getCell(row, col), v);
36151                     // combo's with 'displayField and name set
36152                     if (ed.field.displayField && ed.field.name) {
36153                         ed.field.el.dom.value = r.data[ed.field.name];
36154                     }
36155                     
36156                     
36157                 }).defer(50, this);
36158             }
36159         }
36160     },
36161         
36162     /**
36163      * Stops any active editing
36164      */
36165     stopEditing : function(){
36166         if(this.activeEditor){
36167             this.activeEditor.completeEdit();
36168         }
36169         this.activeEditor = null;
36170     },
36171         
36172          /**
36173      * Called to get grid's drag proxy text, by default returns this.ddText.
36174      * @return {String}
36175      */
36176     getDragDropText : function(){
36177         var count = this.selModel.getSelectedCell() ? 1 : 0;
36178         return String.format(this.ddText, count, count == 1 ? '' : 's');
36179     }
36180         
36181 });/*
36182  * Based on:
36183  * Ext JS Library 1.1.1
36184  * Copyright(c) 2006-2007, Ext JS, LLC.
36185  *
36186  * Originally Released Under LGPL - original licence link has changed is not relivant.
36187  *
36188  * Fork - LGPL
36189  * <script type="text/javascript">
36190  */
36191
36192 // private - not really -- you end up using it !
36193 // This is a support class used internally by the Grid components
36194
36195 /**
36196  * @class Roo.grid.GridEditor
36197  * @extends Roo.Editor
36198  * Class for creating and editable grid elements.
36199  * @param {Object} config any settings (must include field)
36200  */
36201 Roo.grid.GridEditor = function(field, config){
36202     if (!config && field.field) {
36203         config = field;
36204         field = Roo.factory(config.field, Roo.form);
36205     }
36206     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36207     field.monitorTab = false;
36208 };
36209
36210 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36211     
36212     /**
36213      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36214      */
36215     
36216     alignment: "tl-tl",
36217     autoSize: "width",
36218     hideEl : false,
36219     cls: "x-small-editor x-grid-editor",
36220     shim:false,
36221     shadow:"frame"
36222 });/*
36223  * Based on:
36224  * Ext JS Library 1.1.1
36225  * Copyright(c) 2006-2007, Ext JS, LLC.
36226  *
36227  * Originally Released Under LGPL - original licence link has changed is not relivant.
36228  *
36229  * Fork - LGPL
36230  * <script type="text/javascript">
36231  */
36232   
36233
36234   
36235 Roo.grid.PropertyRecord = Roo.data.Record.create([
36236     {name:'name',type:'string'},  'value'
36237 ]);
36238
36239
36240 Roo.grid.PropertyStore = function(grid, source){
36241     this.grid = grid;
36242     this.store = new Roo.data.Store({
36243         recordType : Roo.grid.PropertyRecord
36244     });
36245     this.store.on('update', this.onUpdate,  this);
36246     if(source){
36247         this.setSource(source);
36248     }
36249     Roo.grid.PropertyStore.superclass.constructor.call(this);
36250 };
36251
36252
36253
36254 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36255     setSource : function(o){
36256         this.source = o;
36257         this.store.removeAll();
36258         var data = [];
36259         for(var k in o){
36260             if(this.isEditableValue(o[k])){
36261                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36262             }
36263         }
36264         this.store.loadRecords({records: data}, {}, true);
36265     },
36266
36267     onUpdate : function(ds, record, type){
36268         if(type == Roo.data.Record.EDIT){
36269             var v = record.data['value'];
36270             var oldValue = record.modified['value'];
36271             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36272                 this.source[record.id] = v;
36273                 record.commit();
36274                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36275             }else{
36276                 record.reject();
36277             }
36278         }
36279     },
36280
36281     getProperty : function(row){
36282        return this.store.getAt(row);
36283     },
36284
36285     isEditableValue: function(val){
36286         if(val && val instanceof Date){
36287             return true;
36288         }else if(typeof val == 'object' || typeof val == 'function'){
36289             return false;
36290         }
36291         return true;
36292     },
36293
36294     setValue : function(prop, value){
36295         this.source[prop] = value;
36296         this.store.getById(prop).set('value', value);
36297     },
36298
36299     getSource : function(){
36300         return this.source;
36301     }
36302 });
36303
36304 Roo.grid.PropertyColumnModel = function(grid, store){
36305     this.grid = grid;
36306     var g = Roo.grid;
36307     g.PropertyColumnModel.superclass.constructor.call(this, [
36308         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36309         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36310     ]);
36311     this.store = store;
36312     this.bselect = Roo.DomHelper.append(document.body, {
36313         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36314             {tag: 'option', value: 'true', html: 'true'},
36315             {tag: 'option', value: 'false', html: 'false'}
36316         ]
36317     });
36318     Roo.id(this.bselect);
36319     var f = Roo.form;
36320     this.editors = {
36321         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36322         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36323         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36324         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36325         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36326     };
36327     this.renderCellDelegate = this.renderCell.createDelegate(this);
36328     this.renderPropDelegate = this.renderProp.createDelegate(this);
36329 };
36330
36331 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36332     
36333     
36334     nameText : 'Name',
36335     valueText : 'Value',
36336     
36337     dateFormat : 'm/j/Y',
36338     
36339     
36340     renderDate : function(dateVal){
36341         return dateVal.dateFormat(this.dateFormat);
36342     },
36343
36344     renderBool : function(bVal){
36345         return bVal ? 'true' : 'false';
36346     },
36347
36348     isCellEditable : function(colIndex, rowIndex){
36349         return colIndex == 1;
36350     },
36351
36352     getRenderer : function(col){
36353         return col == 1 ?
36354             this.renderCellDelegate : this.renderPropDelegate;
36355     },
36356
36357     renderProp : function(v){
36358         return this.getPropertyName(v);
36359     },
36360
36361     renderCell : function(val){
36362         var rv = val;
36363         if(val instanceof Date){
36364             rv = this.renderDate(val);
36365         }else if(typeof val == 'boolean'){
36366             rv = this.renderBool(val);
36367         }
36368         return Roo.util.Format.htmlEncode(rv);
36369     },
36370
36371     getPropertyName : function(name){
36372         var pn = this.grid.propertyNames;
36373         return pn && pn[name] ? pn[name] : name;
36374     },
36375
36376     getCellEditor : function(colIndex, rowIndex){
36377         var p = this.store.getProperty(rowIndex);
36378         var n = p.data['name'], val = p.data['value'];
36379         
36380         if(typeof(this.grid.customEditors[n]) == 'string'){
36381             return this.editors[this.grid.customEditors[n]];
36382         }
36383         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36384             return this.grid.customEditors[n];
36385         }
36386         if(val instanceof Date){
36387             return this.editors['date'];
36388         }else if(typeof val == 'number'){
36389             return this.editors['number'];
36390         }else if(typeof val == 'boolean'){
36391             return this.editors['boolean'];
36392         }else{
36393             return this.editors['string'];
36394         }
36395     }
36396 });
36397
36398 /**
36399  * @class Roo.grid.PropertyGrid
36400  * @extends Roo.grid.EditorGrid
36401  * This class represents the  interface of a component based property grid control.
36402  * <br><br>Usage:<pre><code>
36403  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36404       
36405  });
36406  // set any options
36407  grid.render();
36408  * </code></pre>
36409   
36410  * @constructor
36411  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36412  * The container MUST have some type of size defined for the grid to fill. The container will be
36413  * automatically set to position relative if it isn't already.
36414  * @param {Object} config A config object that sets properties on this grid.
36415  */
36416 Roo.grid.PropertyGrid = function(container, config){
36417     config = config || {};
36418     var store = new Roo.grid.PropertyStore(this);
36419     this.store = store;
36420     var cm = new Roo.grid.PropertyColumnModel(this, store);
36421     store.store.sort('name', 'ASC');
36422     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36423         ds: store.store,
36424         cm: cm,
36425         enableColLock:false,
36426         enableColumnMove:false,
36427         stripeRows:false,
36428         trackMouseOver: false,
36429         clicksToEdit:1
36430     }, config));
36431     this.getGridEl().addClass('x-props-grid');
36432     this.lastEditRow = null;
36433     this.on('columnresize', this.onColumnResize, this);
36434     this.addEvents({
36435          /**
36436              * @event beforepropertychange
36437              * Fires before a property changes (return false to stop?)
36438              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36439              * @param {String} id Record Id
36440              * @param {String} newval New Value
36441          * @param {String} oldval Old Value
36442              */
36443         "beforepropertychange": true,
36444         /**
36445              * @event propertychange
36446              * Fires after a property changes
36447              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36448              * @param {String} id Record Id
36449              * @param {String} newval New Value
36450          * @param {String} oldval Old Value
36451              */
36452         "propertychange": true
36453     });
36454     this.customEditors = this.customEditors || {};
36455 };
36456 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36457     
36458      /**
36459      * @cfg {Object} customEditors map of colnames=> custom editors.
36460      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36461      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36462      * false disables editing of the field.
36463          */
36464     
36465       /**
36466      * @cfg {Object} propertyNames map of property Names to their displayed value
36467          */
36468     
36469     render : function(){
36470         Roo.grid.PropertyGrid.superclass.render.call(this);
36471         this.autoSize.defer(100, this);
36472     },
36473
36474     autoSize : function(){
36475         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36476         if(this.view){
36477             this.view.fitColumns();
36478         }
36479     },
36480
36481     onColumnResize : function(){
36482         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36483         this.autoSize();
36484     },
36485     /**
36486      * Sets the data for the Grid
36487      * accepts a Key => Value object of all the elements avaiable.
36488      * @param {Object} data  to appear in grid.
36489      */
36490     setSource : function(source){
36491         this.store.setSource(source);
36492         //this.autoSize();
36493     },
36494     /**
36495      * Gets all the data from the grid.
36496      * @return {Object} data  data stored in grid
36497      */
36498     getSource : function(){
36499         return this.store.getSource();
36500     }
36501 });/*
36502   
36503  * Licence LGPL
36504  
36505  */
36506  
36507 /**
36508  * @class Roo.grid.Calendar
36509  * @extends Roo.util.Grid
36510  * This class extends the Grid to provide a calendar widget
36511  * <br><br>Usage:<pre><code>
36512  var grid = new Roo.grid.Calendar("my-container-id", {
36513      ds: myDataStore,
36514      cm: myColModel,
36515      selModel: mySelectionModel,
36516      autoSizeColumns: true,
36517      monitorWindowResize: false,
36518      trackMouseOver: true
36519      eventstore : real data store..
36520  });
36521  // set any options
36522  grid.render();
36523   
36524   * @constructor
36525  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36526  * The container MUST have some type of size defined for the grid to fill. The container will be
36527  * automatically set to position relative if it isn't already.
36528  * @param {Object} config A config object that sets properties on this grid.
36529  */
36530 Roo.grid.Calendar = function(container, config){
36531         // initialize the container
36532         this.container = Roo.get(container);
36533         this.container.update("");
36534         this.container.setStyle("overflow", "hidden");
36535     this.container.addClass('x-grid-container');
36536
36537     this.id = this.container.id;
36538
36539     Roo.apply(this, config);
36540     // check and correct shorthanded configs
36541     
36542     var rows = [];
36543     var d =1;
36544     for (var r = 0;r < 6;r++) {
36545         
36546         rows[r]=[];
36547         for (var c =0;c < 7;c++) {
36548             rows[r][c]= '';
36549         }
36550     }
36551     if (this.eventStore) {
36552         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36553         this.eventStore.on('load',this.onLoad, this);
36554         this.eventStore.on('beforeload',this.clearEvents, this);
36555          
36556     }
36557     
36558     this.dataSource = new Roo.data.Store({
36559             proxy: new Roo.data.MemoryProxy(rows),
36560             reader: new Roo.data.ArrayReader({}, [
36561                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36562     });
36563
36564     this.dataSource.load();
36565     this.ds = this.dataSource;
36566     this.ds.xmodule = this.xmodule || false;
36567     
36568     
36569     var cellRender = function(v,x,r)
36570     {
36571         return String.format(
36572             '<div class="fc-day  fc-widget-content"><div>' +
36573                 '<div class="fc-event-container"></div>' +
36574                 '<div class="fc-day-number">{0}</div>'+
36575                 
36576                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36577             '</div></div>', v);
36578     
36579     }
36580     
36581     
36582     this.colModel = new Roo.grid.ColumnModel( [
36583         {
36584             xtype: 'ColumnModel',
36585             xns: Roo.grid,
36586             dataIndex : 'weekday0',
36587             header : 'Sunday',
36588             renderer : cellRender
36589         },
36590         {
36591             xtype: 'ColumnModel',
36592             xns: Roo.grid,
36593             dataIndex : 'weekday1',
36594             header : 'Monday',
36595             renderer : cellRender
36596         },
36597         {
36598             xtype: 'ColumnModel',
36599             xns: Roo.grid,
36600             dataIndex : 'weekday2',
36601             header : 'Tuesday',
36602             renderer : cellRender
36603         },
36604         {
36605             xtype: 'ColumnModel',
36606             xns: Roo.grid,
36607             dataIndex : 'weekday3',
36608             header : 'Wednesday',
36609             renderer : cellRender
36610         },
36611         {
36612             xtype: 'ColumnModel',
36613             xns: Roo.grid,
36614             dataIndex : 'weekday4',
36615             header : 'Thursday',
36616             renderer : cellRender
36617         },
36618         {
36619             xtype: 'ColumnModel',
36620             xns: Roo.grid,
36621             dataIndex : 'weekday5',
36622             header : 'Friday',
36623             renderer : cellRender
36624         },
36625         {
36626             xtype: 'ColumnModel',
36627             xns: Roo.grid,
36628             dataIndex : 'weekday6',
36629             header : 'Saturday',
36630             renderer : cellRender
36631         }
36632     ]);
36633     this.cm = this.colModel;
36634     this.cm.xmodule = this.xmodule || false;
36635  
36636         
36637           
36638     //this.selModel = new Roo.grid.CellSelectionModel();
36639     //this.sm = this.selModel;
36640     //this.selModel.init(this);
36641     
36642     
36643     if(this.width){
36644         this.container.setWidth(this.width);
36645     }
36646
36647     if(this.height){
36648         this.container.setHeight(this.height);
36649     }
36650     /** @private */
36651         this.addEvents({
36652         // raw events
36653         /**
36654          * @event click
36655          * The raw click event for the entire grid.
36656          * @param {Roo.EventObject} e
36657          */
36658         "click" : true,
36659         /**
36660          * @event dblclick
36661          * The raw dblclick event for the entire grid.
36662          * @param {Roo.EventObject} e
36663          */
36664         "dblclick" : true,
36665         /**
36666          * @event contextmenu
36667          * The raw contextmenu event for the entire grid.
36668          * @param {Roo.EventObject} e
36669          */
36670         "contextmenu" : true,
36671         /**
36672          * @event mousedown
36673          * The raw mousedown event for the entire grid.
36674          * @param {Roo.EventObject} e
36675          */
36676         "mousedown" : true,
36677         /**
36678          * @event mouseup
36679          * The raw mouseup event for the entire grid.
36680          * @param {Roo.EventObject} e
36681          */
36682         "mouseup" : true,
36683         /**
36684          * @event mouseover
36685          * The raw mouseover event for the entire grid.
36686          * @param {Roo.EventObject} e
36687          */
36688         "mouseover" : true,
36689         /**
36690          * @event mouseout
36691          * The raw mouseout event for the entire grid.
36692          * @param {Roo.EventObject} e
36693          */
36694         "mouseout" : true,
36695         /**
36696          * @event keypress
36697          * The raw keypress event for the entire grid.
36698          * @param {Roo.EventObject} e
36699          */
36700         "keypress" : true,
36701         /**
36702          * @event keydown
36703          * The raw keydown event for the entire grid.
36704          * @param {Roo.EventObject} e
36705          */
36706         "keydown" : true,
36707
36708         // custom events
36709
36710         /**
36711          * @event cellclick
36712          * Fires when a cell is clicked
36713          * @param {Grid} this
36714          * @param {Number} rowIndex
36715          * @param {Number} columnIndex
36716          * @param {Roo.EventObject} e
36717          */
36718         "cellclick" : true,
36719         /**
36720          * @event celldblclick
36721          * Fires when a cell is double clicked
36722          * @param {Grid} this
36723          * @param {Number} rowIndex
36724          * @param {Number} columnIndex
36725          * @param {Roo.EventObject} e
36726          */
36727         "celldblclick" : true,
36728         /**
36729          * @event rowclick
36730          * Fires when a row is clicked
36731          * @param {Grid} this
36732          * @param {Number} rowIndex
36733          * @param {Roo.EventObject} e
36734          */
36735         "rowclick" : true,
36736         /**
36737          * @event rowdblclick
36738          * Fires when a row is double clicked
36739          * @param {Grid} this
36740          * @param {Number} rowIndex
36741          * @param {Roo.EventObject} e
36742          */
36743         "rowdblclick" : true,
36744         /**
36745          * @event headerclick
36746          * Fires when a header is clicked
36747          * @param {Grid} this
36748          * @param {Number} columnIndex
36749          * @param {Roo.EventObject} e
36750          */
36751         "headerclick" : true,
36752         /**
36753          * @event headerdblclick
36754          * Fires when a header cell is double clicked
36755          * @param {Grid} this
36756          * @param {Number} columnIndex
36757          * @param {Roo.EventObject} e
36758          */
36759         "headerdblclick" : true,
36760         /**
36761          * @event rowcontextmenu
36762          * Fires when a row is right clicked
36763          * @param {Grid} this
36764          * @param {Number} rowIndex
36765          * @param {Roo.EventObject} e
36766          */
36767         "rowcontextmenu" : true,
36768         /**
36769          * @event cellcontextmenu
36770          * Fires when a cell is right clicked
36771          * @param {Grid} this
36772          * @param {Number} rowIndex
36773          * @param {Number} cellIndex
36774          * @param {Roo.EventObject} e
36775          */
36776          "cellcontextmenu" : true,
36777         /**
36778          * @event headercontextmenu
36779          * Fires when a header is right clicked
36780          * @param {Grid} this
36781          * @param {Number} columnIndex
36782          * @param {Roo.EventObject} e
36783          */
36784         "headercontextmenu" : true,
36785         /**
36786          * @event bodyscroll
36787          * Fires when the body element is scrolled
36788          * @param {Number} scrollLeft
36789          * @param {Number} scrollTop
36790          */
36791         "bodyscroll" : true,
36792         /**
36793          * @event columnresize
36794          * Fires when the user resizes a column
36795          * @param {Number} columnIndex
36796          * @param {Number} newSize
36797          */
36798         "columnresize" : true,
36799         /**
36800          * @event columnmove
36801          * Fires when the user moves a column
36802          * @param {Number} oldIndex
36803          * @param {Number} newIndex
36804          */
36805         "columnmove" : true,
36806         /**
36807          * @event startdrag
36808          * Fires when row(s) start being dragged
36809          * @param {Grid} this
36810          * @param {Roo.GridDD} dd The drag drop object
36811          * @param {event} e The raw browser event
36812          */
36813         "startdrag" : true,
36814         /**
36815          * @event enddrag
36816          * Fires when a drag operation is complete
36817          * @param {Grid} this
36818          * @param {Roo.GridDD} dd The drag drop object
36819          * @param {event} e The raw browser event
36820          */
36821         "enddrag" : true,
36822         /**
36823          * @event dragdrop
36824          * Fires when dragged row(s) are dropped on a valid DD target
36825          * @param {Grid} this
36826          * @param {Roo.GridDD} dd The drag drop object
36827          * @param {String} targetId The target drag drop object
36828          * @param {event} e The raw browser event
36829          */
36830         "dragdrop" : true,
36831         /**
36832          * @event dragover
36833          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36834          * @param {Grid} this
36835          * @param {Roo.GridDD} dd The drag drop object
36836          * @param {String} targetId The target drag drop object
36837          * @param {event} e The raw browser event
36838          */
36839         "dragover" : true,
36840         /**
36841          * @event dragenter
36842          *  Fires when the dragged row(s) first cross another DD target while being dragged
36843          * @param {Grid} this
36844          * @param {Roo.GridDD} dd The drag drop object
36845          * @param {String} targetId The target drag drop object
36846          * @param {event} e The raw browser event
36847          */
36848         "dragenter" : true,
36849         /**
36850          * @event dragout
36851          * Fires when the dragged row(s) leave another DD target while being dragged
36852          * @param {Grid} this
36853          * @param {Roo.GridDD} dd The drag drop object
36854          * @param {String} targetId The target drag drop object
36855          * @param {event} e The raw browser event
36856          */
36857         "dragout" : true,
36858         /**
36859          * @event rowclass
36860          * Fires when a row is rendered, so you can change add a style to it.
36861          * @param {GridView} gridview   The grid view
36862          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36863          */
36864         'rowclass' : true,
36865
36866         /**
36867          * @event render
36868          * Fires when the grid is rendered
36869          * @param {Grid} grid
36870          */
36871         'render' : true,
36872             /**
36873              * @event select
36874              * Fires when a date is selected
36875              * @param {DatePicker} this
36876              * @param {Date} date The selected date
36877              */
36878         'select': true,
36879         /**
36880              * @event monthchange
36881              * Fires when the displayed month changes 
36882              * @param {DatePicker} this
36883              * @param {Date} date The selected month
36884              */
36885         'monthchange': true,
36886         /**
36887              * @event evententer
36888              * Fires when mouse over an event
36889              * @param {Calendar} this
36890              * @param {event} Event
36891              */
36892         'evententer': true,
36893         /**
36894              * @event eventleave
36895              * Fires when the mouse leaves an
36896              * @param {Calendar} this
36897              * @param {event}
36898              */
36899         'eventleave': true,
36900         /**
36901              * @event eventclick
36902              * Fires when the mouse click an
36903              * @param {Calendar} this
36904              * @param {event}
36905              */
36906         'eventclick': true,
36907         /**
36908              * @event eventrender
36909              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36910              * @param {Calendar} this
36911              * @param {data} data to be modified
36912              */
36913         'eventrender': true
36914         
36915     });
36916
36917     Roo.grid.Grid.superclass.constructor.call(this);
36918     this.on('render', function() {
36919         this.view.el.addClass('x-grid-cal'); 
36920         
36921         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36922
36923     },this);
36924     
36925     if (!Roo.grid.Calendar.style) {
36926         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36927             
36928             
36929             '.x-grid-cal .x-grid-col' :  {
36930                 height: 'auto !important',
36931                 'vertical-align': 'top'
36932             },
36933             '.x-grid-cal  .fc-event-hori' : {
36934                 height: '14px'
36935             }
36936              
36937             
36938         }, Roo.id());
36939     }
36940
36941     
36942     
36943 };
36944 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36945     /**
36946      * @cfg {Store} eventStore The store that loads events.
36947      */
36948     eventStore : 25,
36949
36950      
36951     activeDate : false,
36952     startDay : 0,
36953     autoWidth : true,
36954     monitorWindowResize : false,
36955
36956     
36957     resizeColumns : function() {
36958         var col = (this.view.el.getWidth() / 7) - 3;
36959         // loop through cols, and setWidth
36960         for(var i =0 ; i < 7 ; i++){
36961             this.cm.setColumnWidth(i, col);
36962         }
36963     },
36964      setDate :function(date) {
36965         
36966         Roo.log('setDate?');
36967         
36968         this.resizeColumns();
36969         var vd = this.activeDate;
36970         this.activeDate = date;
36971 //        if(vd && this.el){
36972 //            var t = date.getTime();
36973 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36974 //                Roo.log('using add remove');
36975 //                
36976 //                this.fireEvent('monthchange', this, date);
36977 //                
36978 //                this.cells.removeClass("fc-state-highlight");
36979 //                this.cells.each(function(c){
36980 //                   if(c.dateValue == t){
36981 //                       c.addClass("fc-state-highlight");
36982 //                       setTimeout(function(){
36983 //                            try{c.dom.firstChild.focus();}catch(e){}
36984 //                       }, 50);
36985 //                       return false;
36986 //                   }
36987 //                   return true;
36988 //                });
36989 //                return;
36990 //            }
36991 //        }
36992         
36993         var days = date.getDaysInMonth();
36994         
36995         var firstOfMonth = date.getFirstDateOfMonth();
36996         var startingPos = firstOfMonth.getDay()-this.startDay;
36997         
36998         if(startingPos < this.startDay){
36999             startingPos += 7;
37000         }
37001         
37002         var pm = date.add(Date.MONTH, -1);
37003         var prevStart = pm.getDaysInMonth()-startingPos;
37004 //        
37005         
37006         
37007         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37008         
37009         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37010         //this.cells.addClassOnOver('fc-state-hover');
37011         
37012         var cells = this.cells.elements;
37013         var textEls = this.textNodes;
37014         
37015         //Roo.each(cells, function(cell){
37016         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37017         //});
37018         
37019         days += startingPos;
37020
37021         // convert everything to numbers so it's fast
37022         var day = 86400000;
37023         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37024         //Roo.log(d);
37025         //Roo.log(pm);
37026         //Roo.log(prevStart);
37027         
37028         var today = new Date().clearTime().getTime();
37029         var sel = date.clearTime().getTime();
37030         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37031         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37032         var ddMatch = this.disabledDatesRE;
37033         var ddText = this.disabledDatesText;
37034         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37035         var ddaysText = this.disabledDaysText;
37036         var format = this.format;
37037         
37038         var setCellClass = function(cal, cell){
37039             
37040             //Roo.log('set Cell Class');
37041             cell.title = "";
37042             var t = d.getTime();
37043             
37044             //Roo.log(d);
37045             
37046             
37047             cell.dateValue = t;
37048             if(t == today){
37049                 cell.className += " fc-today";
37050                 cell.className += " fc-state-highlight";
37051                 cell.title = cal.todayText;
37052             }
37053             if(t == sel){
37054                 // disable highlight in other month..
37055                 cell.className += " fc-state-highlight";
37056                 
37057             }
37058             // disabling
37059             if(t < min) {
37060                 //cell.className = " fc-state-disabled";
37061                 cell.title = cal.minText;
37062                 return;
37063             }
37064             if(t > max) {
37065                 //cell.className = " fc-state-disabled";
37066                 cell.title = cal.maxText;
37067                 return;
37068             }
37069             if(ddays){
37070                 if(ddays.indexOf(d.getDay()) != -1){
37071                     // cell.title = ddaysText;
37072                    // cell.className = " fc-state-disabled";
37073                 }
37074             }
37075             if(ddMatch && format){
37076                 var fvalue = d.dateFormat(format);
37077                 if(ddMatch.test(fvalue)){
37078                     cell.title = ddText.replace("%0", fvalue);
37079                    cell.className = " fc-state-disabled";
37080                 }
37081             }
37082             
37083             if (!cell.initialClassName) {
37084                 cell.initialClassName = cell.dom.className;
37085             }
37086             
37087             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37088         };
37089
37090         var i = 0;
37091         
37092         for(; i < startingPos; i++) {
37093             cells[i].dayName =  (++prevStart);
37094             Roo.log(textEls[i]);
37095             d.setDate(d.getDate()+1);
37096             
37097             //cells[i].className = "fc-past fc-other-month";
37098             setCellClass(this, cells[i]);
37099         }
37100         
37101         var intDay = 0;
37102         
37103         for(; i < days; i++){
37104             intDay = i - startingPos + 1;
37105             cells[i].dayName =  (intDay);
37106             d.setDate(d.getDate()+1);
37107             
37108             cells[i].className = ''; // "x-date-active";
37109             setCellClass(this, cells[i]);
37110         }
37111         var extraDays = 0;
37112         
37113         for(; i < 42; i++) {
37114             //textEls[i].innerHTML = (++extraDays);
37115             
37116             d.setDate(d.getDate()+1);
37117             cells[i].dayName = (++extraDays);
37118             cells[i].className = "fc-future fc-other-month";
37119             setCellClass(this, cells[i]);
37120         }
37121         
37122         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37123         
37124         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37125         
37126         // this will cause all the cells to mis
37127         var rows= [];
37128         var i =0;
37129         for (var r = 0;r < 6;r++) {
37130             for (var c =0;c < 7;c++) {
37131                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37132             }    
37133         }
37134         
37135         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37136         for(i=0;i<cells.length;i++) {
37137             
37138             this.cells.elements[i].dayName = cells[i].dayName ;
37139             this.cells.elements[i].className = cells[i].className;
37140             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37141             this.cells.elements[i].title = cells[i].title ;
37142             this.cells.elements[i].dateValue = cells[i].dateValue ;
37143         }
37144         
37145         
37146         
37147         
37148         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37149         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37150         
37151         ////if(totalRows != 6){
37152             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37153            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37154        // }
37155         
37156         this.fireEvent('monthchange', this, date);
37157         
37158         
37159     },
37160  /**
37161      * Returns the grid's SelectionModel.
37162      * @return {SelectionModel}
37163      */
37164     getSelectionModel : function(){
37165         if(!this.selModel){
37166             this.selModel = new Roo.grid.CellSelectionModel();
37167         }
37168         return this.selModel;
37169     },
37170
37171     load: function() {
37172         this.eventStore.load()
37173         
37174         
37175         
37176     },
37177     
37178     findCell : function(dt) {
37179         dt = dt.clearTime().getTime();
37180         var ret = false;
37181         this.cells.each(function(c){
37182             //Roo.log("check " +c.dateValue + '?=' + dt);
37183             if(c.dateValue == dt){
37184                 ret = c;
37185                 return false;
37186             }
37187             return true;
37188         });
37189         
37190         return ret;
37191     },
37192     
37193     findCells : function(rec) {
37194         var s = rec.data.start_dt.clone().clearTime().getTime();
37195        // Roo.log(s);
37196         var e= rec.data.end_dt.clone().clearTime().getTime();
37197        // Roo.log(e);
37198         var ret = [];
37199         this.cells.each(function(c){
37200              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37201             
37202             if(c.dateValue > e){
37203                 return ;
37204             }
37205             if(c.dateValue < s){
37206                 return ;
37207             }
37208             ret.push(c);
37209         });
37210         
37211         return ret;    
37212     },
37213     
37214     findBestRow: function(cells)
37215     {
37216         var ret = 0;
37217         
37218         for (var i =0 ; i < cells.length;i++) {
37219             ret  = Math.max(cells[i].rows || 0,ret);
37220         }
37221         return ret;
37222         
37223     },
37224     
37225     
37226     addItem : function(rec)
37227     {
37228         // look for vertical location slot in
37229         var cells = this.findCells(rec);
37230         
37231         rec.row = this.findBestRow(cells);
37232         
37233         // work out the location.
37234         
37235         var crow = false;
37236         var rows = [];
37237         for(var i =0; i < cells.length; i++) {
37238             if (!crow) {
37239                 crow = {
37240                     start : cells[i],
37241                     end :  cells[i]
37242                 };
37243                 continue;
37244             }
37245             if (crow.start.getY() == cells[i].getY()) {
37246                 // on same row.
37247                 crow.end = cells[i];
37248                 continue;
37249             }
37250             // different row.
37251             rows.push(crow);
37252             crow = {
37253                 start: cells[i],
37254                 end : cells[i]
37255             };
37256             
37257         }
37258         
37259         rows.push(crow);
37260         rec.els = [];
37261         rec.rows = rows;
37262         rec.cells = cells;
37263         for (var i = 0; i < cells.length;i++) {
37264             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37265             
37266         }
37267         
37268         
37269     },
37270     
37271     clearEvents: function() {
37272         
37273         if (!this.eventStore.getCount()) {
37274             return;
37275         }
37276         // reset number of rows in cells.
37277         Roo.each(this.cells.elements, function(c){
37278             c.rows = 0;
37279         });
37280         
37281         this.eventStore.each(function(e) {
37282             this.clearEvent(e);
37283         },this);
37284         
37285     },
37286     
37287     clearEvent : function(ev)
37288     {
37289         if (ev.els) {
37290             Roo.each(ev.els, function(el) {
37291                 el.un('mouseenter' ,this.onEventEnter, this);
37292                 el.un('mouseleave' ,this.onEventLeave, this);
37293                 el.remove();
37294             },this);
37295             ev.els = [];
37296         }
37297     },
37298     
37299     
37300     renderEvent : function(ev,ctr) {
37301         if (!ctr) {
37302              ctr = this.view.el.select('.fc-event-container',true).first();
37303         }
37304         
37305          
37306         this.clearEvent(ev);
37307             //code
37308        
37309         
37310         
37311         ev.els = [];
37312         var cells = ev.cells;
37313         var rows = ev.rows;
37314         this.fireEvent('eventrender', this, ev);
37315         
37316         for(var i =0; i < rows.length; i++) {
37317             
37318             cls = '';
37319             if (i == 0) {
37320                 cls += ' fc-event-start';
37321             }
37322             if ((i+1) == rows.length) {
37323                 cls += ' fc-event-end';
37324             }
37325             
37326             //Roo.log(ev.data);
37327             // how many rows should it span..
37328             var cg = this.eventTmpl.append(ctr,Roo.apply({
37329                 fccls : cls
37330                 
37331             }, ev.data) , true);
37332             
37333             
37334             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37335             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37336             cg.on('click', this.onEventClick, this, ev);
37337             
37338             ev.els.push(cg);
37339             
37340             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37341             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37342             //Roo.log(cg);
37343              
37344             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37345             cg.setWidth(ebox.right - sbox.x -2);
37346         }
37347     },
37348     
37349     renderEvents: function()
37350     {   
37351         // first make sure there is enough space..
37352         
37353         if (!this.eventTmpl) {
37354             this.eventTmpl = new Roo.Template(
37355                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37356                     '<div class="fc-event-inner">' +
37357                         '<span class="fc-event-time">{time}</span>' +
37358                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37359                     '</div>' +
37360                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37361                 '</div>'
37362             );
37363                 
37364         }
37365                
37366         
37367         
37368         this.cells.each(function(c) {
37369             //Roo.log(c.select('.fc-day-content div',true).first());
37370             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37371         });
37372         
37373         var ctr = this.view.el.select('.fc-event-container',true).first();
37374         
37375         var cls;
37376         this.eventStore.each(function(ev){
37377             
37378             this.renderEvent(ev);
37379              
37380              
37381         }, this);
37382         this.view.layout();
37383         
37384     },
37385     
37386     onEventEnter: function (e, el,event,d) {
37387         this.fireEvent('evententer', this, el, event);
37388     },
37389     
37390     onEventLeave: function (e, el,event,d) {
37391         this.fireEvent('eventleave', this, el, event);
37392     },
37393     
37394     onEventClick: function (e, el,event,d) {
37395         this.fireEvent('eventclick', this, el, event);
37396     },
37397     
37398     onMonthChange: function () {
37399         this.store.load();
37400     },
37401     
37402     onLoad: function () {
37403         
37404         //Roo.log('calendar onload');
37405 //         
37406         if(this.eventStore.getCount() > 0){
37407             
37408            
37409             
37410             this.eventStore.each(function(d){
37411                 
37412                 
37413                 // FIXME..
37414                 var add =   d.data;
37415                 if (typeof(add.end_dt) == 'undefined')  {
37416                     Roo.log("Missing End time in calendar data: ");
37417                     Roo.log(d);
37418                     return;
37419                 }
37420                 if (typeof(add.start_dt) == 'undefined')  {
37421                     Roo.log("Missing Start time in calendar data: ");
37422                     Roo.log(d);
37423                     return;
37424                 }
37425                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37426                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37427                 add.id = add.id || d.id;
37428                 add.title = add.title || '??';
37429                 
37430                 this.addItem(d);
37431                 
37432              
37433             },this);
37434         }
37435         
37436         this.renderEvents();
37437     }
37438     
37439
37440 });
37441 /*
37442  grid : {
37443                 xtype: 'Grid',
37444                 xns: Roo.grid,
37445                 listeners : {
37446                     render : function ()
37447                     {
37448                         _this.grid = this;
37449                         
37450                         if (!this.view.el.hasClass('course-timesheet')) {
37451                             this.view.el.addClass('course-timesheet');
37452                         }
37453                         if (this.tsStyle) {
37454                             this.ds.load({});
37455                             return; 
37456                         }
37457                         Roo.log('width');
37458                         Roo.log(_this.grid.view.el.getWidth());
37459                         
37460                         
37461                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37462                             '.course-timesheet .x-grid-row' : {
37463                                 height: '80px'
37464                             },
37465                             '.x-grid-row td' : {
37466                                 'vertical-align' : 0
37467                             },
37468                             '.course-edit-link' : {
37469                                 'color' : 'blue',
37470                                 'text-overflow' : 'ellipsis',
37471                                 'overflow' : 'hidden',
37472                                 'white-space' : 'nowrap',
37473                                 'cursor' : 'pointer'
37474                             },
37475                             '.sub-link' : {
37476                                 'color' : 'green'
37477                             },
37478                             '.de-act-sup-link' : {
37479                                 'color' : 'purple',
37480                                 'text-decoration' : 'line-through'
37481                             },
37482                             '.de-act-link' : {
37483                                 'color' : 'red',
37484                                 'text-decoration' : 'line-through'
37485                             },
37486                             '.course-timesheet .course-highlight' : {
37487                                 'border-top-style': 'dashed !important',
37488                                 'border-bottom-bottom': 'dashed !important'
37489                             },
37490                             '.course-timesheet .course-item' : {
37491                                 'font-family'   : 'tahoma, arial, helvetica',
37492                                 'font-size'     : '11px',
37493                                 'overflow'      : 'hidden',
37494                                 'padding-left'  : '10px',
37495                                 'padding-right' : '10px',
37496                                 'padding-top' : '10px' 
37497                             }
37498                             
37499                         }, Roo.id());
37500                                 this.ds.load({});
37501                     }
37502                 },
37503                 autoWidth : true,
37504                 monitorWindowResize : false,
37505                 cellrenderer : function(v,x,r)
37506                 {
37507                     return v;
37508                 },
37509                 sm : {
37510                     xtype: 'CellSelectionModel',
37511                     xns: Roo.grid
37512                 },
37513                 dataSource : {
37514                     xtype: 'Store',
37515                     xns: Roo.data,
37516                     listeners : {
37517                         beforeload : function (_self, options)
37518                         {
37519                             options.params = options.params || {};
37520                             options.params._month = _this.monthField.getValue();
37521                             options.params.limit = 9999;
37522                             options.params['sort'] = 'when_dt';    
37523                             options.params['dir'] = 'ASC';    
37524                             this.proxy.loadResponse = this.loadResponse;
37525                             Roo.log("load?");
37526                             //this.addColumns();
37527                         },
37528                         load : function (_self, records, options)
37529                         {
37530                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37531                                 // if you click on the translation.. you can edit it...
37532                                 var el = Roo.get(this);
37533                                 var id = el.dom.getAttribute('data-id');
37534                                 var d = el.dom.getAttribute('data-date');
37535                                 var t = el.dom.getAttribute('data-time');
37536                                 //var id = this.child('span').dom.textContent;
37537                                 
37538                                 //Roo.log(this);
37539                                 Pman.Dialog.CourseCalendar.show({
37540                                     id : id,
37541                                     when_d : d,
37542                                     when_t : t,
37543                                     productitem_active : id ? 1 : 0
37544                                 }, function() {
37545                                     _this.grid.ds.load({});
37546                                 });
37547                            
37548                            });
37549                            
37550                            _this.panel.fireEvent('resize', [ '', '' ]);
37551                         }
37552                     },
37553                     loadResponse : function(o, success, response){
37554                             // this is overridden on before load..
37555                             
37556                             Roo.log("our code?");       
37557                             //Roo.log(success);
37558                             //Roo.log(response)
37559                             delete this.activeRequest;
37560                             if(!success){
37561                                 this.fireEvent("loadexception", this, o, response);
37562                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37563                                 return;
37564                             }
37565                             var result;
37566                             try {
37567                                 result = o.reader.read(response);
37568                             }catch(e){
37569                                 Roo.log("load exception?");
37570                                 this.fireEvent("loadexception", this, o, response, e);
37571                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37572                                 return;
37573                             }
37574                             Roo.log("ready...");        
37575                             // loop through result.records;
37576                             // and set this.tdate[date] = [] << array of records..
37577                             _this.tdata  = {};
37578                             Roo.each(result.records, function(r){
37579                                 //Roo.log(r.data);
37580                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37581                                     _this.tdata[r.data.when_dt.format('j')] = [];
37582                                 }
37583                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37584                             });
37585                             
37586                             //Roo.log(_this.tdata);
37587                             
37588                             result.records = [];
37589                             result.totalRecords = 6;
37590                     
37591                             // let's generate some duumy records for the rows.
37592                             //var st = _this.dateField.getValue();
37593                             
37594                             // work out monday..
37595                             //st = st.add(Date.DAY, -1 * st.format('w'));
37596                             
37597                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37598                             
37599                             var firstOfMonth = date.getFirstDayOfMonth();
37600                             var days = date.getDaysInMonth();
37601                             var d = 1;
37602                             var firstAdded = false;
37603                             for (var i = 0; i < result.totalRecords ; i++) {
37604                                 //var d= st.add(Date.DAY, i);
37605                                 var row = {};
37606                                 var added = 0;
37607                                 for(var w = 0 ; w < 7 ; w++){
37608                                     if(!firstAdded && firstOfMonth != w){
37609                                         continue;
37610                                     }
37611                                     if(d > days){
37612                                         continue;
37613                                     }
37614                                     firstAdded = true;
37615                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37616                                     row['weekday'+w] = String.format(
37617                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37618                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37619                                                     d,
37620                                                     date.format('Y-m-')+dd
37621                                                 );
37622                                     added++;
37623                                     if(typeof(_this.tdata[d]) != 'undefined'){
37624                                         Roo.each(_this.tdata[d], function(r){
37625                                             var is_sub = '';
37626                                             var deactive = '';
37627                                             var id = r.id;
37628                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37629                                             if(r.parent_id*1>0){
37630                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37631                                                 id = r.parent_id;
37632                                             }
37633                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37634                                                 deactive = 'de-act-link';
37635                                             }
37636                                             
37637                                             row['weekday'+w] += String.format(
37638                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37639                                                     id, //0
37640                                                     r.product_id_name, //1
37641                                                     r.when_dt.format('h:ia'), //2
37642                                                     is_sub, //3
37643                                                     deactive, //4
37644                                                     desc // 5
37645                                             );
37646                                         });
37647                                     }
37648                                     d++;
37649                                 }
37650                                 
37651                                 // only do this if something added..
37652                                 if(added > 0){ 
37653                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37654                                 }
37655                                 
37656                                 
37657                                 // push it twice. (second one with an hour..
37658                                 
37659                             }
37660                             //Roo.log(result);
37661                             this.fireEvent("load", this, o, o.request.arg);
37662                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37663                         },
37664                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37665                     proxy : {
37666                         xtype: 'HttpProxy',
37667                         xns: Roo.data,
37668                         method : 'GET',
37669                         url : baseURL + '/Roo/Shop_course.php'
37670                     },
37671                     reader : {
37672                         xtype: 'JsonReader',
37673                         xns: Roo.data,
37674                         id : 'id',
37675                         fields : [
37676                             {
37677                                 'name': 'id',
37678                                 'type': 'int'
37679                             },
37680                             {
37681                                 'name': 'when_dt',
37682                                 'type': 'string'
37683                             },
37684                             {
37685                                 'name': 'end_dt',
37686                                 'type': 'string'
37687                             },
37688                             {
37689                                 'name': 'parent_id',
37690                                 'type': 'int'
37691                             },
37692                             {
37693                                 'name': 'product_id',
37694                                 'type': 'int'
37695                             },
37696                             {
37697                                 'name': 'productitem_id',
37698                                 'type': 'int'
37699                             },
37700                             {
37701                                 'name': 'guid',
37702                                 'type': 'int'
37703                             }
37704                         ]
37705                     }
37706                 },
37707                 toolbar : {
37708                     xtype: 'Toolbar',
37709                     xns: Roo,
37710                     items : [
37711                         {
37712                             xtype: 'Button',
37713                             xns: Roo.Toolbar,
37714                             listeners : {
37715                                 click : function (_self, e)
37716                                 {
37717                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37718                                     sd.setMonth(sd.getMonth()-1);
37719                                     _this.monthField.setValue(sd.format('Y-m-d'));
37720                                     _this.grid.ds.load({});
37721                                 }
37722                             },
37723                             text : "Back"
37724                         },
37725                         {
37726                             xtype: 'Separator',
37727                             xns: Roo.Toolbar
37728                         },
37729                         {
37730                             xtype: 'MonthField',
37731                             xns: Roo.form,
37732                             listeners : {
37733                                 render : function (_self)
37734                                 {
37735                                     _this.monthField = _self;
37736                                    // _this.monthField.set  today
37737                                 },
37738                                 select : function (combo, date)
37739                                 {
37740                                     _this.grid.ds.load({});
37741                                 }
37742                             },
37743                             value : (function() { return new Date(); })()
37744                         },
37745                         {
37746                             xtype: 'Separator',
37747                             xns: Roo.Toolbar
37748                         },
37749                         {
37750                             xtype: 'TextItem',
37751                             xns: Roo.Toolbar,
37752                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37753                         },
37754                         {
37755                             xtype: 'Fill',
37756                             xns: Roo.Toolbar
37757                         },
37758                         {
37759                             xtype: 'Button',
37760                             xns: Roo.Toolbar,
37761                             listeners : {
37762                                 click : function (_self, e)
37763                                 {
37764                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37765                                     sd.setMonth(sd.getMonth()+1);
37766                                     _this.monthField.setValue(sd.format('Y-m-d'));
37767                                     _this.grid.ds.load({});
37768                                 }
37769                             },
37770                             text : "Next"
37771                         }
37772                     ]
37773                 },
37774                  
37775             }
37776         };
37777         
37778         *//*
37779  * Based on:
37780  * Ext JS Library 1.1.1
37781  * Copyright(c) 2006-2007, Ext JS, LLC.
37782  *
37783  * Originally Released Under LGPL - original licence link has changed is not relivant.
37784  *
37785  * Fork - LGPL
37786  * <script type="text/javascript">
37787  */
37788  
37789 /**
37790  * @class Roo.LoadMask
37791  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37792  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37793  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37794  * element's UpdateManager load indicator and will be destroyed after the initial load.
37795  * @constructor
37796  * Create a new LoadMask
37797  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37798  * @param {Object} config The config object
37799  */
37800 Roo.LoadMask = function(el, config){
37801     this.el = Roo.get(el);
37802     Roo.apply(this, config);
37803     if(this.store){
37804         this.store.on('beforeload', this.onBeforeLoad, this);
37805         this.store.on('load', this.onLoad, this);
37806         this.store.on('loadexception', this.onLoadException, this);
37807         this.removeMask = false;
37808     }else{
37809         var um = this.el.getUpdateManager();
37810         um.showLoadIndicator = false; // disable the default indicator
37811         um.on('beforeupdate', this.onBeforeLoad, this);
37812         um.on('update', this.onLoad, this);
37813         um.on('failure', this.onLoad, this);
37814         this.removeMask = true;
37815     }
37816 };
37817
37818 Roo.LoadMask.prototype = {
37819     /**
37820      * @cfg {Boolean} removeMask
37821      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37822      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37823      */
37824     /**
37825      * @cfg {String} msg
37826      * The text to display in a centered loading message box (defaults to 'Loading...')
37827      */
37828     msg : 'Loading...',
37829     /**
37830      * @cfg {String} msgCls
37831      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37832      */
37833     msgCls : 'x-mask-loading',
37834
37835     /**
37836      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37837      * @type Boolean
37838      */
37839     disabled: false,
37840
37841     /**
37842      * Disables the mask to prevent it from being displayed
37843      */
37844     disable : function(){
37845        this.disabled = true;
37846     },
37847
37848     /**
37849      * Enables the mask so that it can be displayed
37850      */
37851     enable : function(){
37852         this.disabled = false;
37853     },
37854     
37855     onLoadException : function()
37856     {
37857         Roo.log(arguments);
37858         
37859         if (typeof(arguments[3]) != 'undefined') {
37860             Roo.MessageBox.alert("Error loading",arguments[3]);
37861         } 
37862         /*
37863         try {
37864             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37865                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37866             }   
37867         } catch(e) {
37868             
37869         }
37870         */
37871     
37872         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37873     },
37874     // private
37875     onLoad : function()
37876     {
37877         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37878     },
37879
37880     // private
37881     onBeforeLoad : function(){
37882         if(!this.disabled){
37883             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37884         }
37885     },
37886
37887     // private
37888     destroy : function(){
37889         if(this.store){
37890             this.store.un('beforeload', this.onBeforeLoad, this);
37891             this.store.un('load', this.onLoad, this);
37892             this.store.un('loadexception', this.onLoadException, this);
37893         }else{
37894             var um = this.el.getUpdateManager();
37895             um.un('beforeupdate', this.onBeforeLoad, this);
37896             um.un('update', this.onLoad, this);
37897             um.un('failure', this.onLoad, this);
37898         }
37899     }
37900 };/*
37901  * Based on:
37902  * Ext JS Library 1.1.1
37903  * Copyright(c) 2006-2007, Ext JS, LLC.
37904  *
37905  * Originally Released Under LGPL - original licence link has changed is not relivant.
37906  *
37907  * Fork - LGPL
37908  * <script type="text/javascript">
37909  */
37910
37911
37912 /**
37913  * @class Roo.XTemplate
37914  * @extends Roo.Template
37915  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37916 <pre><code>
37917 var t = new Roo.XTemplate(
37918         '&lt;select name="{name}"&gt;',
37919                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37920         '&lt;/select&gt;'
37921 );
37922  
37923 // then append, applying the master template values
37924  </code></pre>
37925  *
37926  * Supported features:
37927  *
37928  *  Tags:
37929
37930 <pre><code>
37931       {a_variable} - output encoded.
37932       {a_variable.format:("Y-m-d")} - call a method on the variable
37933       {a_variable:raw} - unencoded output
37934       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37935       {a_variable:this.method_on_template(...)} - call a method on the template object.
37936  
37937 </code></pre>
37938  *  The tpl tag:
37939 <pre><code>
37940         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37941         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37942         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37943         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37944   
37945         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37946         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37947 </code></pre>
37948  *      
37949  */
37950 Roo.XTemplate = function()
37951 {
37952     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37953     if (this.html) {
37954         this.compile();
37955     }
37956 };
37957
37958
37959 Roo.extend(Roo.XTemplate, Roo.Template, {
37960
37961     /**
37962      * The various sub templates
37963      */
37964     tpls : false,
37965     /**
37966      *
37967      * basic tag replacing syntax
37968      * WORD:WORD()
37969      *
37970      * // you can fake an object call by doing this
37971      *  x.t:(test,tesT) 
37972      * 
37973      */
37974     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37975
37976     /**
37977      * compile the template
37978      *
37979      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37980      *
37981      */
37982     compile: function()
37983     {
37984         var s = this.html;
37985      
37986         s = ['<tpl>', s, '</tpl>'].join('');
37987     
37988         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
37989             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
37990             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
37991             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
37992             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
37993             m,
37994             id     = 0,
37995             tpls   = [];
37996     
37997         while(true == !!(m = s.match(re))){
37998             var forMatch   = m[0].match(nameRe),
37999                 ifMatch   = m[0].match(ifRe),
38000                 execMatch   = m[0].match(execRe),
38001                 namedMatch   = m[0].match(namedRe),
38002                 
38003                 exp  = null, 
38004                 fn   = null,
38005                 exec = null,
38006                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38007                 
38008             if (ifMatch) {
38009                 // if - puts fn into test..
38010                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38011                 if(exp){
38012                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38013                 }
38014             }
38015             
38016             if (execMatch) {
38017                 // exec - calls a function... returns empty if true is  returned.
38018                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38019                 if(exp){
38020                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38021                 }
38022             }
38023             
38024             
38025             if (name) {
38026                 // for = 
38027                 switch(name){
38028                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38029                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38030                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38031                 }
38032             }
38033             var uid = namedMatch ? namedMatch[1] : id;
38034             
38035             
38036             tpls.push({
38037                 id:     namedMatch ? namedMatch[1] : id,
38038                 target: name,
38039                 exec:   exec,
38040                 test:   fn,
38041                 body:   m[1] || ''
38042             });
38043             if (namedMatch) {
38044                 s = s.replace(m[0], '');
38045             } else { 
38046                 s = s.replace(m[0], '{xtpl'+ id + '}');
38047             }
38048             ++id;
38049         }
38050         this.tpls = [];
38051         for(var i = tpls.length-1; i >= 0; --i){
38052             this.compileTpl(tpls[i]);
38053             this.tpls[tpls[i].id] = tpls[i];
38054         }
38055         this.master = tpls[tpls.length-1];
38056         return this;
38057     },
38058     /**
38059      * same as applyTemplate, except it's done to one of the subTemplates
38060      * when using named templates, you can do:
38061      *
38062      * var str = pl.applySubTemplate('your-name', values);
38063      *
38064      * 
38065      * @param {Number} id of the template
38066      * @param {Object} values to apply to template
38067      * @param {Object} parent (normaly the instance of this object)
38068      */
38069     applySubTemplate : function(id, values, parent)
38070     {
38071         
38072         
38073         var t = this.tpls[id];
38074         
38075         
38076         try { 
38077             if(t.test && !t.test.call(this, values, parent)){
38078                 return '';
38079             }
38080         } catch(e) {
38081             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38082             Roo.log(e.toString());
38083             Roo.log(t.test);
38084             return ''
38085         }
38086         try { 
38087             
38088             if(t.exec && t.exec.call(this, values, parent)){
38089                 return '';
38090             }
38091         } catch(e) {
38092             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38093             Roo.log(e.toString());
38094             Roo.log(t.exec);
38095             return ''
38096         }
38097         try {
38098             var vs = t.target ? t.target.call(this, values, parent) : values;
38099             parent = t.target ? values : parent;
38100             if(t.target && vs instanceof Array){
38101                 var buf = [];
38102                 for(var i = 0, len = vs.length; i < len; i++){
38103                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38104                 }
38105                 return buf.join('');
38106             }
38107             return t.compiled.call(this, vs, parent);
38108         } catch (e) {
38109             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38110             Roo.log(e.toString());
38111             Roo.log(t.compiled);
38112             return '';
38113         }
38114     },
38115
38116     compileTpl : function(tpl)
38117     {
38118         var fm = Roo.util.Format;
38119         var useF = this.disableFormats !== true;
38120         var sep = Roo.isGecko ? "+" : ",";
38121         var undef = function(str) {
38122             Roo.log("Property not found :"  + str);
38123             return '';
38124         };
38125         
38126         var fn = function(m, name, format, args)
38127         {
38128             //Roo.log(arguments);
38129             args = args ? args.replace(/\\'/g,"'") : args;
38130             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38131             if (typeof(format) == 'undefined') {
38132                 format= 'htmlEncode';
38133             }
38134             if (format == 'raw' ) {
38135                 format = false;
38136             }
38137             
38138             if(name.substr(0, 4) == 'xtpl'){
38139                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38140             }
38141             
38142             // build an array of options to determine if value is undefined..
38143             
38144             // basically get 'xxxx.yyyy' then do
38145             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38146             //    (function () { Roo.log("Property not found"); return ''; })() :
38147             //    ......
38148             
38149             var udef_ar = [];
38150             var lookfor = '';
38151             Roo.each(name.split('.'), function(st) {
38152                 lookfor += (lookfor.length ? '.': '') + st;
38153                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38154             });
38155             
38156             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38157             
38158             
38159             if(format && useF){
38160                 
38161                 args = args ? ',' + args : "";
38162                  
38163                 if(format.substr(0, 5) != "this."){
38164                     format = "fm." + format + '(';
38165                 }else{
38166                     format = 'this.call("'+ format.substr(5) + '", ';
38167                     args = ", values";
38168                 }
38169                 
38170                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38171             }
38172              
38173             if (args.length) {
38174                 // called with xxyx.yuu:(test,test)
38175                 // change to ()
38176                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38177             }
38178             // raw.. - :raw modifier..
38179             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38180             
38181         };
38182         var body;
38183         // branched to use + in gecko and [].join() in others
38184         if(Roo.isGecko){
38185             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38186                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38187                     "';};};";
38188         }else{
38189             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38190             body.push(tpl.body.replace(/(\r\n|\n)/g,
38191                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38192             body.push("'].join('');};};");
38193             body = body.join('');
38194         }
38195         
38196         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38197        
38198         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38199         eval(body);
38200         
38201         return this;
38202     },
38203
38204     applyTemplate : function(values){
38205         return this.master.compiled.call(this, values, {});
38206         //var s = this.subs;
38207     },
38208
38209     apply : function(){
38210         return this.applyTemplate.apply(this, arguments);
38211     }
38212
38213  });
38214
38215 Roo.XTemplate.from = function(el){
38216     el = Roo.getDom(el);
38217     return new Roo.XTemplate(el.value || el.innerHTML);
38218 };